Sequence/docs

Rate Limits

Sequence enforces per-client API rate limiting with a token-bucket model.


REST API Limit Model

  • Limits are applied per authenticated client (client_id), not per source IP. Two API keys for the same account share that account's profile-level limit; two keys for different accounts have fully isolated buckets.

  • The configured limit comes from your account profile (rate_limit_qps).

  • The implementation is a token bucket: bucket capacity = 2 × qps (lets you absorb a brief burst after idle), refill = qps tokens/second.

  • If no profile-specific value is set, default behavior is low-QPS safe mode.

  • Default per-tier values:

    TierSustained QPSBurst capacity
    free510
    paid / pro / enterprisevaries — set on profile2 × qps
  • Exceeding limit returns 429 Too Many Requests with:

json
{
  "error": "Rate limit exceeded"
}
Note

Docs examples should not hardcode one global QPS number. Your effective limit depends on your account profile and environment configuration.


SSE Streams

SSE endpoints are long-lived authenticated HTTP connections:

  • GET /v1/tca/stream — post-completion TCA reports
  • GET /v1/risk/stream — risk-state changes
  • GET /v1/alerts/stream — user-defined alert triggers

For order and fill streams, use the unified WebSocket /v1/stream (subscribe to the orders and fills channels).

Practical guidance:

  • Run one consumer per stream type per application instance.
  • Reconnect with backoff on disconnect.
  • Filter server-side where supported (node_order_id, edge_id) to reduce client load.

WebSocket Market Data

GET /v1/stream is the unified WebSocket — subscribe to prices:<symbol>, book:<symbol>, orders, fills, routing, tca, funding, and more on a single connection.

Recommended auth flow:

  1. GET /v1/ws/ticket with bearer auth
  2. Connect with ?ticket=<short-lived-ticket>

This avoids putting long-lived API keys in URLs.


Headers and Retry Behavior

Current API behavior to rely on:

  • 429 indicates throttling.
  • Error body includes a structured envelope with error.code = "RATE_LIMITED" and a request_id for correlation.
json
{
  "error": {
    "code": "RATE_LIMITED",
    "message": "rate limit exceeded",
    "request_id": "550e8400-..."
  }
}

Do not assume standardized rate-limit headers (such as X-RateLimit-*) are always present.


Client Best Practices

  1. Prefer streaming (SSE/WebSocket) over polling hot endpoints.
  2. Use exponential backoff on 429, 503, and disconnects.
  3. Batch reads where possible (/v1/nbbo, paginated /v1/orders).
  4. Use separate API keys per service to isolate load.

Minimal retry template:

javascript
async function withBackoff(requestFn, retries = 4) {
  for (let i = 0; i <= retries; i++) {
    const res = await requestFn();
    if (![429, 503].includes(res.status)) return res;
    const delayMs = Math.min(1000 * 2 ** i, 8000);
    await new Promise((r) => setTimeout(r, delayMs));
  }
  throw new Error('request failed after retries');
}