Skip to main content

Rate Limits

All Senticore surfaces enforce rate limits. Limits depend on the surface and the client tier.

Trading-action limits (POST /api/v1/trading/actions)

The retail submit path is guarded by two independent limiters — you can trip either one:

LimiterScopeDefaultOn trip
Edge IP throttleper source IP10 req/s, burst 20429 plain text + x-ratelimit-after
Per-account limiterper account50 actions / 1000 ms429 JSON SubmitResponse, code: "rate_limited"
Two different 429 bodies

The edge IP throttle returns a plain-text body (Too Many Requests! Wait for {n}s) — it is not JSON. The per-account limiter returns a JSON SubmitResponse. Handle both.

Every submit response carries these headers:

X-RateLimit-Limit: 50
X-RateLimit-Remaining: 37
X-RateLimit-Reset-Ms: 1000 # ms UNTIL reset, not an epoch timestamp
Retry-After: 1 # whole seconds, only on 429 / 503

X-RateLimit-Reset-Ms is a countdown in milliseconds, not an absolute time. A value of 1000 means the window resets in one second.

Market-maker / BSL limits

The MM/BSL order-entry lanes run a sliding-window limiter with per-scope budgets (defaults; read your live values from GET /api/v1/mm/limits / GET /api/v1/bsl/limits):

ScopeDefault budget (per 1000 ms window)
per account~2,000–4,000 actions
per API key~16,000 actions
per IP~16,000 actions
per market~64,000 actions
per shard~128,000 actions

Two properties that make quote churn cheap:

  • Cancels cost 0 against the throughput budget (separate cancel scope).
  • Quote-replace charges only net-new legs — replacing a resting quote isn't double-counted.

A 429 on this lane returns a JSON body with a rateLimit budget view:

{ "ok": false, "error": "mm rate limit exceeded scope=account max_actions_per_window=2000",
"rateLimit": { "account": "0x1111…", "windowMs": 1000, "maxActionsPerWindow": 2000,
"usedActions": 2000, "remainingActions": 0, "resetInMs": 1000 } }

WebSocket limits

SubjectLimit
Connections per IP32
Connections per account8 (4 per agent)
Subscriptions per connection32
Markets per connection16
Control messages (subscribe/unsubscribe) per minute60

A stale resume cursor returns resume_required (private) / RESYNC_REQUIRED (public) rather than a silent gap — see WebSocket → Reconnect.

FIX rate limits

SubjectLimit
Logon attempts per minute5
Order entry messages per secondProvisioned by account/tier
Resend messages per requestCapped by FIX_RESEND_MAX_MESSAGES

BSL order-entry limits

Read the live limits endpoint before starting a quote engine:

GET /api/v1/bsl/limits

The response includes:

  • actions.maxPerBatch
  • actions.lowLatencyMaxActions
  • actions.maxActionTsSkewMs
  • rateLimits.windowMs
  • rateLimits.maxActionsPerWindow
  • rateLimits.maxActionsPerApiKeyWindow
  • rateLimits.maxActionsPerIpWindow
  • ingress.binaryMaxBodyBytes
  • backlog.prebatchedMaxBatches

Per-account throughput can also be restricted by API-agent policy and builder plan. Builder Basic currently maps to 600/min; Builder Pro maps to 3,000/min while active.

Existing clients may still read GET /api/v1/mm/limits; it is a compatibility route for the same BSL limit envelope.

Cross-shard credit admission (separate reject reason)

Distinct from rate limits, an order can also be rejected by the cross-shard credit broker - a per-(account, asset) admission control. This is a separate admission-reject reason, not a throughput limit, so it surfaces as a reject on the action rather than an HTTP 429. The broker is default off and is only active when the engine runs more than one market shard (engine_market_shard_count > 1) and a credit policy is enabled; it is a no-op in the single-shard production default. When enabled it can reject for haircut (MM_CROSS_SHARD_CREDIT_HAIRCUT_PPM), per-account cap (MM_CROSS_SHARD_CREDIT_MAX_ACCOUNT_LOCKED), per-market cap (MM_CROSS_SHARD_CREDIT_MAX_MARKET_LOCKED), or the fail-closed kill-switch (MM_CROSS_SHARD_CREDIT_KILL_SWITCH). The environment variable prefix is legacy; the policy now belongs to the BSL/risk admission model. Rejections are exposed in metrics by reason.

Burst behavior

All surfaces use a token-bucket model with sustained rate plus a small burst allowance.