API v1

Rate Limits & Errors

Request limits, expected status codes, and retry behavior.

This page is modeled after common API docs patterns: one place for limits, errors, and operational behavior.

Rate limits

Tokens API enforces per-key limits. If a key exceeds allowed throughput or quota, the API returns 429.

Practical guidance:

  • Treat 429 as retryable.
  • Use exponential backoff with jitter.
  • Avoid unbounded parallel fan-out.
  • Cache data where possible.

Quota behavior

Platform keys can also have monthly quotas. Exceeding quota also returns 429.

Error envelope

Platform endpoints use a stable error shape:

{
  "error": {
    "_tag": "BadRequestError",
    "message": "Invalid mint",
    "details": "..."
  }
}

details is optional. Some errors use an object for details with a stable code field (for example list mutations and advisory refusals below); branch on _tag first, then details.code.

Common status codes

  • 400 Bad request (invalid query/body/params)
  • 401 Unauthorized (missing/invalid API key)
  • 403 Forbidden (missing required scopes, or an advisory refusal — see below)
  • 404 Not found (unknown resource or mapping)
  • 429 Rate-limited or quota exceeded
  • 500 Internal server error

Advisory refusals (execution endpoints)

Where available, execution endpoints (the /v2/execution/* family: links, evaluate, route) refuse to build a trade for a mint that carries a compromised or blocked advisory. The response is 403 with _tag: "AssetAdvisoryError" and an object details:

{
  "error": {
    "_tag": "AssetAdvisoryError",
    "message": "SILV is flagged as compromised",
    "details": {
      "code": "advisory_compromised",
      "mint": "SiLVFMgD3eD2rgK628NbTBq9MnuJF5FW2CRaVyTB35L",
      "status": "compromised",
      "reason": "Issuer treasury exploited; market pulled by the issuer.",
      "url": "https://..."
    }
  }
}
  • details.code is advisory_compromised or advisory_blocked, mirroring details.status.
  • Do not retry: the flag is lifted by an operator, not by time. Show reason (and url when present) to the user instead.
  • Read endpoints never refuse; they annotate. The same mint is still served by GET /v1/assets/:assetId with advisory populated, so you can fetch the warning to display. caution advisories never produce this error.
  • Route-style endpoints that consider several variants list a refused variant under excluded[] with reason: "advisory_compromised" rather than failing the whole request, as long as another eligible variant exists.

See Asset by ID → Advisories for the status semantics.

Retry matrix

  • Retry: 429, transient 5xx
  • Do not retry without changes: 400, 401, 403, 404

TypeScript retry example

export async function fetchWithRetry(url: string, init: RequestInit, maxRetries = 4): Promise<Response> {
  let attempt = 0;

  while (true) {
    const res = await fetch(url, init);
    if (res.ok) return res;

    const retryable = res.status === 429 || (res.status >= 500 && res.status < 600);
    if (!retryable || attempt >= maxRetries) return res;

    const base = 250 * 2 ** attempt;
    const jitter = Math.floor(Math.random() * 150);
    await new Promise(resolve => setTimeout(resolve, base + jitter));
    attempt += 1;
  }
}

On this page