Credits & errors

Errors

Every error the API returns — the RFC 9457 problem shape, every error code, and retry guidance.

When something goes wrong, the API tells you what, why, and whether retrying will help — in one predictable shape, every time. You can write your error handling once against the code field and it will hold for every endpoint, because the set of codes is closed: nothing outside the error codes list will ever come back.

Use cases#

One error handler for the whole API

Switch on the code field — never on title or detail, which are prose and may be reworded. One handler covers every endpoint, current and future, because the set of codes is closed and additive.

Decide whether to retry

Read the status against the retry table: 429/503/504 retry after Retry-After; 402 and 4xx never do. You recover from transient failures without ever re-charging a request that cannot succeed.

Escalate to support

Log request_id on every response and quote it when you write in. Support can trace the exact request through the platform's logs, with no reproduction steps needed.

The problem shape#

Every error response — including validation failures and unhandled exceptions — is an RFC 9457 Problem Details document, served as application/problem+json. RFC 9457 is the web standard that defines this shape; you don't need to know it, because the example and table below cover the full contract.

{
  "type": "https://dashboard.sproutsdata.ai/errors/credits-insufficient",
  "title": "Insufficient credits",
  "status": 402,
  "code": "credits.insufficient",
  "detail": "This action costs 0.595 credits; 0.25 available.",
  "instance": "/v1/search",
  "request_id": "bd22794d0c5c06f1d3e7045e3c0e32ab",
  "extra": { "available": 0.25, "required": 0.595, "action_type": "dsp.search.auto" }
}
FieldWhat it is
typeAbsolute URI for this error class: https://dashboard.sproutsdata.ai/errors/<slug>, where the slug is the code with . and _ replaced by -. It is the same on every deployment, and it redirects to the code's entry on Error codes
titleShort human-readable summary. Prose; may be reworded
statusThe HTTP status, repeated in the body
codeThe contract. Machine-readable, from the closed set in Error codes. Switch on this, never on title or detail
detailA sentence for a developer. Prose; may be reworded
instanceThe request path
request_idThe id that traces this request through our logs. Always log it — it is the only handle a support conversation can start from
extraError-specific fields (e.g. extra.errors[] on validation failures, the credit figures on a 402)

Fields with no value are omitted from error bodies (unlike success envelopes, which always serialize every field).

The http-<status> fallback

Errors that don't originate in application logic — a bare 404 on an unknown path, a 405 from the wrong method — can't carry a code from that set. Instead:

  • type is a fallback of https://dashboard.sproutsdata.ai/errors/http-<status> (so http-404, http-405, and so on), which redirects to this section.
  • code is filled generically: request.invalid for 4xx, internal for 5xx.

Treat them as transport-level mistakes: check the method, path, and base URL rather than the code.

Retry guidance#

StatusRetry?
429, 503, 504Yes — after the number of seconds in the Retry-After header. A metered call is charged only once its response is ready, so a 503 or 504 from the retrieval engine was not charged. A 503 credits.unavailable may have been charged: retry it with the same Idempotency-Key
502Yes, later. Agent-run routes send 502 upstream.unavailable when the agent engine answered with an error. Retrying a run start with the same Idempotency-Key is safe
402Never. Out of credits is fixed by buying, not waiting. 402 is never rate limiting and carries no Retry-After
400, 401, 403, 404, 409, 422, 501No — fix the credential, request, or feature choice first
500Once, cautiously; then contact support with the request_id

There is no per-key request-rate limit on the REST API today. Build your client to honour a 429 with Retry-After anyway (back off, then retry after the header's seconds), because limits may be introduced, and they will be announced in the changelog first. The 429s you can meet today are listed under quota.*.

request_id — on every response#

request_id appears on every response, success and error:

  • In the body: meta.request_id on successes, top-level on problems.
  • As an X-Request-Id response header, so a transport interceptor can log it without parsing bodies.

You may send your own X-Request-Id (≤128 chars, [A-Za-z0-9_.:-]) to correlate with your logs; it is echoed back.

More on errors#