DeepSeek API Rate Limits: Concurrency and How to Fix 429

Understand DeepSeek’s account-level concurrency limits, diagnose HTTP 429, and manage requests with queues, bounded retries and practical Python or Node.js examples.

DeepSeek API rate limits are based on concurrent requests: 2,500 for Flash and 500 for Pro, per account and model. A request uses a slot until its response finishes. Sending more than the applicable allowance produces HTTP 429, so the useful number to control is how many requests remain active across your account.

If you are dealing with “Too Many Requests,” pause the burst, let existing work finish, and put new requests behind a concurrency limit. Repeatedly resending a rejected request can make the queue worse. Below are the current official limits, the checks that explain unexpected 429s, and Python and Node.js examples for managing them.

What are the current DeepSeek API limits?

DeepSeek’s Rate Limit & Isolation documentation, checked on October 5, 2026, lists these standard limits. They apply to accounts using the direct DeepSeek API; an approved capacity expansion can change an account’s total allowance.

ModelAPI model nameConcurrent requests per account
DeepSeek-V4.1-Flashdeepseek-flash2,500
DeepSeek-V4-Pro-0813deepseek-v4-pro500

DeepSeek does not publish a standard requests-per-minute (RPM) or tokens-per-minute (TPM) allowance in these documents. Do not turn the figures above into per-minute or daily quotas. They describe unfinished requests, not how many messages you may send during a calendar period.

The official model table maps those API names to the current model versions. It still accepts deepseek-v4-flash and deepseek-v4-flash-vision-exp, but their original models have been retired and requests are served by the current Flash model. Do not budget a separate Vision-Exp allocation alongside Flash.

Keep Flash and Pro traffic in separate concurrency budgets. Creating additional API keys within one account does not give each key a fresh allowance: DeepSeek counts the account’s requests together for the relevant model.

How concurrency differs from RPM and TPM

RPM counts requests started during a minute. TPM measures tokens processed during a minute. Concurrency counts requests that have started but have not finished. A slow response can therefore matter more to your available capacity than a burst of short responses that finish quickly.

According to DeepSeek’s slot-counting rule, a request counts from the time it is sent until the model response is complete. Waiting for inference and receiving a streamed answer both keep it active. The first token is progress, not a released slot.

For an illustrative calculation, suppose your application allows 10 simultaneous requests and each completes in an average of 20 seconds. Keeping those slots continuously occupied would produce roughly 10 × 60 ÷ 20 = 30 completed requests per minute. This is planning arithmetic, not a measured DeepSeek speed or a provider quota. Longer responses, variable latency and time spent retrying will change the result.

This also explains why there is no fixed “wait until the next minute” reset for the published concurrency limit. Capacity becomes available as active requests finish. A gateway’s separate per-minute rule, if you use one, can have a different reset policy.

How to fix DeepSeek 429 “Too Many Requests”

DeepSeek calls HTTP 429 “Rate Limit Reached” in its error reference. Start by identifying where the response came from, then reduce the pressure on that service.

  1. Confirm the provider and capture the error. Check your configured base URL, model, HTTP status and error body. Calls to api.deepseek.com use DeepSeek’s direct service; an extension or gateway can apply other limits. Keep a timestamp and any request identifier available for diagnosis. Leave API keys and private prompt content out of shared logs.
  2. Stop immediate retries and let active work drain. Pause a batch or reduce its workers. In an agent or coding extension, check background tasks and parallel agents as well as the request visible in your current window. One user action can trigger several model calls.
  3. Enforce an account-wide concurrency budget. Queue requests before sending them. Include every worker, service and deployment using that account and model. A limit of 100 in each of six processes can permit 600 simultaneous requests; a local setting is not an account-wide cap.
  4. Retry rejected requests with increasing delays and jitter. Jitter means adding randomness so workers do not retry together. Use a finite attempt budget and a job deadline. If the responding service supplies a valid Retry-After header, respect it; do not assume DeepSeek will always return one or a quota-reset timestamp.
  5. Reduce unnecessary work, then review capacity. Match output length and thinking mode to the task. Shorter actual responses can free slots sooner, but cutting useful answers or disabling reasoning indiscriminately is not a capacity strategy. Request an increase when an efficient workload still needs more concurrent work.

For a production queue, set a maximum backlog and expire jobs that are no longer useful. When arrival traffic stays above completion capacity, a larger queue only postpones the failure. Return a clear busy response, defer suitable background jobs, or reduce intake until the backlog recovers.

Why can 429 appear when you are sending only a few requests?

Your screen may show only part of the account’s activity. Check other API keys, scheduled jobs, deployment replicas, long-running streams and automatic retries. If both a client library and your application retry the same failure, their combined attempts can be much higher than expected. Choose one layer to own the retry policy, or deliberately coordinate both.

Also check whether the request is going through a provider with its own restrictions, or whether an expanded account has reached a per-user limit. If an occasional direct request still gets 429 after other traffic has stopped, save the error details and contact the provider. A low local request count alone does not establish the cause.

OpenRouter and other gateways

An error such as error from provider (deepseek): too many requests suggests a provider rejection, but inspect the response details rather than diagnosing from the wording alone. DeepSeek’s published direct-account limits are not a guaranteed personal allocation on a gateway route.

OpenRouter documents its own error format, provider-error metadata and possible Retry-After headers. An error that occurs after the response has begun can also appear inside the response body or stream while HTTP status remains 200. Use the gateway’s documented error fields and limits for that route; do not apply a direct-API retry example unchanged.

Make sure the problem is actually rate limiting

A direct DeepSeek 401 points to authentication, 402 to insufficient balance, and 400 or 422 to a request or parameter problem. Fix those causes before retrying. 500 and 503 concern the service or overload and need a separate recovery policy. The DeepSeek API error guide covers the diagnosis for each code; the balance guide explains how to check funds when the error is 402.

Limit concurrency and retry 429s in your application

The examples below show two small clients for the direct Chat Completions API. Each allows up to four concurrent requests and up to four attempts per job, including the initial attempt. Those are demonstration settings, not DeepSeek quotas or a recommended production allocation. Both use non-streaming responses, a 120-second deadline per network attempt, and a small output budget for the demonstration prompts.

They retry only an explicit HTTP 429. Authentication failures, insufficient balance, timeouts and other failures are surfaced to the caller. Each example also honors a valid Retry-After delay in seconds or HTTP-date form, as defined by HTTP semantics. If that delay exceeds 30 seconds, the example stops and asks you to reschedule instead of retrying earlier than instructed.

Set DEEPSEEK_API_KEY in your environment before running either version. If you have not connected an application yet, the DeepSeek API setup guide explains the initial configuration. Running these examples sends billable API requests; the snippets are implementation examples, not reported live test results.

Python: hold a semaphore until the response is complete

Install HTTPX with python -m pip install httpx, save this as a Python file, and run it in the environment containing your key. The semaphore controls active attempts, and retry waits happen outside it so another job can use a free slot. HTTPX’s async client is shared across the requests.

import asyncio
import os
import random
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime

import httpx

API_KEY = os.environ["DEEPSEEK_API_KEY"]
# Application settings for this small example, not DeepSeek quotas.
CONCURRENCY, ATTEMPTS, TIMEOUT = 4, 4, 120


def retry_delay(header, attempt):
    delay = random.uniform(1, min(30, 2 ** (attempt + 1)))
    if header:
        try:
            if header.strip().isdigit():
                seconds = int(header)
            else:
                when = parsedate_to_datetime(header)
                if when.tzinfo is None:
                    when = when.replace(tzinfo=timezone.utc)
                seconds = (when - datetime.now(timezone.utc)).total_seconds()
            delay = max(delay, seconds)
        except (ValueError, TypeError, OverflowError):
            pass
    if delay > 30:
        raise RuntimeError("Long Retry-After: reschedule this job later")
    return delay


async def complete(client, slots, prompt):
    for attempt in range(ATTEMPTS):
        async with slots:
            response = await asyncio.wait_for(client.post(
                "/chat/completions",
                json={"model": "deepseek-flash",
                      "messages": [{"role": "user", "content": prompt}],
                      "thinking": {"type": "disabled"},
                      "max_tokens": 256, "stream": False},
            ), timeout=TIMEOUT)
            # HTTPX has read the complete body before we release the slot.
        if response.status_code != 429 or attempt == ATTEMPTS - 1:
            response.raise_for_status()
            return response.json()
        await asyncio.sleep(retry_delay(
            response.headers.get("Retry-After"), attempt))


async def main():
    slots = asyncio.Semaphore(CONCURRENCY)
    prompts = ["Define concurrency briefly.", "Explain a request queue."]
    async with httpx.AsyncClient(
        base_url="https://api.deepseek.com",
        headers={"Authorization": f"Bearer {API_KEY}"}, timeout=None,
        limits=httpx.Limits(max_connections=CONCURRENCY),
    ) as client:
        results = await asyncio.gather(
            *(complete(client, slots, prompt) for prompt in prompts),
            return_exceptions=True,
        )
    for result in results:
        if isinstance(result, Exception):
            print(f"Request failed: {result}")
        else:
            print(result["choices"][0]["message"]["content"])


if __name__ == "__main__":
    asyncio.run(main())

Node.js: process a batch with a fixed number of workers

Save this version as an .mjs file and use a supported Node.js release with built-in fetch and AbortSignal.timeout. Each worker handles one job at a time, including its retry waits, and reads the complete response before moving on. This keeps the example small without starting every job as a simultaneous network request.

import { setTimeout as sleep } from "node:timers/promises";

const API_KEY = process.env.DEEPSEEK_API_KEY;
if (!API_KEY) throw new Error("Set DEEPSEEK_API_KEY first");
// Application settings for this small example, not DeepSeek quotas.
const CONCURRENCY = 4, ATTEMPTS = 4, TIMEOUT_MS = 120_000;

function retryDelay(header, attempt) {
  let delay = 1000 + Math.random() * (Math.min(30, 2 ** (attempt + 1)) - 1) * 1000;
  if (header) {
    const value = header.trim();
    const wait = /^\d+$/.test(value)
      ? Number(value) * 1000 : Date.parse(value) - Date.now();
    if (!Number.isNaN(wait)) delay = Math.max(delay, wait);
  }
  if (delay > 30_000) throw new Error("Long Retry-After: reschedule this job later");
  return delay;
}

async function complete(prompt) {
  for (let attempt = 0; attempt < ATTEMPTS; attempt++) {
    const response = await fetch("https://api.deepseek.com/chat/completions", {
      method: "POST",
      headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify({
        model: "deepseek-flash",
        messages: [{ role: "user", content: prompt }],
        thinking: { type: "disabled" }, max_tokens: 256, stream: false,
      }),
      signal: AbortSignal.timeout(TIMEOUT_MS),
    });
    const text = await response.text(); // Finish reading before the next request.
    if (response.ok) return JSON.parse(text);
    if (response.status !== 429 || attempt === ATTEMPTS - 1) {
      throw new Error(`DeepSeek HTTP ${response.status}`);
    }
    await sleep(retryDelay(response.headers.get("Retry-After"), attempt));
  }
}

const prompts = ["Define concurrency briefly.", "Explain a request queue."];
let next = 0;
const results = new Array(prompts.length);
async function worker() {
  while (next < prompts.length) {
    const index = next++;
    try { results[index] = await complete(prompts[index]); }
    catch (error) { results[index] = error; }
  }
}
await Promise.all(Array.from({ length: Math.min(CONCURRENCY, prompts.length) }, worker));
for (const result of results) {
  if (result instanceof Error) console.error(result.message);
  else console.log(result.choices[0].message.content);
}

The request reference defines max_tokens and the message format; the thinking-mode guide documents "thinking": {"type": "disabled"}. Here those settings suit short definition prompts. Give a real task enough output space, and keep thinking enabled when its reasoning needs justify it.

These examples limit one process. With several replicas, use a shared queue or limiter, or divide a fixed total budget among them. The Python version creates one pending task per prompt, so use a bounded producer-and-worker queue for a large or continuous input stream. The Node version bounds active workers but still holds its small input batch and results in memory.

The attempt timeout does not cover local queue time, retry waits or the whole job. Add an overall deadline in production. A client timeout also does not prove that DeepSeek never received the request or stopped processing it: avoid immediately launching a duplicate, particularly if an agent may repeat external actions. Keep request duration, queue age, active attempts and 429 counts visible so you can see whether a change actually reduces congestion.

Keep-alive messages, streaming and the 10-minute cutoff

A connection that has not produced an answer yet is not necessarily a failed request. DeepSeek’s documented keep-alive behavior allows the service to send blank lines for non-streaming requests or SSE comments such as : keep-alive for streaming requests while you wait.

For a non-streaming call, read the completed body before decoding the JSON. For streaming, use an SSE parser that ignores comment lines and processes actual data events. Do not treat a keep-alive comment as model output or try to decode every received line as JSON.

DeepSeek closes the connection if inference has not started after 10 minutes. That is a pre-inference waiting cutoff. It is not a rate-limit reset period, and it does not say that every generated answer must finish within 10 minutes.

Your client, hosting platform or reverse proxy may end the connection earlier. Align their timeouts with the experience you intend to provide. Streaming can show progress to the reader, but it does not increase the concurrency allowance: hold your application’s slot until the stream finishes or closes, rather than releasing it when headers or the first token arrive.

How account limits and user_id work together

The optional user_id identifies an end user of your application for content-safety handling, KV-cache isolation and scheduling. It is not an API key and does not create a separate DeepSeek account. The official isolation rules distinguish two cases:

  • Standard account: all user_id values are combined when calculating the account’s model-specific concurrency. Adding IDs does not multiply capacity.
  • Account with expanded concurrency: the approved account total applies alongside a per-user limit of 2,500 for Flash or 500 for Pro. A single user can reach their limit while the account still has spare capacity. An empty ID is treated as a special user ID, so leaving every user unidentified can matter here.

Use a stable opaque identifier such as user_8f2a. DeepSeek permits letters, digits, hyphens and underscores, with a maximum of 512 characters, and says not to include private user information. Assign IDs consistently to your actual users rather than rotating them to evade a limit.

Request methodWhere the identifier goes
Direct Chat Completions HTTP requestTop-level "user_id": "user_8f2a" in the JSON body
OpenAI SDK calling DeepSeekextra_body={"user_id": "user_8f2a"}
DeepSeek’s Anthropic-compatible API"metadata": {"user_id": "user_8f2a"}

Those locations come from DeepSeek’s parameter instructions. For the raw HTTP examples above, add the top-level field beside model and messages. Keep messages as an array of message objects.

How to request a higher DeepSeek rate limit

Use the capacity-expansion link on the official Rate Limit & Isolation page. DeepSeek says it considers actual business needs and does not charge an additional fee for capacity expansion. Approval and the resulting allowance should be confirmed before you raise your application’s budget.

To explain your need, prepare the model you use, your current and expected peak active requests, typical response duration, and what the workload does. Include recurring 429s and the queue controls already in place. These are useful planning details, not a claim that DeepSeek requires a particular application format or promises an approval time.

More concurrency permits more work at once; it does not make the tokens free or guarantee faster individual responses. Budget for normal usage separately using the DeepSeek pricing guide. Once expansion is approved, check both your account allowance and the per-user rules before increasing traffic gradually.

Frequently asked questions

Does buying credits increase the API rate limit?

DeepSeek documents credit balance and capacity expansion separately. Adding funds addresses an insufficient-balance error; the published route to higher concurrency is a capacity-expansion request. Do not assume that a larger balance has changed your allowance.

Can I use another API key to get around 429?

Another key on the same account shares its model-specific limit. Separate keys can help organize applications, but the combined workload still needs a shared budget. Check other services using the account before deciding that the key itself is faulty.

Is a context-length or output-token error a TPM limit?

No. Context length and max_tokens constrain an individual request or answer. They do not describe a token allowance per minute. Read the returned error and the model’s request limits before reducing concurrency for a problem caused by an oversized prompt or invalid parameter.

Do these limits apply to the DeepSeek chat website or app?

No. The figures here are API concurrency limits, not daily chat-message allowances. If you are using the consumer chat service and seeing a busy message or a failed conversation, use the DeepSeek troubleshooting guide. First confirm whether the problem is with the official service or a third-party interface.

Get the queue under control first

If 429 is interrupting your application, start with the provider you actually call and the total active work behind that account. A modest concurrency cap, a queue with an expiry policy, and bounded retries give you a clearer picture than repeated manual resends. Keep the useful parts of each answer; reduce unnecessary work before sacrificing the task’s quality.

When those controls are in place, use the remaining demand to decide whether you need more capacity. If rejections persist with little account-wide activity, take the timestamp, model and error details to the provider instead of guessing at a hidden daily quota. That gives you a practical next step without confusing a configuration problem, a gateway restriction and DeepSeek’s published concurrency limit.

Privacy and cookie settings