Skip to main content
Every Struct surface returns errors in a consistent shape. This page covers the wire format for REST and WebSocket. The TypeScript SDK exposes a typed hierarchy on top, documented in SDK Errors.

REST

Failed REST requests return the same envelope as successful ones, with success: false and a human-readable message. The HTTP status reflects the error class.
{
  "success": false,
  "data": null,
  "message": "Invalid API key"
}

Status codes

StatusMeaningCommon causes
400 Bad RequestInvalid parameters.Missing required query string, malformed condition_id, unsupported candle resolution.
401 UnauthorizedMissing or invalid credentials.X-API-Key header absent, key disabled, or JWT signature invalid.
403 ForbiddenAuth succeeded but the key is over its limits.Org out of credits, key revoked, or quota exceeded.
404 Not FoundResource does not exist.Market, event, or trader not indexed (yet) or wrong identifier.
429 Too Many RequestsRate limit exceeded.See Rate Limits.
500 Internal Server ErrorUnexpected server failure.Transient. Retry with backoff.
502, 503, 504Upstream or timeout.Transient. Retry with backoff.

Headers

On 429, the response includes Retry-After (seconds) when applicable. Honour it before retrying.

Retrying

Any 5xx, 429, network failure, or timeout is retryable. Use exponential backoff with jitter; never tight-loop. The TypeScript SDK does this automatically when retry is configured.

WebSocket

In-band errors

Invalid messages on either the rooms (/ws) or alerts (/ws/alerts) endpoint return a JSON error frame instead of closing the connection:
{
  "error": "unknown event type"
}
{
  "type": "error",
  "message": "filter limit exceeded"
}
These cover protocol-level problems: unknown room IDs, malformed subscribe payloads, filter limits, and similar. The socket stays open; you can correct the message and retry.

Close codes

When the server closes the connection, it uses a standard WebSocket close code:
CodeReasonAction
1000Normal closure.Client disconnected cleanly. No action.
1001Server going away (restart, deploy).Reconnect with backoff.
1008Policy violation. Usually auth failure.Fix credentials before reconnecting. Do not retry blindly.
1009Message too large.Reduce filter size or split into multiple subscriptions.
1011Server error.Reconnect with backoff.
4001Authentication failed.Check api-key and token query parameters.
4002Connection limit reached.Increase your plan or close idle sockets. See Rate Limits.
4003Ping timeout (no client activity within keepalive window).Send pings every 30s.
4004Out of credits.Top up credits in the dashboard.

Reconnection

When the socket drops for any non-auth reason, reconnect with exponential backoff and resubscribe to every room you were previously in. Subscriptions are not persisted server-side; they live only for the lifetime of the connection. The TypeScript SDK handles reconnect and resubscribe automatically. See SDK WebSockets.
let attempt = 0;

function connect() {
  const ws = new WebSocket("wss://api.struct.to/ws?api-key=YOUR_API_KEY");

  ws.onopen = () => {
    attempt = 0;
    resubscribe(ws);
  };

  ws.onclose = (event) => {
    if (event.code === 1008 || event.code === 4001) return;
    const delay = Math.min(30_000, 1_000 * 2 ** attempt);
    attempt += 1;
    setTimeout(connect, delay + Math.random() * 500);
  };
}

Webhooks

Webhook deliveries are retried automatically when your endpoint returns a non-2xx response, with exponential backoff. Endpoints that fail repeatedly are paused; see Webhooks for the full retry schedule.

SDK errors

The TypeScript SDK lifts these wire-level failures into a typed hierarchy:
StructError
├── HttpError          (REST: non-2xx response)
├── NetworkError       (REST: fetch failed)
├── TimeoutError       (REST: request timed out)
└── WebSocketError
    └── WebSocketClosedError
See SDK Errors for the full hierarchy, retry behaviour, and example handlers.
Last modified on May 27, 2026