Developers

Errors and rate limits

Every error uses the same JSON envelope, so one handler covers the whole API. Quote the requestId when you contact support.

The error envelope

json
{
  "error": {
    "code": "insufficient_scope",
    "message": "This API key needs the \"products:write\" scope for this request.",
    "requestId": "req_…",
    "details": { "requiredScope": "products:write" }
  }
}
  • code is stable and machine-readable. Branch on it, not on message.
  • message is written for people and may change.
  • details is optional structured context, such as validation issues or quota numbers.

Status codes

StatusMeaningWhat to do
400Malformed requestFix the request. Don’t retry unchanged.
401Missing, invalid, expired or revoked API keyCheck the key and environment.
403The key lacks the scope, or the endpoint isn’t available to API keysAdd the scope to a new key, or use the portal.
404Not found, or not in your companyCheck the id. Other companies’ records always return 404.
422Validation failed (details list the fields)Fix the fields named in details.
429Rate limitedWait for Retry-After seconds, then continue.
503Temporarily overloaded (code overloaded)Retry once after Retry-After plus jitter, only for safe requests.

Rate limits

Each API key has its own per-minute limit, set by your company’s plan. Every authenticated response includes X-RateLimit-Limit and X-RateLimit-Remaining.

PlanRequests per minute, per key
Starter60
Growth600
Enterprise6,000

When you go over the limit the API returns 429 rate_limited with a Retry-After header in seconds. Wait that long before sending more requests.

503 overloaded

If the API is briefly overloaded it sheds load with 503, error code overloaded and a short Retry-After. Nothing was processed.

  1. Retry once, after Retry-After seconds plus a small random jitter (for example up to 1 second), so many clients don’t retry at the same moment.
  2. If the retry also fails, stop and surface a calm “busy, try again shortly” message.

Never automatically retry writes

Only automatically retry reads (GET) and POST /v1/verify, which is a status check and safe to repeat. Don’t automatically retry other POST, PATCH or DELETE requests: a timeout doesn’t tell you whether the change happened. For code generation, send an idempotencyKey so a deliberate retry is safe.

Retry a safe request once
async function withOverloadRetry(send: () => Promise<Response>) {
  const first = await send();
  if (first.status !== 503) return first;
  const retryAfter = Number(first.headers.get('retry-after') ?? '1');
  const delayMs = Math.max(1, retryAfter) * 1000 + Math.random() * 1000;
  await new Promise((resolve) => setTimeout(resolve, delayMs));
  return send(); // one retry only
}

The TypeScript SDK does this for you: it retries safe requests once on 503 and exposes retryAfterSeconds on CredCheckError.

Next: Pagination

Errors and rate limits · CredChek