Skip to content

Errors & rate limits

Status Meaning
401 Missing or invalid key.
402 Broker SDK — seat_limit_reached. Provisioning a new user_ref at your seat cap. Deprovision an inactive client to free a seat, or raise the cap. Re-provisioning an existing user never hits this.
403 Key revoked, account suspended, or — on the Broker SDK — the feature isn't enabled for your account ({"error": "feature_not_enabled", "feature": "stats"}).
404 The user_ref was never provisioned. The most common day-one error: every per-user endpoint returns it.
422 Request body or query validation failed. The response names the offending field.
429 Rate limit exceeded — see below.
500 Server error — log it and retry.
503 ML inference is down. Pre-trade still returns a valid rules-only verdict with HTTP 200, so you rarely see this; method tells you which path answered.

Rate limits

The ceiling depends on which credential you are using.

Credential Default Scope
Personal API key (tlyt_) 100 / minute Per caller
Broker subscription key (tlyt_sk_) 3,000 / minute Across your whole book, not per client

A broker account's limit is contracted and can be raised — GET /sdk/v1/account reports effective_rate_limit, which is the number actually enforced, so read that rather than assuming the default.

A pre-trade check is one call per intended order. At 3,000/minute that is 50 orders a second across your entire client base, so normal order flow does not approach it. What does approach it is a retry storm or a polling loop.

There is no Retry-After header

A 429 returns {"detail": "Rate limit exceeded. Try again later."} and no timing header. Use your own backoff schedule — do not read Retry-After, because it is not sent and your fallback value is what will actually be used.

On 429: exponential backoff, starting at 1s, doubling to a 30s cap.

import time, requests

def call_with_backoff(fn, max_attempts=5):
    delay = 1.0
    for attempt in range(max_attempts):
        try:
            return fn()
        except requests.HTTPError as e:
            if e.response.status_code != 429:
                raise
            # No Retry-After is sent, so the schedule is entirely ours.
            time.sleep(delay)
            delay = min(delay * 2, 30.0)
    raise RuntimeError("rate limit not cleared after retries")

Never block an order on us

Pre-trade is decision support, not an order gate. On any error — 429, 500, a timeout — let the order through. Your client's ability to trade must never depend on our availability.