Errors and limits
The one error envelope, every status code and error code, and the rate limits.
Every failure the API returns, from a typo in a field to a fault on our side, uses the same JSON envelope and an appropriate HTTP status code. There is exactly one shape to parse, so your error handling is one code path.
The envelope
{
"error": {
"code": "insufficient_balance",
"message": "Your balance cannot cover this call. Add funds and try again.",
"param": null,
"doc_url": "https://outbound.dev/docs/errors",
"request_id": "req_8f3a1b2c9d4e"
}
}| Field | Type | Description |
|---|---|---|
code | string | Stable, machine-readable identifier. Codes never change meaning, so branch your logic on code. |
message | string | Human-readable explanation, suitable for logs. Messages may be reworded at any time: never match on them. |
param | string or null | The request field that failed validation; null otherwise. |
doc_url | string or null | Link to this page. |
request_id | string or null | Unique identifier for the request, also in the X-Request-Id response header. Quote it when you write to us. |
Status codes
The HTTP status tells you the class of failure; the code tells you the specific one.
| Status | Meaning |
|---|---|
200 | Found. You pay the listed price. |
202 | Accepted. Poll the job until it finishes. |
401 | The API key is missing, malformed, expired or revoked. |
402 | Your balance, or the key's daily cap, cannot cover the call. The body says how much to add. No work started. |
403 | The key is valid but its scopes do not include this endpoint. |
404 | The endpoint or resource does not exist, or is not visible to your organization. |
409 | A request with the same Idempotency-Key is still running. |
422 | The request failed validation, or reused an Idempotency-Key with a different body. param names the field where it can. |
429 | Too fast. Retry-After says when to try again. |
500 | An unexpected error on our side. |
503 | Your organization is still being set up. Retry-After says when to try again. |
Error codes
| Code | Status | Meaning |
|---|---|---|
unauthorized | 401 | The key is missing, malformed, expired or revoked. |
insufficient_balance | 402 | Your balance, less what open calls have set aside, cannot cover the call. Auto-recharge, if it is on, tops up. |
key_daily_cap_reached | 402 | The call would pass the key's daily cap. Caps count per UTC day. |
insufficient_scope | 403 | The key's scopes do not include this endpoint. |
not_found | 404 | No such endpoint or resource for your organization. |
idempotency_in_progress | 409 | A request with this Idempotency-Key is still running. |
idempotency_key_reused | 422 | This Idempotency-Key was sent before with a different body. |
validation_error | 422 | A field is missing or invalid; param names it. |
rate_limited | 429 | A rate limit was reached; wait for Retry-After. |
unexpected_error | 500 | Something failed on our side. Retry, or write to us with the request_id. |
org_provisioning | 503 | Your organization is still being set up; wait for Retry-After. |
Handling failures
- Branch on `code`, never on `message`. Codes are stable identifiers; messages are prose we may reword to be clearer. An integration that string-matches messages will break on a copy edit.
- A `404` does not always mean the resource never existed. The API never confirms that another organization's resources exist, so a
404on an id you believe you own can also mean it belongs to a different organization. - Retry `429`, `500` and `503`; fix everything else. On
429and503, wait for theRetry-Afterheader before retrying. Send anIdempotency-Keyon every POST, so a retry is never charged twice. - On `402`, add funds or raise the key's cap. No work started and nothing was charged. Turn on auto-recharge in the console to top up when the balance runs low.
Rate limits
Every request is checked against two windows: a per-minute limit and a per-day limit. A request must fit both. Limits are counted per organization, so ten keys do not get ten times the limit.
Search starts at 20 requests a minute and 1,000 a day. Every other endpoint has its own per-minute and per-day limits, and the headers below show where you stand on each call. Need more? Write to hello@outbound.dev and we can raise your organization's limits.
Reading the headers
Every response that reaches a limit check, success or 429, carries three headers:
| Header | Meaning |
|---|---|
RateLimit-Limit | Quota of the binding window |
RateLimit-Remaining | Requests remaining in the binding window |
RateLimit-Reset | Seconds until the binding window resets |
The binding window is whichever of the two windows has fewer requests remaining: the one that will stop you first.
Handling 429s
When you exceed a limit, the API returns 429 with the error envelope and a Retry-After header giving the seconds to wait. Wait that long before retrying, and back off further if you keep reaching the limit.