The API uses standard HTTP status codes. A non-2xx response means the call did not succeed; the body explains why.
Status codes#
| Code | Meaning | What to do |
|---|---|---|
200 | OK | You have a result in results. |
400 | Invalid request | The body was not valid JSON, or query was missing or over 500 characters. Check your quoting and Content-Type: application/json. |
401 | Unauthorized | The key is missing, malformed or revoked. Send it as Authorization: Bearer <key>. |
429 | Rate limit exceeded | You are over quota. Back off and retry — see Rate limits. |
5xx | Server error | A transient problem on our side. Retry with backoff; if it persists, contact us with the trace_id. |
Error response shape#
Errors return JSON with a message and, where available, the trace_id for the request:
{
"error": "invalid_request",
"message": "query must be 500 characters or fewer",
"trace_id": "oden-mquycpae-p3thrk"
}
Handling errors well#
- Check the status code, not just the body. A
429and a400need different responses — one retries, one is a bug in your request. - Log the
trace_id. It is the fastest way for us to find your request if you report an issue. - Retry only what is retryable.
429and5xxare worth retrying with backoff;400and401are not — fix the request or the key.
r = requests.post(url, headers=headers, json=body, timeout=30)
if r.status_code == 401:
raise RuntimeError("Check your ODEN_KEY")
if r.status_code == 400:
raise ValueError(r.json().get("message", "bad request"))
if r.status_code in (429, 500, 502, 503):
... # back off and retry
r.raise_for_status()
data = r.json()
Next steps#
- Rate limits — the detail behind
429. - Authentication — the detail behind
401.