GuidesResources
Errors
Every error — 4xx and 5xx alike — uses one shape:
{ "success": false, "error": { "code": "INSUFFICIENT_CREDITS", "message": "…", "retryable": false, "requestId": "req_01J..." }}code, message, retryable, and requestId are always present. Treat everything else as
optional.
Branch on code, not on the HTTP status. Several codes share a status — NOT_SUPPORTED
arrives as both 400 and 501 depending on where the gap is — and the code is the specific
one. Use retryable to decide whether to try again at all.
The codes you will see
Section titled “The codes you will see”| HTTP | Code | Meaning | Retry |
|---|---|---|---|
| 400 | VALIDATION_ERROR | Bad or missing parameters | No |
| 400 | CATALOG_QUERY_TOO_BROAD | The filter matches too much to serve; narrow it | No |
| 400 | BRIDGE_UNSUPPORTED | The venue has no bridge-in route: custody-only or fiat | No |
| 400 | TRADERS_UNSUPPORTED | The venue or requested trader verb is not served | No |
| 400 | SQL_QUERY_FAILED | The analytical query failed to execute | No |
| 401 | UNAUTHORIZED / AUTHENTICATION_ERROR | Missing, unknown, or revoked key | No |
| 402 | INSUFFICIENT_CREDITS | Balance below the endpoint weight | After top-up |
| 403 | PLAN_REQUIRED / PERMISSION_DENIED | The plan does not include this route or window | After upgrade |
| 403 | SCOPE_MISSING | The key lacks the required scope, e.g. trade | No |
| 404 | MARKET_NOT_FOUND / EVENT_NOT_FOUND / OUTCOME_NOT_FOUND | Unknown record | No |
| 404 | EXCHANGE_NOT_AVAILABLE / VENUE_NOT_AVAILABLE | Unknown or unserved venue | No |
| 404 | CLUSTER_NOT_FOUND | Cluster detail lookup missed | No |
| 404 | SNAPSHOT_NOT_FOUND | No stored order-book snapshot in that archive window | No |
| 404 | ROUTE_NOT_FOUND | The route is not mounted | No |
| 409 | API_KEY_LIMIT | The account’s active-key cap is reached | After revoking a key |
| 422 | BRIDGE_NO_ROUTE | No route for that source/amount; change source chain, token or amount | No |
| 429 | RATE_LIMITED / RATE_LIMIT_EXCEEDED | Request window exceeded | Yes, with backoff |
| 400/501 | NOT_SUPPORTED / ACCOUNTS_UNSUPPORTED | An honest capability gap | No |
| 502 | VENUE_RELAY_FAILED | The venue relay failed before returning a usable result | Yes, with backoff |
| 502/503 | BRIDGE_UPSTREAM | Provider rejected or failed | Yes, with backoff |
| 503 | BRIDGE_PROVIDER_UNAVAILABLE | Provider not configured or down | Yes, with backoff |
| 503 | CATALOG_UNAVAILABLE / HISTORY_UNAVAILABLE | The lane is temporarily unavailable | Yes, with backoff |
| 503 | MATCHES_UNAVAILABLE | The cross-match lane is not enabled on this deployment | Yes, once enabled |
| 503 | ARBITRAGE_UNAVAILABLE | Executable-arbitrage lane disabled on this deployment | Yes, once enabled |
| 503 | PLATFORM_UNAVAILABLE / BILLING_UNAVAILABLE | Temporary outage | Yes, with backoff |
| 503 | SQL_TIMEOUT | The analytical query exceeded its time limit | Yes, with backoff |
| 5xx | INTERNAL / NETWORK_ERROR | Unexpected failure or transport error | Yes, with backoff |
MATCHES_UNAVAILABLE and ARBITRAGE_UNAVAILABLE are the two codes above that are about a
deployment rather than a request. The six cross-match verbs — fetchMarketMatches,
fetchMatchedMarkets, compareMarketPrices, fetchHedges, and the deprecated fetchMatches /
fetchMatchedPrices aliases — are always mounted, so an unenabled lane answers an honest 503
instead of a 404. It is marked retryable because enabling the lane is a deployment change, not
something a caller can fix by retrying now, and it is deliberately raised before any credit is
debited: a dark lane never charges you and never answers 402. The
/api/{exchange}/fetchArbitrage route is always mounted the same way and stays dark with
ARBITRAGE_UNAVAILABLE until READS_ENABLE_ARBITRAGE is set and the live order-book fetcher is
wired through READS_ENABLE_ORDERBOOK. It is likewise retryable and raised before any credit is
debited for the same deployment-change reason.
The table includes runtime codes that the generated ErrorDetail enum in the
API reference does not yet list. Branch on the response’s code and retryable fields.
NOT_SUPPORTED is not a failure
Section titled “NOT_SUPPORTED is not a failure”NOT_SUPPORTED means the venue does not expose that capability at all — no public trades
tape, no per-address order list. It is a correct answer about the world.
Do not retry it, do not fall back to a different venue silently, and do not render it as an
empty result. An empty array means “ran and found nothing”; NOT_SUPPORTED means “cannot
run here”. See Capability-honest data.
Retrying
Section titled “Retrying”Retry only when retryable is true, regardless of HTTP status. Current retryable errors include
429, upstream and relay failures returned as 502, and temporary 503 failures. Errors marked
non-retryable will fail identically on the second attempt.
INSUFFICIENT_CREDITS is the one that looks retryable and is not — the balance will not
change because you asked again. Surface it and stop.
For backoff shape, jitter, and why writes are never auto-retried, see Rate limits & retries.
Typed errors in the SDK
Section titled “Typed errors in the SDK”The TypeScript SDK throws typed subclasses of PredictefyError, so you can
branch on the class instead of parsing strings. The server’s code and message are
preserved on the thrown error either way.
import { InsufficientCreditsError, NotSupportedError } from '@predictefy/sdk';
try { await client.gemini.fetchTrades(marketId);} catch (err) { if (err instanceof NotSupportedError) return renderUnavailable(); if (err instanceof InsufficientCreditsError) return renderTopUp(); throw err;}