error object. For example, asking for a series that doesn’t exist:
curl --request GET \
--url 'https://data-api.orbbit.co/v1/commodities/metric-values?metric_names=cheddar_close' \
--header 'authorization: Bearer YOUR_API_KEY'
400 Unknown metric
{
"error": {
"type": "invalid_request",
"message": "Unknown metrics: cheddar_close"
}
}
| Field | Type | Description |
|---|---|---|
error.type | string | A stable, machine-readable name for the kind of error. Branch on this. |
error.message | string | A human-readable explanation. Log it or show it to a developer, but don’t parse it, because the wording can change. |
Error types
| Status | error.type | What happened | What to do |
|---|---|---|---|
400 | invalid_request | A parameter is missing, the wrong type, or out of range. | Fix the request. The message names the parameter. |
401 | unauthorized | The API key is malformed, unknown, or revoked. | Send Authorization: Bearer <key> with a live key. |
402 | insufficient_balance | Paid calls only. Your balance can’t cover the call (Insufficient balance), or this key has used up its monthly spend budget (Monthly spend cap reached). Refused calls are not charged. | Top up on Balance or turn on auto-recharge, or raise the key’s budget. See Pricing & Billing. |
402 | payment_required | A paid call with no API key and no valid wallet payment. The body is the x402 price quote, not the error object above. Nothing was charged. | Pay with x402, or send an API key. See Pay With x402. |
403 | forbidden | The key is valid but lacks this endpoint’s scope. | Add the scope in the console. See Authentication. |
404 | not_found | No record has the ID in the path. | Check the ID came from a search result. |
429 | rate_limited | Calls without an API key only. More than 60 calls in a minute from one IP address. | Wait and try again, or send an API key. |
503 | service_unavailable | Wallet payments only. The payment service for the chosen network could not be reached. Nothing was charged. | Retry, or pay on the other network. |
Other 5xx | internal | Something went wrong on Orbbit’s side. The message is always An unexpected error occurred. | Retry with exponential backoff. If it persists, contact support. |
Common 400 errors
| Endpoint | Message | Cause |
|---|---|---|
GET /v1/commodities/metric-values | Unknown metrics: <names> | A name in metric_names or ids doesn’t exist. Get valid names from GET /v1/commodities. |
| Any endpoint | query: Unrecognized key: "<name>" | The call sends a parameter the endpoint doesn’t take, such as the older metrics, from or to. GET /v1/commodities/metric-values takes metric_names, ids, start_date and end_date. |
GET /v1/commodities/metric-values | This call would return <n> prices, more than 50000; … | The series and dates asked for hold more than 50,000 values. Narrow start_date and end_date, or ask for fewer series. |
GET /v1/commodities/metric-values | metric_names: give ids or metric_names | Neither metric_names nor ids was sent. |
GET /v1/commodities | Unknown attribute words: <words> | An attributes[<word>] filter names a word no product has. |
GET /v1/commodities | Unknown category <value>; category is one of … | category is not one of dairy, poultry, meat, grains, fruits or vegetables. |
GET /v1/commodities/metric-values | None | More than 100 series are named, a date is not a real YYYY-MM-DD day, or start_date is later than end_date. |
GET /v1/commodities | limit: Too big: expected number to be <=500 | limit is above 500. |
GET /v1/branded-foods | at most 200 per page | limit is above 200. |
Any endpoint with limit | None | limit is not a whole number of at least 1. |
Retrying
Retry only5xx errors, and 429 once the minute is up. Every other error will fail again until you change the request, or, for 402, until you add balance. Every endpoint only reads data, so retrying a request never changes anything.
Python
import random, time, requests
def get_with_retry(url, headers, params=None, attempts=5):
for attempt in range(attempts):
response = requests.get(url, headers=headers, params=params)
if response.status_code < 500:
return response
time.sleep(min(2 ** attempt, 30) + random.random())
return response