Errors and rate limits

Failures always use the same envelope. Branch on the code, never on the message text or on substring matching.

Error envelope

{
  "error": {
    "code": "payment_past_due",
    "message": "API access is suspended because a payment is past due."
  }
}

code is contract and stable. message is prose written for a human reading a terminal and may be reworded at any time.

Code table

StatusCodeWhat it meansWhat to do
400invalid_requestA query parameter failed validation: an out of range limit, an unparsable cursor, or a malformed updated_since.Fix the request. Retrying unchanged will fail identically.
401invalid_keyThe key is missing, malformed, unknown, revoked, or past its expiry. The four cases are intentionally indistinguishable.Check the Authorization header, then check the key's status on the API keys page. Do not retry in a loop.
402payment_past_dueThe organization has an unpaid invoice. API access is suspended while the console remains usable.An organization admin resolves billing. The same key resumes working automatically, with no reissue.
402subscription_inactiveThe subscription is canceled or restricted, or a trial ended without a payment method.Reactivate the subscription in the console. The same key resumes working automatically.
403insufficient_scopeThe key is valid but was not granted a scope this endpoint requires. The message names the missing scope.Create a key with the required scope and migrate to it. Scopes on an existing key cannot be widened.
403api_disabledAPI access has been switched off for this organization by EnrollPilot support, independently of billing.Contact support. Retrying will not help.
404not_foundNo such record, or the id belongs to another organization. Both answer the same way so the API cannot be used to probe for ids.Verify the id came from a list response on this same key.
405method_not_allowedVersion 1 is read only. Anything other than GET is refused, and the response carries an Allow: GET header.Use GET.
429rate_limitedA per key, per organization, or unauthenticated per address limit was exceeded.Wait for the number of seconds in the Retry-After header, then retry with backoff.
500internal_errorSomething failed on our side. The message is deliberately generic; details stay in our logs.Retry with exponential backoff. If it persists, contact support with the approximate time of the request.
503api_unavailableThe public API is switched off globally, which happens only during an incident.Retry later with backoff.
503api_temporarily_unavailableThe API cannot currently enforce its rate limits and refuses traffic rather than serve it unthrottled.Retry shortly with backoff.

Understanding 402

The API is stricter about billing than the console is, on purpose. An organization whose payment is past due can keep working in the browser while automated data extraction is suspended. That is why payment_past_due exists as its own code rather than being folded into subscription_inactive.

SituationCodeRecovery
Invoice unpaid, subscription otherwise intactpayment_past_duePay the invoice. Access returns automatically.
Subscription canceled or restrictedsubscription_inactiveReactivate the subscription.
Trial ended with no payment method on filesubscription_inactiveAdd a payment method and choose a plan.

Never treat 402 as a reason to reissue keys

Billing is evaluated on every request, not baked into the key. Creating a new key while suspended produces a new key that returns the same 402. Keep the existing key and fix billing.

Rate limits

LimitApplies toCeiling
Per keyEach individual API key120 requests per minute
Per organizationAll of an organization's keys combined600 requests per minute
UnauthenticatedRequests with no valid key, by source address60 attempts per minute

The per organization ceiling is the real limit. Adding keys does not add capacity, so splitting a job across several keys will not raise your throughput.

Handling 429

  • Read the Retry-After header and sleep for at least that many seconds.
  • Back off exponentially with jitter on repeated limiting. Do not retry in a tight loop.
  • Prefer larger pages over more requests. One call at limit=200 costs the same as one call at limit=10.

Retry policy at a glance

StatusRetry?
400, 401, 403, 404, 405No. Fix the request, the key, or the scope.
402No. Resolve billing first, then resume on the same key.
429Yes, after Retry-After, with backoff.
500, 503Yes, with exponential backoff and jitter.

Caching

Every response, success and failure alike, is sent with Cache-Control: private, no-store, max-age=0. Do not place a shared cache in front of the API.