Documentation · Rate limits

Rate limits

Per-key limits for REST and WebSocket, the usage headers on every response, and what happens when you hit a limit.


The numbers

LimitAll plansType
Sustained rate (REST)2 req/secHard
Burst (REST)Bursts up to 10 requests, then 2/secHard
Daily allowanceGoverned by credits — see Credits belowHard
Concurrent WS connections5 per keyHard
Historical depthEvery event since coverage began · growing weeklyHard
Concurrent in-flight HTTP10Soft

For context: a client polling /pm/v2/newsevery 15 seconds uses about 5,760 calls a day (comfortably inside the quota), and if you're on the WebSocket, you don't need to poll at all. If your use case genuinely needs more, talk to us.

Soft vs hardSoft limits surface a 429 with Retry-After and a warning email at 80% utilisation. Hard limits return 429 immediately and stay in 429 until the window resets. These protective limits apply to every plan — credits, not rate, are what differ by plan.

Headers on every response

Every REST response carries rate-limit state. Your client can back off without parsing the body.

httphttp
HTTP/1.1 200 OK
Content-Type: application/json
X-Vera-Tier:               standard
X-Vera-RPS-Limit:          2
X-Vera-Burst-Limit:        10
X-Vera-Burst-Remaining:    9
X-Request-Id:              a1b2c3d4-e5f6-7890-abcd-ef1234567890

Quota headers (X-Vera-Quota-Remaining-Day, X-Vera-Quota-Remaining-Minute) appear only when your daily quota is close to exhaustion, not on every response.

What 429 looks like

There are two kinds of 429 with different bodies. Check the status code, then the body, to tell them apart.

Protective rate limit — a flat object; type tells you which limit fired. Never billed; just slow down and retry. Retry-After is usually a fraction of a second for burst:

httphttp
HTTP/1.1 429 Too Many Requests
Retry-After: 0.18
Content-Type: application/json

{
  "type": "per_second_burst",
  "limit": "burst capacity exceeded (10 req/s burst, 2 req/s sustained)",
  "message": "Rate limit exceeded: burst capacity exceeded (10 req/s burst, 2 req/s sustained). Please retry after 0.18s.",
  "request_id": "req_8jJw3Fk1pQzN",
  "documentation_url": "https://vera.cryptobriefing.com/docs/rate-limits"
}

type is one of per_second_burst, per_minute, or per_day.

Credit exhaustion— you're out of allowance/wallet. A distinct body nested under detail and keyed by code, carrying the reset time and remaining wallet:

httphttp
HTTP/1.1 429 Too Many Requests
Content-Type: application/json

{
  "detail": {
    "code": "credit_quota_exhausted",
    "message": "Credit allowance exhausted for this period.",
    "resets_at": "2026-07-17T00:00:00+00:00",
    "wallet_balance": 0,
    "pricing_url": "https://vera.cryptobriefing.com/#pricing"
  }
}

Backoff strategy

Always honour Retry-After. If it's missing (it shouldn't be, but just in case), back off with exponential jitter starting at 1 second. The reference clients in Quickstart already implement this pattern.

pythonpython
import time, random, httpx

def get_with_backoff(client: httpx.Client, url: str, params: dict | None = None):
    for attempt in range(5):
        resp = client.get(url, params=params)
        if resp.status_code != 429:
            return resp
        # Retry-After can be fractional (e.g. "0.18" for a burst hit) — parse as
        # float, not int, or fractional values raise ValueError.
        retry_after = float(resp.headers.get("Retry-After", 0))
        if retry_after > 0:
            time.sleep(retry_after)
        else:
            time.sleep((2 ** attempt) + random.random())
    resp.raise_for_status()

Warning emails

We email the subscription owner (and any team members on a future team plan) at these milestones:

  • 80% of your daily credit allowance consumed
  • 100% consumed — further billable calls return 429 credit_quota_exhausted until the UTC-midnight reset
  • 3+ rate-limit responses within 60 seconds (suggests a misconfigured client)

Asking for more

Need higher caps? Email vera@cryptobriefing.com with what you're building. Enterprise plans lift these limits — tell us your use case and we'll quote you against the actual numbers you need.

Credits

Access is metered in credits. One successful (2xx) response on a billable data route costs one credit; requests that fail auth, get rate-limited, error out, or hit a non-billable route cost nothing.

Credits are drawn from your plan's allowance first, then from any prepaid wallet:

  • Free — a daily allowance of real-time calls (shown in your dashboard) that resets at 00:00 UTC and does not roll over.
  • Builder — a one-time pack adds prepaid wallet credits that never expire and cover usage beyond the daily allowance.
  • Pro — a monthly REST-credit allowance for queries and backfill, with WebSocket streaming as the real-time channel.

When your available credits are exhausted, billable requests return 429 credit_quota_exhausted (with the reset time and wallet balance in the body) until the allowance resets or you add credits. Rate limits (burst / RPM) are separate protective limits and are never billed.

Kalshi coverage

Kalshi is on the roadmap for a forthcoming Multi-Venue add-on. We'll share coverage and upgrade details when it ships.