TokensAPI

Authentication

API keys, scopes, key safety and request ids for API v1.

API key

Every endpoint on this page is authenticated with an API key in the x-api-key header (not Authorization: Bearer):

curl -sS "$API_BASE_URL/$PATH" -H "x-api-key: $API_KEY"

A missing key returns 401 with message: "Missing API key"; a key that is not active returns 401 with message: "Invalid API key". A key without the required scope returns 403 with details.required and details.granted.

Keys are created and regenerated in the dashboard. A regenerated key replaces the previous one immediately. The dashboard playground runs with your active key, so its calls count towards the same usage and limits.

Keep your API key server-side

Treat the key like a password:

  • Call the API from your server or an edge function and proxy the data to the browser. Never ship the key in frontend code or a public repository.
  • Rotate a key from the dashboard if it leaks. Regenerating a key deactivates the previous one immediately.
  • One key per environment (development, staging, production) keeps usage and rate limits separate.

Scopes

Every v1 endpoint accepts the umbrella scope assets:read, which is on every key by default. Four fine-grained scopes exist for keys that should only read one kind of data:

ScopeGrants
assets:readEverything on this surface.
assets:profile:readinclude=profile on asset detail.
assets:risk:readinclude=risk on asset detail and GET /v1/assets/risk-summary.
assets:ohlcv:readinclude=ohlcv on asset detail.
assets:markets:readinclude=markets on asset detail.

A request that asks for an include the key cannot read returns 403 with details.required and details.granted.

Request IDs

Every response carries an x-request-id header. When you report a problem, include the request URL, the response body (with your key redacted), the x-request-id, and the UTC time of the call.

Example client

interface ApiErrorEnvelope {
  error: { _tag: string; message: string; details?: Record<string, unknown> };
}

function isApiError(value: unknown): value is ApiErrorEnvelope {
  const error = (value as { error?: { _tag?: unknown; message?: unknown } } | null)?.error;
  return typeof error?._tag === "string" && typeof error.message === "string";
}

export async function getJson<T>(path: string, apiKey: string): Promise<T> {
  const res = await fetch(`https://api.tokens.xyz${path}`, {
    headers: { "x-api-key": apiKey },
  });
  const json: unknown = await res.json().catch(() => null);
  if (!res.ok) {
    if (isApiError(json)) throw new Error(`${json.error._tag}: ${json.error.message}`);
    throw new Error(`HTTP ${res.status}`);
  }
  return json as T;
}