| Plan | Price | Requests per day |
|---|---|---|
| Free | $0 | 50 |
| Personal | $9/mo | 1,000 |
| Dev | 250/yr) | 5,000 |
| Pro | 49/mo billed annually) | 50,000 |
/api/v1/..., /api/v2/...) counts as one, whatever the response size.get_snapshot tool costs two for a company, ETF or earnings snapshot and one for the others./health, /about, /services, /sources) are public and do not count.x-ratelimit-limit: 50
x-ratelimit-remaining: 37
x-ratelimit-reset: 2026-07-18T23:59:59Zx-ratelimit-reset is the end of the current UTC day. Read x-ratelimit-remaining and back off before you hit zero; a client that only reacts to 429 wastes its last requests of the day on rejections.429 Too Many Requests with a Retry-After header carrying the number of seconds until the UTC midnight reset. Honor it - retrying earlier only burns connections, the counter does not free up before the reset. The detail string names the daily limit, your current plan and a link to upgrade.lane_busy, see Upstream failures below). Those carry a Retry-After of seconds, or none, and a retry after that wait usually succeeds. The rate limit headers still show what is left of your quota./api/... path carries a JSON body with a detail field: a string for most errors, a list for validation errors (422), and an object for typed upstream failures (below). A 401 adds a stable code. A 500 is the exception and has no stable shape.| Status | Meaning | Body |
|---|---|---|
| 400 | The endpoint refused a parameter value or combination, or the upstream refused the identifiers you sent | Detail string, or a typed upstream object |
| 401 | Missing or invalid x-api-key | JSON with code: missing_api_key, invalid_api_key or unauthorized |
| 404 | Unknown path or no data for the requested entity | {"detail": "Not Found"}, an endpoint-specific detail string, or a typed upstream object |
| 413 | The request asks for more than the upstream will serve | Typed upstream object (upstream_too_large) whose retry_hint says how to narrow the request, or a detail string |
| 422 | Request validation failed (bad parameter value, malformed identifier) | {"detail": [...]} with the offending parameter named per entry |
| 429 | Daily quota exhausted, or an upstream or our queue for it asks you to slow down | Quota: a detail string naming the limit, the plan and the upgrade link, with Retry-After seconds to the UTC midnight reset. Otherwise a detail string or a typed upstream object (lane_busy), with a Retry-After of seconds or none |
| 500 | Internal error on our side | No stable shape: an error label (for example response_shape_invalid) or a plain-text body. Branch on the status, not the body |
| 502 | The upstream data source failed or returned an unusable payload | Typed upstream object, or an endpoint-specific detail string |
| 503 | The upstream is unreachable or has queued the request, or a required dataset is still warming up after a deploy | Typed upstream object, or a detail string; retry with backoff |
| 504 | The upstream did not answer in time | Typed upstream object |
code tells them apart; see Authentication for an example, and Request cost and error codes for every label an error carries.retry_hint below); if a source degrades for longer, status.sugra.ai shows it.{
"detail": {
"error": "upstream_error",
"source": "abs",
"upstream_status": 503,
"retry_hint": "transient upstream condition; retry later"
}
}error | Status | Retrying the same request |
|---|---|---|
upstream_error | 502 | Helps when upstream_status is 429 or 5xx. Otherwise read retry_hint: it says whether to retry or what to change |
upstream_shape_unexpected | 502 | Sometimes: a maintenance page or a partial answer clears on retry, a changed format does not. Retry a few times, then stop |
upstream_unreachable | 503 | Helps after a backoff |
upstream_deferred | 503 | Helps later: the upstream accepted the request and queued it |
upstream_timeout | 504 | Helps after a backoff |
lane_busy | 429 | Helps after Retry-After: our queue for that upstream is full. Not your quota |
upstream_too_large | 413 | Does not help: the upstream will not serve a request that large. retry_hint says how to narrow it |
upstream_rejected_request | the upstream's own 400 or 404 | Does not help: check the identifiers you sent |
source names the upstream, and upstream_status is the status it returned, when it returned one. retry_hint is written for people: branch your code on error and upstream_status, not on the hint's wording. Endpoints that are not typed yet answer the same failures with a detail string.Retry-After and stops on a long one, and retries a 502, 503 or 504 unless the typed body says the upstream refused the request itself. A long wait with x-ratelimit-remaining at 0 is the daily quota; with requests left, it is an upstream asking for a pause.x-ratelimit-remaining is authoritative and free to read - it rides on every successful response and counts what is left for the whole account, not for one key.