DeepSeek user_id Isolation: Multi-Tenant Scheduling, Cache, and Safety

DeepSeek user_id isolation is the provider-documented use of the user_id parameter to distinguish application users served through the same API account. DeepSeek assigns the field three purposes: content-safety isolation, KV-cache isolation, and scheduling isolation. It is useful production metadata, but it is not authentication, authorization, conversation memory, or a substitute for tenant-aware application security.

We tested the field with 80 live requests to the official DeepSeek Chat Completions endpoint on July 25, 2026. The benchmark used two synthetic users, repeated prompt prefixes, omitted-ID controls, a documentation-aligned stateless response control, and a low-load concurrency smoke test. It also challenged the documented user_id validation rules.

The strongest result came from the cache experiment. In all three independent groups, Tenant A warmed a synthetic prefix and then received a 99.59% cache-hit ratio or better. Tenant B’s first request used the same body except for user_id and received zero cache-hit tokens. After Tenant B warmed its own lane, its hit ratio rose to 99.67%. All three groups were consistent with DeepSeek’s documented KV-cache isolation.

The validation result was less tidy. DeepSeek documents an allowed character set of letters, numbers, hyphens, and underscores. The live endpoint nevertheless accepted a space, a dot, and an exclamation mark in three test IDs. It did reject a 513-character ID with HTTP 400 and accepted a 512-character ID. The safe production choice remains to follow the published contract rather than depend on temporary backend leniency.

Scope: this is an independent test of the official hosted API using deepseek-v4-flash, not a test of DeepSeek Chat, a third-party gateway, self-hosted weights, or deepseek-v4-pro. No customer data, private user identifiers, or real tenant content was used. Chat-Deep.ai is independent and is not affiliated with DeepSeek.

Quick answer: what does DeepSeek user_id do?

Pass a stable, pseudonymous user_id with each request when multiple application users share one DeepSeek account. With raw OpenAI-format HTTP, it is a top-level request field. With the Python OpenAI SDK, DeepSeek says to send it through extra_body. With the Anthropic-compatible endpoint, it belongs in metadata.user_id.

user_id can help withuser_id does not provide
Separating DeepSeek context-cache lanesUser authentication
Distinguishing application users for content-safety handlingAuthorization to your database, tools, or files
Scheduling isolation under DeepSeek’s documented rulesA separate account quota for every regular user
Keeping stable application users in distinct documented cache lanesConversation history or server-side memory
Giving the provider a stable pseudonymous identityA place to send email addresses, names, or other private data

The practical pattern is:

  1. Authenticate the person in your application.
  2. Derive the tenant and user context on your server.
  3. Convert that context into a stable pseudonymous identifier.
  4. Pass the result as user_id.
  5. Enforce authorization, data isolation, quotas, and audit rules in your own systems.

For the surrounding API setup, see the DeepSeek API guide. For broader concurrency and 429 handling, use the dedicated DeepSeek API rate-limits guide.

Live benchmark results at a glance

Sanitized live DeepSeek user_id benchmark evidence showing 80 API requests, token usage, cost, and verified outcomes
Sanitized evidence from the live run: 80 calls, 79 HTTP 200 responses, one expected 400 response, and a calculated cost of $0.007452.

The run sent 80 calls in about 100 seconds. Seventy-nine returned HTTP 200, and the expected 513-character rejection returned HTTP 400. Seventy-seven of 80 calls passed every predefined expectation. The three expectation failures were not server failures: they were the space, dot, and exclamation-mark IDs that the test expected the API to reject but the endpoint accepted.

SuiteCallsLive resultCareful interpretation
user_id validation98 HTTP 200; 1 HTTP 400; 6/9 expectations passedLength enforcement matched the docs; three out-of-contract characters were accepted
KV-cache isolation2121/21 passedAll three A/B groups supported the documented isolation behavior
Omitted-ID cache control66/6 passedResults were consistent with one shared fallback cache lane when no identity was sent
Stateless response control1212/12 output contracts passedConstrained responses matched the documented stateless design; this was not an independent hidden-memory test
Scheduling smoke test3232/32 passedRequest/response association survived eight-way client concurrency; fairness and limits were not tested
Full benchmark8079 HTTP 200, 1 expected HTTP 400, 77/80 expectation passesEvidence for this endpoint, model, date, and small synthetic sample

All 80 requests completed on their first attempt. Every successful call ended with finish_reason: "stop". The observed backend fingerprint was fp_8b330d02d0_prod0820_fp8_kvcache_20260402.

The API reported:

  • 153,256 prompt tokens
  • 104,704 cache-hit prompt tokens
  • 48,552 cache-miss prompt tokens
  • 1,292 completion tokens
  • 0 reasoning tokens
  • $0.007452 calculated cost at the prices checked on the test date
  • 274 ms minimum, 303 ms median, 513 ms p95, and 587 ms maximum response-header availability

The overall prompt cache-hit share was 68.32%. At the then-current Flash rates, the same prompt and output token counts would have cost approximately $0.021818 if every prompt token had been billed as a cache miss. The observed cache counters reduced that calculation by about 65.8%. That is a benchmark cost comparison, not a future price guarantee.

The calculation used the official rates checked on July 25, 2026: $0.0028 per million cache-hit input tokens, $0.14 per million cache-miss input tokens, and $0.28 per million output tokens.

SuitePrompt tokensCache-hit tokensCompletion tokensCalculated cost
Validation424048$0.00007280
KV-cache isolation113,29880,640458$0.00492615
Omitted-ID control36,21824,064102$0.00179750
Statelessness9160152$0.00017080
Scheduling smoke2,4000532$0.00048496
Total153,256104,7041,292$0.00745221

The documented DeepSeek user_id contract

Three documented DeepSeek user_id functions for content safety, KV cache isolation, and scheduling isolation
DeepSeek documents three provider-side functions for user_id; none replaces application authentication or authorization.

The official Rate Limit & Isolation documentation says user_id can distinguish users on the developer’s side under one account. It assigns the field three specific functions.

Content-safety isolation

DeepSeek says it uses the identifier to distinguish application-side user identities for content-safety handling. The normal Chat Completions response does not expose a user safety profile, score, enforcement history, or moderation dashboard field. Therefore, this aspect was not independently verified by the benchmark.

Do not translate “content-safety isolation” into a claim that user_id moderates your application for you. You still need an appropriate use policy, input and output controls, incident handling, and deterministic authorization around tools and data.

KV-cache isolation

DeepSeek says user_id isolates KVCache for privacy management. Its separate Context Caching guide explains that caching is enabled by default and operates on reusable prompt prefixes. Responses expose prompt_cache_hit_tokens and prompt_cache_miss_tokens, which made this behavior observable without inspecting provider infrastructure.

Context caching remains best effort. DeepSeek says cache construction takes seconds, a hit is not guaranteed, and unused cache data is normally cleared within hours to days. A single miss cannot prove isolation. The meaningful comparison is a warm same-user result against a first identical-prefix request in a different user’s lane.

Read the full DeepSeek Context Caching guide for prefix-unit rules and cost optimization. This article focuses on the identity boundary around that cache.

Scheduling isolation

DeepSeek also uses user_id for scheduling isolation, but the wording matters. For regular API accounts, all user_id values are still combined when calculating account concurrency. The documented account limits are 500 concurrent requests for deepseek-v4-pro and 2,500 for deepseek-v4-flash.

For accounts that receive increased concurrency quotas, DeepSeek says it applies both a total account limit and per-user_id limits. The published per-ID figures are 500 for Pro and 2,500 for Flash. An empty ID is treated as a special ID for this purpose.

This does not document weighted fairness, reserved capacity, priority classes, RPM, TPM, or a guaranteed minimum for every tenant. Multiple API keys under one account also share the account-level concurrency calculation. Build your own per-tenant queue and quotas instead of assuming the provider will implement your product’s fairness policy.

How the 80-call test worked

The benchmark called:

POST https://api.deepseek.com/chat/completions

Every model request used:

{
  "model": "deepseek-v4-flash",
  "thinking": {
    "type": "disabled"
  },
  "temperature": 0,
  "top_p": 1,
  "stream": false
}

Output limits varied from 50 to 80 tokens according to the suite. JSON mode was used for mechanically checkable responses. Temperature zero reduced an avoidable source of variation, but it is not a documented deterministic seed.

Tenant A and Tenant B were synthetic. The result files store labels, lengths, and SHA-256 hashes rather than relying on real user details. Synthetic English reference records provided the long cache prefixes. A six-second pause followed the second warm-up call before each main cache probe.

The checks were defined around HTTP status, token-accounting consistency, finish_reason, valid JSON, and exact expected fields. Cache tests were not retried, so a retry could not manufacture a cleaner cache sequence. The temporary API credential was never written to the benchmark outputs.

Live user_id validation: documentation versus observed behavior

Live DeepSeek user_id validation matrix comparing documented format rules with observed API responses
The API enforced the 512-character maximum but accepted three values outside the documented character set in this dated run.

DeepSeek documents a maximum length of 512 characters and the character set [a-zA-Z0-9\-_]+. It also says not to include private user information. We sent nine controlled cases directly in the JSON request body.

Test valueDocumentation expectationLive statusBenchmark result
Field omittedAccepted200Passed
Empty stringObservational case200Accepted in this run
Letters, numbers, and hyphenAccepted200Passed
UnderscoreAccepted200Passed
Exactly 512 charactersAccepted200Passed
Contains a spaceShould fall outside documented set200Unexpectedly accepted
Contains a dotShould fall outside documented set200Unexpectedly accepted
Contains !Should fall outside documented set200Unexpectedly accepted
513 charactersRejected400Passed

The 513-character response identified user_id as too long and stated the 512-character maximum. The three punctuation cases returned normal model completions instead of validation errors.

This is precisely why an evidence-first guide must distinguish the documented contract from one observed implementation. The correct production rule is still to generate IDs that contain only ASCII letters, digits, _, and -, and keep them at 512 characters or fewer. A lenient server can become strict without notice. A different endpoint, SDK layer, regional deployment, or future release can also enforce the published pattern.

Do not use the unexpected acceptance as permission to put an email address into the field. DeepSeek explicitly says not to include private information in user_id. Sections 3.3 and 5.5 of the DeepSeek Open Platform Terms also place downstream end-user disclosure and personal-information obligations on the developer: disclose the applicable processing rules and obtain consent or another lawful basis where required. The terms say DeepSeek’s own privacy policy does not cover the personal-information rules for end users of a developer’s downstream application.

How to send user_id correctly

DeepSeek user_id placement in raw OpenAI format, the Python OpenAI SDK, and the Anthropic-compatible API
Field placement changes by protocol: top-level JSON, OpenAI SDK extra_body, or Anthropic metadata.user_id.

Raw HTTP or cURL

With the OpenAI-format endpoint, put user_id at the top level of the request body. Keep messages as an array.

curl https://api.deepseek.com/chat/completions \
  -H "Authorization: Bearer ${DEEPSEEK_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-v4-flash",
    "messages": [
      {
        "role": "system",
        "content": "Return a concise support-ticket summary."
      },
      {
        "role": "user",
        "content": "The synthetic test customer cannot reset a password."
      }
    ],
    "user_id": "tenant_7f93-user_214c",
    "thinking": {"type": "disabled"},
    "max_tokens": 120
  }'

One official isolation-page example currently displays messages as a single object. The Chat Completions schema requires an array, so the example above uses the schema-correct form.

Python with the OpenAI SDK

DeepSeek’s documentation says to place the nonstandard field in extra_body when using the OpenAI Python SDK.

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["DEEPSEEK_API_KEY"],
    base_url="https://api.deepseek.com",
)

response = client.chat.completions.create(
    model="deepseek-v4-flash",
    messages=[
        {"role": "system", "content": "Return a concise support-ticket summary."},
        {"role": "user", "content": "Synthetic ticket TKT-204 needs review."},
    ],
    max_tokens=120,
    extra_body={
        "user_id": "tenant_7f93-user_214c",
        "thinking": {"type": "disabled"},
    },
)

usage = response.usage.model_dump()
print(response.choices[0].message.content)
print("cache hit:", usage.get("prompt_cache_hit_tokens", 0))
print("cache miss:", usage.get("prompt_cache_miss_tokens", 0))

Do not assume the OpenAI user parameter is the same control. DeepSeek documents user_id, and the Python example specifically uses extra_body. If a framework only exposes user, verify the raw outgoing JSON before claiming that tenant isolation is active.

Anthropic-compatible requests

On DeepSeek’s Anthropic-compatible route, the documented placement is metadata.user_id.

import os
from anthropic import Anthropic

client = Anthropic(
    api_key=os.environ["DEEPSEEK_API_KEY"],
    base_url="https://api.deepseek.com/anthropic",
)

message = client.messages.create(
    model="deepseek-v4-flash",
    system="Return a concise support-ticket summary.",
    messages=[
        {"role": "user", "content": "Synthetic ticket TKT-204 needs review."}
    ],
    metadata={"user_id": "tenant_7f93-user_214c"},
    max_tokens=120,
)

print(message.content)

The official Anthropic API compatibility guide says metadata.user_id is supported while other metadata fields are ignored.

Live cache-isolation test with two synthetic users

Live two-tenant DeepSeek user_id cache isolation results with same-tenant hits and cross-tenant misses
All three groups showed a warm Tenant A lane, zero hits for Tenant B’s first identical probe, then a warm Tenant B lane.

Each of the three cache groups used a unique synthetic reference containing 160 generated records. The repeated prefix was approximately 5,376 cacheable tokens. The probe bodies for Tenant A and Tenant B were byte-equivalent after removing user_id; the harness recorded matching request-body hashes to verify that no prompt difference explained the result.

Each group ran this seven-call sequence:

  1. Tenant A warm-up 1
  2. Tenant A warm-up 2
  3. After a six-second cache-build wait, Tenant A probe
  4. Tenant B first identical-prefix probe
  5. Tenant B second warm-up
  6. After a second six-second wait, Tenant B probe
  7. Tenant A return probe

The same pattern appeared in all three groups.

Phase, aggregated across 3 groupsPrompt tokensCache-hit tokensCache-miss tokensObserved meaning
Tenant A first probe after warming16,19416,12866Tenant A’s prefix was warm
Tenant B first cross-tenant probe16,194016,194Tenant B did not inherit A’s warm prefix
Tenant B probe after its own warming16,18216,12854Tenant B then had its own warm prefix
Tenant A return probe16,18216,12854Tenant A’s lane remained warm

At the individual-call level, the same-tenant probes produced cache-hit ratios from 99.59% to 99.67%. Every first Tenant B cross-tenant probe produced 0%. Fifteen of the 21 cache-suite calls reported hits; the six misses were the three initial Tenant A warm-ups and the three first Tenant B cross-tenant probes.

This result supports the documented isolation behavior in this run. It does not prove that no cache-isolation bug can ever exist, inspect DeepSeek’s internal storage, or establish a formal confidentiality guarantee. The only provider-reported cache signal available to the harness was token usage.

It also does not show that cached output was shared. DeepSeek’s cache concerns input-prefix computation. The model still generated each output, and each response had to satisfy the current request’s JSON contract.

A minimal reproducible cache probe

The following Python shape reproduces the important comparison. Use only synthetic content, make the prefix long enough to observe useful token counters, and expect caching to remain best effort.

import os
import time
import requests

URL = "https://api.deepseek.com/chat/completions"
HEADERS = {
    "Authorization": f"Bearer {os.environ['DEEPSEEK_API_KEY']}",
    "Content-Type": "application/json",
}

synthetic_prefix = "\n".join(
    f"Record {i:03d}: generated test data; no personal information."
    for i in range(1, 801)
)

def run(user_id: str, request_code: str):
    body = {
        "model": "deepseek-v4-flash",
        "messages": [
            {
                "role": "system",
                "content": "Return JSON with request_code copied exactly."
            },
            {
                "role": "user",
                "content": f"{synthetic_prefix}\nrequest_code={request_code}"
            },
        ],
        "user_id": user_id,
        "response_format": {"type": "json_object"},
        "thinking": {"type": "disabled"},
        "temperature": 0,
        "max_tokens": 50,
    }
    response = requests.post(URL, headers=HEADERS, json=body, timeout=120)
    response.raise_for_status()
    data = response.json()
    usage = data["usage"]
    print(
        user_id,
        request_code,
        "hit=", usage["prompt_cache_hit_tokens"],
        "miss=", usage["prompt_cache_miss_tokens"],
    )

run("tenant_a", "A-WARM-1")
run("tenant_a", "A-WARM-2")
time.sleep(6)
run("tenant_a", "SHARED-PROBE")
run("tenant_b", "SHARED-PROBE")
run("tenant_b", "B-WARM-2")
time.sleep(6)
run("tenant_b", "B-PROBE")

Repeat the sequence with new unique prefixes. Do not treat one cold miss as conclusive, because the provider documents caching as best effort.

What happened when user_id was omitted?

The omitted-ID control created two fresh prefix groups. Within each group, the harness labeled the warm-up requests “Logical A” and the later probe “Logical B,” but no user_id field was sent for either label.

In both groups:

  • the first request reported 0 cache-hit tokens and 6,036 misses;
  • the second request reported 6,016 hits and 20 misses;
  • the later “Logical B” probe reported 6,016 hits and 21 misses.

Those two later probe ratios were 99.65%. The labels existed only in the test application, so DeepSeek received no identity boundary with which to separate them. The result is consistent with omitted requests using a shared fallback cache lane in this run. DeepSeek does not document that observation as a named “default identity partition.”

This does not mean Logical B received Logical A’s text or model output. It means the API reported reused prefix computation. Still, in this run a multi-tenant application that omitted the documented isolation field used a shared fallback cache lane instead of explicit per-user lanes.

user_id is not conversation memory

The official Multi-round Conversation guide states that Chat Completions is stateless. An application must resend the relevant messages on every turn.

We ran a response-contract control in four cycles. Each cycle first sent a unique synthetic marker under Tenant A. A second request used the same user_id but omitted the earlier message and explicitly instructed the model to return NOT_PRESENT when the marker was absent. A third request performed the corresponding control under Tenant B.

All 12 calls passed:

  • 4/4 marker-setting requests copied the current marker;
  • 4/4 same-user requests without history returned NOT_PRESENT;
  • 4/4 different-user requests without history returned NOT_PRESENT.

Because the recall prompts explicitly stated that history was absent and prescribed NOT_PRESENT, these results verify the requested output contract only. They are not independent evidence about hidden server memory. The stateless conclusion comes from DeepSeek’s protocol documentation: applications must resend the messages the model needs, and user_id is not documented as a history handle.

Use user_id for the documented backend isolation functions. Use your own conversation store, truncation, or summary strategy for chat continuity. The tested DeepSeek multi-turn conversations guide explains those history choices.

What the scheduling smoke test did and did not show

DeepSeek account concurrency and user_id scheduling rules for regular and expanded-quota accounts
Regular accounts share one account pool; DeepSeek documents additional per-user_id caps for expanded-quota accounts.

The scheduling suite sent two batches of 16 short calls with a client concurrency of eight. One batch assigned all 16 requests to Tenant A. The other alternated eight Tenant A and eight Tenant B requests.

All 32 returned HTTP 200 on the first attempt, returned the correct tenant and request code, and reported zero cache-hit tokens. The single-tenant batch recorded a true sample median response-header availability of 301 ms and a p95 of 403 ms. The split-tenant batch recorded a true sample median of 294 ms and a p95 of 307 ms; within that split batch, Tenant A’s median was 296 ms and Tenant B’s was 292.5 ms.

The lower split-tenant p95 is an observation, not evidence that splitting IDs improves speed. The two batches were small, ran at different moments, were not randomized into a performance experiment, and were nowhere near the official limits. The timer stopped when the HTTP response object became available, before the body had been fully read; it therefore measures response-header availability rather than time to first streamed token or complete-body latency. Network and service conditions were part of the measurement.

The smoke test supports only two modest statements:

  • concurrent responses remained associated with the correct synthetic request and tenant;
  • low-load calls with one or two IDs completed normally in this run.

It cannot establish scheduling fairness, noisy-neighbor protection, per-ID throttling, or behavior at 500 or 2,500 concurrent requests. Stressing a paid production endpoint to those levels would be unnecessary for this guide.

Choosing a safe user_id strategy

DeepSeek user_id provider isolation compared with application authentication, authorization, storage, billing, and queues
Provider-side identity isolation complements, but does not replace, the application’s tenant security controls.

The identifier should be stable enough to represent the intended isolation lane but opaque enough not to expose private data.

StrategyBenefitTradeoff
One ID per end userStrong user-level cache and safety separationShared prefixes warm separately for each person
One ID per tenant or workspaceCache reuse inside the tenant with separation between tenantsIndividual users share one provider-side identity lane
Tenant plus user pseudonymFine-grained distinction and easy internal mappingMore cache partitions and more application bookkeeping
Random ID per requestMaximum partition churnDefeats meaningful cache reuse and stable scheduling identity
One global ID or omitted fieldMaximum shared-prefix opportunityNo explicit user-level cache partition

DeepSeek documents users on your business side, not a universal rule for whether the value must represent a person, tenant, workspace, or internal service. Choose granularity deliberately. If content-safety identity needs to follow an individual, a tenant-only ID may be too broad. If strict tenant cache separation is the primary goal, a stable tenant-derived component is essential.

Never accept user_id directly from an untrusted browser field or request header and pass it through unchecked. Derive it from the authenticated server-side context. The OWASP Multi-Tenant Security Cheat Sheet recommends binding tenant context to the authenticated session, validating it through application layers, isolating cache namespaces, and implementing tenant-aware quotas and logging.

Generate a pseudonymous allowed-string ID

Server-side HMAC pattern for generating a stable pseudonymous DeepSeek user_id without exposing private data
A keyed server-side HMAC can produce a stable opaque identifier that stays inside DeepSeek’s documented character set.

An HMAC lets the application create a stable identifier without sending its raw database IDs or email addresses to DeepSeek.

import hashlib
import hmac
import os

def make_deepseek_user_id(tenant_id: str, internal_user_id: str) -> str:
    secret = os.environ["USER_ID_HMAC_SECRET"].encode("utf-8")
    canonical = f"{tenant_id}:{internal_user_id}".encode("utf-8")
    digest = hmac.new(secret, canonical, hashlib.sha256).hexdigest()
    return f"u_{digest}"

The result contains only u_ plus lowercase hexadecimal characters, so it stays inside the documented character set and is far below 512 characters. Keep the HMAC secret server-side. Store the mapping or be prepared for the operational effect of rotating that secret: a new value creates a new cache and scheduling lane.

user_id is only one layer of multi-tenant safety

Production multi-tenant request boundary with authentication and authorization before a DeepSeek API call
Authenticate, resolve tenant context, authorize data access, and apply local quotas before sending the DeepSeek request.

A valid user_id does not authorize a request. A malicious person must not be able to change an ID and gain access to another customer’s prompts, vector results, files, tool credentials, or database rows.

Enforce tenant boundaries before the model call:

  • derive tenant context from a verified login or service identity;
  • apply tenant filters or row-level security to every data query;
  • use separate vector-store namespaces or mandatory tenant filters;
  • tenant-prefix application cache keys and object-storage paths;
  • scope tool credentials to the least privilege required;
  • keep per-tenant queues, quotas, and concurrency budgets;
  • redact sensitive data before it enters a prompt;
  • log hashes, counts, and request IDs instead of full private prompts where possible;
  • test cross-tenant access as an authorization regression case.

Also keep the API key on the server. DeepSeek’s Open Platform Terms say developers must not expose an API key in browser or other client-side code and must implement organizational and technical security measures. The DeepSeek API key guide covers secure creation, storage, and rotation.

For workloads involving personal, regulated, or confidential information, review the provider’s terms and your own legal obligations before sending data. The site’s DeepSeek data-residency guide covers the hosted-service privacy context separately. Cache isolation does not change where the provider processes data.

What to log in production

The response does not document an echoed user_id, so keep your own correlation record. Avoid logging the raw ID if a hash or internal surrogate is sufficient.

Useful metrics include:

  • internal tenant and user surrogate;
  • DeepSeek model;
  • application request ID;
  • active in-flight count by tenant and model;
  • queue depth and queue wait time;
  • HTTP status and retry count;
  • response-header availability at p50, p95, and p99, plus separately measured full-body or streaming timings where relevant;
  • prompt, cache-hit, cache-miss, completion, and reasoning tokens;
  • calculated cost by tenant;
  • model system_fingerprint;
  • timeout, 429, 500, and 503 rates;
  • prompt-template version;
  • cache-hit ratio by stable prefix and identity lane.

Alert on sudden cache-hit collapse after a prompt-template deployment. Dynamic timestamps, random request IDs, and frequently changing instructions at the beginning of a prompt can prevent prefix reuse. Place changing data after stable instructions and references when the task permits.

For error handling, distinguish validation failures from transient failures. Do not retry a bad user_id indefinitely. Use the DeepSeek error-codes guide for 400, 401, 402, 422, 429, 500, and 503 behavior.

Production checklist

  • Use the current official endpoint and model name.
  • Send user_id at the documented location for the chosen protocol.
  • Keep the value stable for the intended user or tenant lane.
  • Restrict generation to letters, digits, hyphens, and underscores.
  • Keep the ID at 512 characters or fewer.
  • Never include names, emails, phone numbers, prompt text, or other private data.
  • Derive the ID on the server from authenticated context.
  • Do not trust a tenant identifier supplied directly by the client.
  • Keep authorization and tenant data filters outside the model.
  • Resend conversation history because user_id is not memory.
  • Monitor cache-hit and cache-miss tokens separately.
  • Implement application-side per-tenant queues and quotas.
  • Treat all API keys under one DeepSeek account as sharing account concurrency.
  • Handle 429, 500, and 503 with bounded retry and jitter.
  • Do not rely on the three undocumented characters accepted in this one run.
  • Recheck documentation, pricing, and behavior before major production changes.

Benchmark limitations

This benchmark is descriptive, not a certification or security proof. It used one model, one endpoint, one account, one network location, two synthetic IDs, three cache groups, and one test date. It could not clear or inspect DeepSeek’s internal cache. Cache-hit token counters were the only provider-reported cache signal.

The cache system is best effort. The consistency of three groups is useful evidence, but a larger repeated study would provide tighter confidence. A cache miss by itself is ambiguous; the paired warm-same-user and cold-different-user design is what made the observed pattern meaningful.

The scheduling smoke test deliberately stayed far below published concurrency limits. It did not test 429 thresholds, capacity expansion, fairness, or service-level guarantees. The stateless response-control prompts explicitly stated that earlier history was absent and prescribed the answer, so they were not an independent memory test. Recorded timing is response-header availability and includes network and provider conditions; it is not streaming TTFT or full-body completion time. Prices, validation behavior, limits, model behavior, and system fingerprints can change.

FAQ

What is DeepSeek user_id isolation?

DeepSeek user_id isolation is the use of DeepSeek’s custom user_id request field for the three functions the provider documents: content-safety isolation, KV-cache isolation, and scheduling isolation among users served through the same API account.

Where do I put user_id in a DeepSeek request?

Put it at the top level of a raw OpenAI-format Chat Completions JSON body. With the Python OpenAI SDK, use extra_body={"user_id": "..."}. With DeepSeek’s Anthropic-compatible endpoint, use metadata={"user_id": "..."}.

What characters are allowed in DeepSeek user_id?

The documented character set is letters, digits, hyphens, and underscores, with a maximum length of 512. Although the July 25, 2026 test accepted a space, dot, and exclamation mark, production code should follow the documented set.

Does user_id isolate the DeepSeek context cache?

DeepSeek says it does. In this benchmark, all three groups showed a warmed Tenant A prefix, zero cache-hit tokens on Tenant B’s first identical-prefix request, and a high Tenant B hit ratio after Tenant B warmed its own lane. That supports the documentation for this run but is not a universal security proof.

What happens if I omit user_id?

In this benchmark, requests without the field behaved consistently with a shared fallback cache lane. In both omitted-ID controls, a later request with a different application-side logical-user label reused the warmed prefix because no identity distinction was sent to DeepSeek. DeepSeek does not name this behavior a default identity partition in the cited documentation.

Does user_id make DeepSeek remember a conversation?

No. DeepSeek documents Chat Completions as stateless, so the application must resend the messages the model needs. The benchmark’s same-ID controls returned the prescribed NOT_PRESENT value, but those constrained prompts were not an independent memory test.

Does every user_id receive a separate DeepSeek rate limit?

Not for regular accounts. DeepSeek says all IDs are combined for the account concurrency calculation. For increased-quota accounts, it documents both a total account cap and per-ID concurrency limits.

Can I use an email address as user_id?

No. DeepSeek explicitly says not to include private user information. Generate a stable pseudonymous value, such as an HMAC-derived hexadecimal identifier, and keep the mapping inside your server.

Does user_id replace tenant authorization?

No. It does not protect your database, vector store, tools, files, sessions, application caches, or object storage. Enforce authenticated tenant context and authorization independently at every data boundary.

How can I verify user_id cache isolation?

Use synthetic long prefixes. Warm a unique prefix under Tenant A, record prompt_cache_hit_tokens, then send the identical request body under a previously unused Tenant B ID. Repeat across several unique groups, allow time for cache construction, and report both hits and misses without treating one run as definitive.

Official sources checked

Last tested and source-checked: July 25, 2026.