DocsDocs

API reliability

Error format and codes, rate limits, retry rules, availability, versioning and deprecation.

13 Nov 2026 · 5 min read

Every error is JSON with an error code and, usually, a message. Limits are per minute and per month on each plan, and keyless callers are capped per IP. A 429 tells you how long to wait in Retry-After. All routes live under /v1, and a retired route is announced in the changelog.

Error format

An error response is JSON with a machine-readable error code and, on most paths, a human message. Some errors add fields, such as tier, upgrade_url, hint or retry_after_seconds. Branch on the HTTP status and error. The message text can change.

One failure is not JSON: an unhandled server error returns 500 with a plain text body. Since 19 June 2026 every endpoint uses this shape, as the changelog records. HTTP status codes did not change then.

Shell
curl -i "https://api.goldprice.dev/v1/spot/FOO"
Response, 400
{  "error": "invalid_symbol",  "symbol": "FOO",  "hint": "expected format BASE-QUOTE-CONTRACT, e.g. XAU-USD-SPOT"}

A request that fails parameter validation returns 422 with error: validation_error and a details list. Each item has loc (where the field is), msg and type.

Shell
curl -i "https://api.goldprice.dev/v1/convert?from=XAU"
Response, 422
{  "error": "validation_error",  "message": "Request validation failed",  "details": [    {      "loc": [        "query",        "to"      ],      "msg": "Field required",      "type": "missing"    }  ]}

Error codes

These are the codes an integration meets most. Each endpoint page lists its own: prices, carat and convert. The OpenAPI spec documents the error responses of every route.

StatuserrorWhen
400invalid_symbolThe symbol is not in the form BASE-QUOTE-CONTRACT, for example XAU-USD-SPOT.
400range_too_deepA bars request reaches back further than the plan's intraday depth. The response carries max_depth_days.
400interval_not_supported_for_symbolThe interval is not available for that symbol.
401invalid_api_keyThe bearer token is missing, malformed or revoked. A request that sends a key is never answered as keyless.
403plan_gatedThe plan does not include this route, interval or history depth. The response names your tier and an upgrade_url.
404unknown_countryNo physical prices for that country. The response lists the countries that have them.
404not_foundThe requested resource is unavailable or not found.
422validation_errorA parameter is missing or invalid. details says which.
429rate_limitedA keyless caller is over the per-IP hourly cap.
429rate_limit_exceededOver the per-minute limit: the plan's for a key, the keyless limit for an IP.
429quota_exceededThe plan's monthly requests are used up. The quota resets when the next month starts.
429stream_connection_limitThe plan's limit on open streams is reached. Retry-After is 5 seconds.
503auth_unavailableThe key could not be checked. Retry-After is 30 seconds.
503spot_unavailable, fx_unavailableA price or exchange rate the request needs is temporarily unavailable.
503stream_unavailableThe live feed behind a stream is down.
500noneThe operation failed internally. The body is plain text.

Rate limits and plan limits

Three limits apply. Requests a minute and requests a month come from the plan. Callers with no key are limited per IP: 10 a minute and 100 an hour. Free plan limits has every plan as a table, and pricing has the prices.

PlanRequests a minuteRequests a month
No key (per IP)10, and 100 an hourNot applicable
Free105,000
Basic60100,000
Plus120300,000
Pro3001,000,000

Free and keyless prices are 15 minutes late. Paid plans get live prices. History depth, intraday intervals, streaming and dealer prices also differ by plan, and a request outside the plan returns plan_gated.

The 429 response

A 429 carries five headers. X-RateLimit-Scope says which limit you hit.

HeaderMeaning
X-RateLimit-LimitThe limit for the window named by the scope.
X-RateLimit-RemainingRequests left in that window. 0 on a 429.
X-RateLimit-ResetUnix time, in seconds, when the window resets.
X-RateLimit-Scopeanon-ip-hour, tier-rate-minute or tier-quota-month.
Retry-AfterSeconds to wait before retrying.

A monthly 429 also sends X-Quota-Exceeded: true. This is a keyless caller over the per-minute limit, run against the live API. The numbers change on every call.

Response headers, 429
x-ratelimit-limit: 10x-ratelimit-remaining: 0x-ratelimit-reset: 1794543300x-ratelimit-scope: tier-rate-minuteretry-after: 43
Response body, 429
{  "error": "rate_limit_exceeded",  "tier": "free_2026",  "limit_per_minute": 10,  "retry_after_seconds": 43}

Retry guidance

Data endpoints are read-only GET requests, so repeating one changes nothing.

ResponseWhat to do
429 rate_limit_exceeded or rate_limitedWait Retry-After seconds, then retry. The same number is in retry_after_seconds in the body.
429 quota_exceededDo not retry automatically. The month's requests are used up. Retry-After is the time to the reset. Wait for it or change plan.
429 stream_connection_limitClose a stream you no longer need, or wait 5 seconds and reconnect.
503 auth_unavailableRetry after 30 seconds. The key was sent but could not be checked, and the request is not downgraded to keyless access.
503 spot_unavailable, fx_unavailable, stream_unavailableThe failure is temporary. Retry later, and use Retry-After when it is present.
500The operation failed internally. Retry later.
400, 401, 403, 404, 422Do not retry the same request. Fix the symbol, parameters, key or plan first.

A 200 can still carry old data. Every price has is_stale and computed_at, so check them before you show or store a price.

Availability and status

Measured uptime of the Gold Price API was 100% over the 30 days and 99.81% over the 90 days to 13 November 2026. That is a record of past performance, not a service level agreement. No plan has automatic credits, and any credit is at our discretion (Terms sections 9 and 16). Uptime history is public. Planned maintenance and incidents are announced on the status page and by email to paying customers.

GET /v1/health is a liveness probe and returns {"status":"ok"}. GET /v1/status is the readiness probe: it reports the deployed commit, Redis, how fresh each data collector is, and whether the gold, silver and copper bars are current. Neither needs a key.

Shell
curl "https://api.goldprice.dev/v1/health"
Response
{  "status": "ok"}

Versioning and deprecation

Every route is under /v1, and the OpenAPI spec describes them. A change to a route, a field or an error shape is listed in the changelog, for example the error format change of 19 June 2026 and the retirement of the probability surface on 26 July 2026.

GET /v1/prices/history is deprecated. Use `/v1/bars` instead. Responses from the deprecated route carry Deprecation: true and a Link header with rel="successor-version" pointing at /v1/bars. No retirement date is set. A Sunset header will be sent once there is one.

Shell
curl -i "https://api.goldprice.dev/v1/prices/history?symbol=XAU-USD-SPOT&from=2026-11-01&to=2026-11-05"
Response headers
deprecation: truelink: <https://api.goldprice.dev/v1/bars>; rel="successor-version"

Price changes take effect with 30 days' notice by email, and your current price holds through the notice period (Terms). Subscribers on older plans keep their plan, price and limits while they stay subscribed.

Keep reading

Start with a free API key

Free includes gold, silver and copper spot, MCP, and commercial use with a visible link to goldprice.dev.

Get a free key