Reference

Errors and rate limits

Match on code, never on message. Codes are stable and part of the contract; messages are written for humans and get reworded.

Error shape

→ 422 Unprocessable Entity
{
  "error": {
    "code": "invalid_address",
    "message": "Not a valid Tron address for the named chain.",
    "field": "address",
    "request_id": "req_5Hn2…",
    "docs": "https://fin-techai.com/docs/errors#invalid_address"
  }
}

Every error carries a request_id. Quote it in a support request and we can see the exact call, including which node served it.

Status and error codes

HTTPcodeMeaning and what to do
400malformed_requestBody is not valid JSON, or a required field is missing. Not retryable.
401invalid_keyKey unknown, revoked, or wrong environment — a test key on the production host lands here deliberately.
403scope_insufficientKey is valid but not scoped to this endpoint. Fix the key, do not retry.
403ip_not_allowedSource IP outside the key's CIDR allowlist. Logged with its source.
404not_foundResource does not exist, or belongs to another account. We do not distinguish the two on purpose.
409idempotency_conflictSame Idempotency-Key, different body. Change the key or send the original body.
410quote_expiredQuote passed quote_expires_at. Re-quote — we will not silently re-price.
422invalid_addressAddress fails validation for the named chain. Check chain and address agree.
422chain_unsupportedChain not covered for this module. See integrations.
422thin_historyNot enough on-chain history for a credit profile. Treat as unknown, not as bad.
429rate_limitedOver your limit. Honour Retry-After. Never billed.
503coverage_degradedThe chain you asked about is materially behind head and we would rather fail than serve a stale score silently. Retryable.
500 / 502 / 504internal_errorOurs. Retry with jittered backoff. Never billed.

Rate limits

Limits are per account, not per key — splitting traffic across keys to exceed a sustained limit is a breach of the API terms, not a clever workaround. Every response carries the current state.

X-FTAI-RateLimit-Limit: 600          // sustained, per minute
X-FTAI-RateLimit-Remaining: 574
X-FTAI-RateLimit-Reset: 1785363560    // unix seconds
Retry-After: 12                       // on 429 only
PlanSustained / minBurstBatch rows / day
Sandbox60060 / s10,000
Scale6,000300 / s500,000
EnterpriseContractedContractedContracted

Illustrative figures — your contracted limits are in your order form. Batch endpoints have their own daily row allowance and do not consume the per-minute limit.

A correct retry policy

// retry: 429, 503, and 5xx. Never 4xx.
const RETRYABLE = new Set([429, 500, 502, 503, 504]);

async function call(fn, attempt = 0) {
  const res = await fn();
  if (res.ok || !RETRYABLE.has(res.status) || attempt >= 5) return res;
  const after = Number(res.headers.get('Retry-After'));
  const wait = after
    ? after * 1000
    // exponential with full jitter — not a fixed backoff
    : Math.random() * Math.min(30000, 2 ** attempt * 1000);
  await new Promise(r => setTimeout(r, wait));
  return call(fn, attempt + 1);
}