How the limits work
DeepSeek meters requests-per-minute and tokens-per-minute per account, per model. Splitting traffic across multiple API keys doesn't help — the buckets are account-scoped. Chat and reasoner have separate buckets, so a chat spike won't throttle reasoner. The exact limits are published in the console and can change, so monitor them rather than hard-coding them.
Why 429s cluster
Peak load tracks Asia daytime and new-model launches. The first hour after a big release routinely returns 429s on any hosted provider; DeepSeek is no different. Building your client to survive that hour is more valuable than trying to avoid it. The 429 is not a bug; it is a backpressure signal. Your job is to absorb it gracefully.
The retry pattern that works
- Retry only 429, 500, 502, 503, 504 — never 4xx auth or 400 validation.
- Exponential backoff with full jitter: sleep = random(0, min(cap, base × 2^attempt)).
- Cap total attempts at 3–5. Beyond that you're queueing failures for the user.
- Respect Retry-After when the header is present.
Multi-provider fallback
For anything customer-facing, wire a secondary provider hosting the same or an equivalent model — for DeepSeek, that's Fireworks, Together, or a self-hosted deployment. On repeated 429/5xx, flip to the fallback and log the switch. Prefer sticky routing per request rather than fanning out. A fallback is not a free pass; it adds cost and complexity, but it protects user experience.
What not to do
- Don't retry inside a single HTTP request — the connection may already be dead.
- Don't retry SSE mid-stream without deduplication logic.
- Don't create N API keys to 'multiply' quota. It doesn't work and it's a TOS risk.
Observability and alerting
Export rate-limit metrics from your client: 429 rate, retry count, latency p95, and fallback switches. Alert when 429 rate exceeds 5% for more than 5 minutes, or when fallback usage climbs above 20% for an hour. These thresholds catch provider stress before users notice. A simple dashboard in your own infrastructure is enough; if you need managed observability, platforms like Supabase or Base44 can host the backend while your client emits events.
Circuit breakers and load shedding
After a burst of 429s, a circuit breaker stops sending requests for a short cooldown period. This protects the provider from a thundering herd and gives your queue a chance to drain. Pair the breaker with load shedding: drop non-essential requests first, then defer analytics, and only drop user-facing requests as a last resort. The goal is graceful degradation, not perfect uptime.
Budget burn analysis
Rate limits sometimes protect you from runaway spend. A bug that loops over a large dataset can burn a balance in minutes. Set a daily spend alert in the DeepSeek console and cap max_tokens per request in your client. For teams running many workloads, separate API keys per environment so a staging mistake cannot drain production budget.
Testing your retry logic
Use a mock server to simulate 429, 500, and slow responses. Verify that your backoff is non-linear, that Retry-After is respected, and that concurrent failures do not pile up. Unit tests catch the obvious cases; a small load test catches the concurrency bugs. This is the highest-ROI reliability work you can do before going live.
Rate limits across models
Each model family has its own bucket. If your chat workload is throttled, your reasoner workload may still be fine. Plan capacity per model, not per account. R1 consumes more tokens per request, so the tokens-per-minute limit becomes the binding constraint earlier than with chat. If you run both models, size each workload separately.
Queueing strategies
For bursty workloads, add a queue between your app and the API. A queue absorbs spikes and lets you retry with backoff without blocking user requests. Workers pull from the queue at a rate that respects the API limit. This pattern is standard for batch jobs, but it also works for real-time requests if you can tolerate a small delay.
Streaming retry gotchas
When stream=true, the connection is long-lived. A network blip or 5xx after the first token is harder to retry than a failed non-streaming request. Your retry logic must track which tokens have already been delivered and resume or restart accordingly. In many cases it is easier to retry the whole request and let the user see a brief interruption than to implement mid-stream recovery.
Incident response playbook
When 429s spike, the first step is to confirm the scope: is it your account, your region, or the whole provider? Check the status page, your own metrics, and the provider's status feed. Then activate fallback, reduce non-essential traffic, and notify users if latency rises. After the incident, review whether limits, timeouts, or queues need adjustment. Document the playbook so anyone on call can execute it.
Per-account vs. per-key limits
Every API key under a single DeepSeek account shares the same rate-limit and spend buckets. Creating separate keys for production, staging, and experiments is still a good idea for security and cost attribution, but it does not multiply your quota. If you need more capacity, the only paths are a limit increase from DeepSeek, a second account, or a fallback provider. Treat per-key limits as a security convenience, not a scaling mechanism.
Exponential backoff recipe
The retry loop that survives real load is short and boring. In pseudocode: for attempt in 0..maxAttempts: response = call(); if response.ok: return; if not retryable(response.status): raise; delay = min(cap, base * 2 ** attempt); jitter = random(0, delay); sleep(retry_after or jitter); raise TooManyRetries. Use base = 0.5s and cap = 20s for chat, cap = 60s for reasoner. Full jitter (random(0, delay)) beats equal jitter or no jitter in every published study, because it decorrelates retries across concurrent clients. Never sleep a fixed delay; a fleet retrying in lockstep can hold a provider down long after the original spike has passed. When Retry-After is present, treat it as authoritative and skip the computed delay for that attempt.
Handling 429 vs 402 vs 5xx
The three failure modes look similar in a log but need different responses. A 429 is a rate-limit signal: back off and retry. A 402 is a payment error: retrying is pointless until the account is topped up, so alert an operator and stop the workload. A 5xx is a provider fault: retry a small number of times, then switch to the fallback provider. Mixing them into a single generic retry policy is a common bug — it either burns money re-hitting 402s or fails too fast on transient 5xxs. Branch on status code explicitly. Log each class as a separate counter so dashboards show which failure mode is dominant during an incident.
Concurrency ceilings in practice
Rate limits are measured per minute, not per second, so short-lived spikes can slip under the limit while long sustained load will not. In practice, teams run out of TPM before RPM because each request carries a few thousand tokens. Set a client-side semaphore that caps concurrent in-flight requests, tuned by measurement rather than guessed. Start at 8 concurrent for chat and 2 for reasoner, then increase until the 429 rate exceeds 1%. A queue in front of the semaphore absorbs bursts without inflating memory. For workloads that must be smooth, add a token-bucket limiter to shape traffic to a steady RPM well below the published ceiling; you trade peak throughput for near-zero 429s.