Market holidayJapan will be closed on 2026-11-03 (Culture Day), 5 days from now.All announcements
Article

Fixing HTTP 429 on Market Data APIs: Telling Three Rate Limits Apart

A 429 is not one error but three: rate limiting, daily quota exhaustion, and monthly quota exhaustion. We break down each error code, give a troubleshooting checklist, and ship a Python retry helper that distinguishes them.

429 Too Many Requests is one of the most common errors a market data API returns — and one of the most commonly mishandled. The instinct is to wait a moment and retry. But if you have not first worked out which kind of 429 you hit, retrying makes things worse: when the daily quota is exhausted, every retry burns more of the allowance you have already run out of.

A 429 is not one error. It is three.

Market data APIs return at least three distinct 429s, with completely different meanings and remedies:

Error codeMeaningCorrect response
RATE_LIMIT_EXCEEDEDPer-minute rate exceededRetry with exponential backoff
DAILY_LIMIT_EXCEEDEDDaily quota exhaustedStop retrying; switch to streaming or upgrade
MONTHLY_LIMIT_EXCEEDEDMonthly quota exhaustedStop retrying; wait for reset or upgrade

The distinction is defined in the API docs, and the code field in the response body tells you which one you got. So the first step is always to read code, not just the HTTP status.

RATE_LIMIT_EXCEEDED: the only retryable case

This is the one kind where retrying actually helps. It means your burst concurrency or per-minute request count exceeded the plan limit, while total quota remains.

Do it with exponential backoff plus jitter:

  • Wait 1s, then 2s, then 4s — each multiplied by a random factor between 0.5 and 1.0
  • Cap retries (3–5 is reasonable) and raise an alert when you give up
  • Never put backoff in a bare loop, or many requests will retry in lockstep and create a fresh spike

DAILY_LIMIT_EXCEEDED: retrying is pure waste

The daily allowance is gone. Retrying is not just useless — it hides the real problem, which is that your architecture over-consumes quota.

There are three usual causes:

  1. Using REST polling for live data. The most common by far. Request volume scales with symbol count and refresh rate, and blows through any plan tier quickly.
  2. Frequent WebSocket reconnects. The handshake counts toward quota. A flawed reconnect strategy — too-short heartbeat timeouts, or immediate reconnect with no backoff — drains quota continuously.
  3. No caching. When several modules each pull the same symbols within the same second, quota is consumed once per call.

The fix is architectural, not a retry tuning exercise: live data over WebSocket, REST for queries and backfill.

MONTHLY_LIMIT_EXCEEDED: the month is over

Running out of monthly quota usually signals a capacity planning gap. Three options: upgrade, wait for the monthly reset, or drop non-critical requests to a lower frequency.

Alert at 70% and 90% of quota usage so you never discover this at 100%.

Troubleshooting checklist

  1. Read code in the response body and classify the 429.
  2. Keep X-Request-Id. This trace ID ships in the response headers; quoting it to support dramatically speeds up diagnosis.
  3. Attribute your request sources. Break daily quota consumption down by module and find the one growing abnormally.
  4. Hunt for pollers. Search the codebase for scheduled calls to market data endpoints.
  5. Audit reconnect logic. Confirm WebSocket clients do not reconnect immediately without backoff.
  6. Add a cache layer. Request each symbol at most once per time window.
  7. Estimate first with the quota calculator. Size the real requirement before rewriting code.

Python: backoff that distinguishes error codes

Python
import random, time
import httpx

API_KEY = "your_api_key"
BASE = "https://api.tiqex.io/api/v1/market"

# Only rate limiting is worth retrying; quota exhaustion must fail fast and alert
RETRYABLE = {"RATE_LIMIT_EXCEEDED"}

def get_quote(codes: str, max_retries: int = 4):
    for attempt in range(max_retries):
        resp = httpx.get(
            f"{BASE}/quote",
            params={"codes": codes},          # up to 50 codes per call
            headers={"X-API-Key": API_KEY},
            timeout=10,
        )
        if resp.status_code == 200:
            return resp.json()

        body = resp.json() if resp.content else {}
        code = body.get("code")
        request_id = resp.headers.get("X-Request-Id")

        if resp.status_code == 429 and code in RETRYABLE:
            backoff = (2 ** attempt) * (0.5 + random.random())
            print(f"[{code}] rate limited, retrying in {backoff:.1f}s (attempt {attempt + 1})")
            time.sleep(backoff)
            continue

        # Quota exhausted or any other error: no retry, raise with the trace ID
        raise RuntimeError(f"request failed code={code} requestId={request_id}")

    raise RuntimeError("retries exhausted")

print(get_quote("BTCUSDT,ETHUSDT,AAPL"))

The important detail is the RETRYABLE allowlist. Only RATE_LIMIT_EXCEEDED gets retried; the other two fail immediately. That is what stops you from burning remaining quota on doomed attempts.

Working backwards from symptoms: three real scenarios

Mapping the three 429 types onto observed behaviour speeds up diagnosis considerably.

Scenario one: errors start around 09:00 and ease off in the afternoon. Classic daily quota exhaustion. Traffic rises around the open, polling and query requests concentrate, and the allowance drains within hours. The usual root cause is missing caching, or a scheduled job that bulk-pulls every symbol at the open.

Scenario two: normally fine, but a burst of 429s whenever the market moves hard. Almost always rate limiting. Volatile markets deliver more ticks, which trigger more downstream REST lookups, which create a feedback loop. The fix is queueing and throttling those downstream queries, not just increasing backoff.

Scenario three: errors begin around the 20th of each month. A monthly capacity planning problem — a quota sized for average load gets mostly consumed by one backfill or load test early in the month. Bill bulk backfills separately from production traffic.

Catching it early: two quota monitoring thresholds

Do not wait for a 429 to start reacting. Quota monitoring needs exactly two thresholds:

  • Alert at 70% — there is still room to reduce non-critical request frequency.
  • Alert at 90% — enter incident mode: pause bulk backfills immediately and check for runaway pollers.

No elaborate system is required. Record every successful request client-side, aggregate daily, and compare against the plan allowance with a scheduled job. The critical detail is attributing usage per module; an aggregate number alone cannot tell you where the growth came from.

FAQ

Does a 429 consume my quota?

Rejected requests generally do not count against business quota, though rate-limited requests still increment rate counters. The real problem is quota exhaustion — by then your normal requests are all failing and retrying accomplishes nothing.

Why do I still get 429s after upgrading?

If code is RATE_LIMIT_EXCEEDED, the bottleneck is per-minute rate, not volume — check your concurrency. If it is DAILY_LIMIT_EXCEEDED, quota is draining too fast and upgrading only delays the problem; the root cause is usually REST polling, which belongs on WebSocket.

How do I tell a 429 apart from a 403?

A 429 means you exceeded usage; a 403 means you lack entitlement. 403s typically come from a market not being enabled on your plan, a key scope restriction, or an IP policy — none of which resolve by waiting. If the error clears on its own after a pause, it was almost certainly a 429.

How do I find out where my quota goes?

Daily quota and per-minute rate (RPM) rules are documented in the errors and rate limits topic. Instrument your client per module so every request is attributed — that is how you localize the outlier fast.

Do WebSocket reconnects consume quota?

Yes. The handshake counts toward daily quota and RPM; ticks pushed after the handshake are not metered. So reconnect logic needs backoff — do not use frequent reconnects as a keep-alive.

Next steps

First determine which 429 you are hitting. If the answer is daily quota exhaustion, the fix is not retry tuning — it is moving live data to WebSocket. The complete error code and rate limit reference is in the errors and rate limits docs. Estimate your requirement with the quota calculator before rewriting anything, and revisit plan tiers only if your real demand genuinely exceeds them. Correct header usage is covered in the REST authentication guide.