Skip to content

GuidesResources

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.

HTTPCodeMeaningRetry
400VALIDATION_ERRORBad or missing parametersNo
400CATALOG_QUERY_TOO_BROADThe filter matches too much to serve; narrow itNo
400BRIDGE_UNSUPPORTEDThe venue has no bridge-in route: custody-only or fiatNo
400TRADERS_UNSUPPORTEDThe venue or requested trader verb is not servedNo
400SQL_QUERY_FAILEDThe analytical query failed to executeNo
401UNAUTHORIZED / AUTHENTICATION_ERRORMissing, unknown, or revoked keyNo
402INSUFFICIENT_CREDITSBalance below the endpoint weightAfter top-up
403PLAN_REQUIRED / PERMISSION_DENIEDThe plan does not include this route or windowAfter upgrade
403SCOPE_MISSINGThe key lacks the required scope, e.g. tradeNo
404MARKET_NOT_FOUND / EVENT_NOT_FOUND / OUTCOME_NOT_FOUNDUnknown recordNo
404EXCHANGE_NOT_AVAILABLE / VENUE_NOT_AVAILABLEUnknown or unserved venueNo
404CLUSTER_NOT_FOUNDCluster detail lookup missedNo
404SNAPSHOT_NOT_FOUNDNo stored order-book snapshot in that archive windowNo
404ROUTE_NOT_FOUNDThe route is not mountedNo
409API_KEY_LIMITThe account’s active-key cap is reachedAfter revoking a key
422BRIDGE_NO_ROUTENo route for that source/amount; change source chain, token or amountNo
429RATE_LIMITED / RATE_LIMIT_EXCEEDEDRequest window exceededYes, with backoff
400/501NOT_SUPPORTED / ACCOUNTS_UNSUPPORTEDAn honest capability gapNo
502VENUE_RELAY_FAILEDThe venue relay failed before returning a usable resultYes, with backoff
502/503BRIDGE_UPSTREAMProvider rejected or failedYes, with backoff
503BRIDGE_PROVIDER_UNAVAILABLEProvider not configured or downYes, with backoff
503CATALOG_UNAVAILABLE / HISTORY_UNAVAILABLEThe lane is temporarily unavailableYes, with backoff
503MATCHES_UNAVAILABLEThe cross-match lane is not enabled on this deploymentYes, once enabled
503ARBITRAGE_UNAVAILABLEExecutable-arbitrage lane disabled on this deploymentYes, once enabled
503PLATFORM_UNAVAILABLE / BILLING_UNAVAILABLETemporary outageYes, with backoff
503SQL_TIMEOUTThe analytical query exceeded its time limitYes, with backoff
5xxINTERNAL / NETWORK_ERRORUnexpected failure or transport errorYes, 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 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.

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.

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;
}