Skip to content

Errors

Every error carries a stable machine-readable code plus a human-readable message. Codes never change meaning; messages may be reworded. Both official SDKs surface the code (Error::Api { code, .. } in Rust, BlinkApiError.code in TypeScript).

REST

Errors return a JSON envelope with an HTTP status:

json
{ "code": "NOT_FOUND", "error": "market 99999 not found" }
CodeStatusMeaning
INVALID_PARAMETER400A parameter is missing or invalid (interval, market_type, status, empty q, malformed cursor). The message names the parameter and the accepted values.
NOT_FOUND404Resource does not exist: unknown market, account, subaccount, token, client order id, or transaction hash.
INTERNAL_ERROR500Internal error. Safe to retry with backoff.

Successful responses are always 200 with the documented shape; list endpoints return [] rather than 404 when the account or market simply has no data yet.

WebSocket

Protocol errors arrive as error frames on the open connection:

json
{
  "type": "error",
  "request_id": "request_1",
  "subscription_id": null,
  "code": "UNKNOWN_CHANNEL",
  "error": "unknown channel `bogus`"
}
CodeMeaningConnection
INVALID_MESSAGEFrame was not valid protocol JSON.stays open
UNKNOWN_CHANNELSubscription named a channel that does not exist.stays open
INVALID_SUBSCRIPTIONMissing or invalid scope (market_id, is_perps, interval, address, or subaccount_id).stays open
UNKNOWN_SUBSCRIPTIONAn unsubscribe command named a server subscription ID that is not active on this connection.stays open
HEARTBEAT_TIMEOUTThe client sent no message for 60 seconds.closed — reconnect and re-subscribe
FEED_UNAVAILABLEThis deployment does not serve the streaming feed.closed

Replay gaps and per-subscription lag use a routed resync_required frame rather than a connection-wide error. Replace only that subscription with a fresh snapshot. If the bounded connection queue itself overflows, the gateway drops the socket immediately so reconnect establishes consistent snapshots.

Transaction submission

Two distinct failure surfaces exist, and only one of them is on-chain:

1. Rejected at admission (the common case). The sequencer pre-executes every transaction before sequencing it. A transaction that would fail — insufficient margin, bad nonce, invalid signature, an order outside the price band — is rejected synchronously and never reaches a block. The submission response carries the failure, e.g.:

Transaction rejected: Transaction execution unsuccessful

Rejected transactions do not consume a nonce and are not indexed; GET /transactions/{hash} returns 404 for them. Retrying after fixing the cause is safe.

2. Sequenced but reverted (rare). Transactions that bypass sequencer admission — forced inclusion via the data-availability layer — execute in a block and can revert there. These ARE indexed, with status: "failed" and the receipt's reason in error:

json
{ "hash": "0x…", "status": "failed", "error": "insufficient margin",
  "block": "1346", "success": false, "timestamp_ms": 1717000000000 }

So in the transaction resource, committed and failed both mean sequenced; admission rejections surface only in the submit response.

Nonce races

Concurrent submissions from one account can lose a nonce race and come back as an admission rejection. Because rejected transactions never sequence, resubmitting with the same payload is safe and is what the official clients do.