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#
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.
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.
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" }
}| Field | What it is |
|---|---|
type | Absolute 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 |
title | Short human-readable summary. Prose; may be reworded |
status | The HTTP status, repeated in the body |
code | The contract. Machine-readable, from the closed set in Error codes. Switch on this, never on title or detail |
detail | A sentence for a developer. Prose; may be reworded |
instance | The request path |
request_id | The id that traces this request through our logs. Always log it — it is the only handle a support conversation can start from |
extra | Error-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:
typeis a fallback ofhttps://dashboard.sproutsdata.ai/errors/http-<status>(sohttp-404,http-405, and so on), which redirects to this section.codeis filled generically:request.invalidfor 4xx,internalfor 5xx.
Treat them as transport-level mistakes: check the method, path, and base URL rather than the code.
Retry guidance#
| Status | Retry? |
|---|---|
| 429, 503, 504 | Yes — 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 |
| 502 | Yes, 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 |
| 402 | Never. 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, 501 | No — fix the credential, request, or feature choice first |
| 500 | Once, 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_idon successes, top-level on problems. - As an
X-Request-Idresponse 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.