Where the DeepSeek API Stands on October 22, 2026 DeepSeek moved its Flash model to the V4.1-Flash-1017 checkpoint on October 17, 2026, keeping the `deepseek-flash` model ID and API calling method unchanged while opening its Search and Fetch APIs to outside developers, according to MarkTechPost's report on the release. The checkpoint arrived about five weeks after the September 10, 2026 launch of V4.1-Flash, which introduced native multimodal support and a new price list effective 04:00 UTC that day, and three days after requests to `deepseek-v4-pro` began routing to V4.1 Flash at Flash rates on September 14. DeepSeek's changelog directs developers to call the single `deepseek-flash` alias, since version-pinned names and the retired `deepseek-v4-pro` string are what can break existing code. DeepSeek upgraded V4.1 Flash to the V4.1-Flash-1017 checkpoint on October 17, 2026, and API users did not have to touch their code. The model ID stays deepseek-flash and now points at the new build, according to MarkTechPost’s report on the release https://www.marktechpost.com/2026/10/17/deepseek-just-released-v4-1-flash-1017-scores-77-0-on-deepswe-opens-search-api/?amp=1 . Anything pinned to a version suffix or to the old deepseek-v4-pro name is the part of your stack that can break. The checkpoint landed about five weeks after the original V4.1 Flash launch, which also folded V4 Pro traffic into Flash see the coverage of the V4.1 Flash launch and V4 Pro sunset https://shattered.io/deepseek-v4-1-flash-v4-pro-retirement-2026/ . Tutorials written in early September already carry stale model names, stale prices, and no mention of the peak and off-peak rate split that now drives the monthly bill. Python remains the default language for this work. Stack Overflow’s 2025 developer survey found that 57.9% of respondents used Python for extensive development in the past year, up 7 percentage points from 2024, and that 84% of surveyed developers used or planned to use AI tools in their workflow survey data https://survey.stackoverflow.co/2025/technology . A DeepSeek client with streaming, validated JSON output, tool calling, retries, and a cost ledger fits in under 500 lines of Python, tests included. Twelve steps and about 45 minutes take you from an empty folder to that project. Don't miss new tech stories on Google Add Shattered once in the Google app and our stories show up in your news suggestions. Add now https://www.google.com/preferences/source?hl=en&q=https%3A%2F%2Fshattered.io%2F Where the DeepSeek API Stands on October 22, 2026 Three dates define the current state of the DeepSeek API. On September 10, 2026, DeepSeek launched V4.1-Flash with native multimodal support and a new price list that took effect at 04:00 UTC the same day, per the company’s release post https://www.deepseek.com/en/news/deepseek-v4-1-flash/ . On September 14, requests addressed to deepseek-v4-pro began routing to V4.1 Flash and billing at Flash rates, a routing that DeepSeek said would continue until V4.1-Pro launches. On October 17, the Flash model moved to the 1017 build and the company opened its Search and Fetch APIs to outside developers. The practical consequence: there is exactly one model alias a new project should call. DeepSeek’s changelog tells developers to use deepseek-flash for the latest Flash model, and the API calling method and the price framework did not change with the October update. The version-specific name, DeepSeek-V4.1-Flash-1017, describes the backend checkpoint rather than a separate model ID. Treat it as a label for release notes and never as a string in your request body. Quality moved too. The 1017 build is described as faster and leaner, with gains in reasoning, coding, agentic tasks, and output length. Independent numbers are still arriving, and the early benchmark readings are collected in our look at the HLE score for V4.1-Flash-1017 https://shattered.io/deepseek-v4-1-flash-1017-hle-score-42-6-2026/ . For a tutorial the benchmark race matters less than the stability of the interface, and on that front the news is good: one alias, one endpoint, unchanged calling method. Reliability deserves a sober note. Secondary reports described service slowdowns and an outage of roughly five hours in the days before the 1017 release, and those reports did not come from DeepSeek’s own changelog. Whatever the exact figure, any production client needs retries with backoff, timeouts, and a plan for a provider that is down. Steps 3 and 11 build exactly that. | Date 2026 | Change | What it means for your code | |---|---|---| | September 10 | V4.1-Flash launches with native multimodal support and new pricing from 04:00 UTC | Peak and off-peak rates enter your cost math | | September 14 | deepseek-v4-pro requests route to V4.1 Flash at Flash rates until V4.1-Pro launches | Old Pro model IDs no longer return a Pro model | | October 17 | V4.1-Flash-1017 replaces the earlier checkpoint, alias deepseek-flash unchanged | No code change needed if you use the alias | | October 17 | Search API and Fetch API open to all developers at $0.75 per 1,000 successful requests | New tool surface, billing starts November 1 | | October 19 | Cloudflare lists the model on Workers AI | A second route to the same model family | | October 20 | US administration bars DeepSeek from civilian federal agency systems, per CNBC reporting | Compliance check for government work | | November 1 | Per-request billing for Search and Fetch begins at 00:00 Beijing time | Add a spending cap before this date | One more status item. DeepSeek’s notices give no V4.1-Pro release date, so treat Pro as announced in principle and unavailable today. If a Pro tier ships, expect a new model alias and a new rate card, and expect the routing from deepseek-v4-pro to end. A config-driven model name, which Step 3 sets up, turns that future migration into a one-line change. Prerequisites: Versions, Accounts and Budget Everything below was run against a local mock of the chat-completions endpoint, since a live key was not needed to verify the client logic. The mock returns streaming chunks, tool calls, JSON-mode replies, and usage fields with cache counters. That setup exercised every code path in the project. Live behavior, including exact token counts and latency, will differ, so treat the sample outputs as shape references. - Python 3.11 or newer. The code uses a union type hint datetime | None , so use a recent interpreter. - pip 23 or newer inside a virtual environment, so nothing touches your system Python. - openai the official Python SDK . DeepSeek’s endpoint has followed the OpenAI chat-completions request format in earlier releases, and the code depends on that. Confirm against the API docs page titled Your First API Call before shipping. - pydantic 2.x for validating JSON output, tenacity for retries, python-dotenv for local configuration, and pytest for tests. - A DeepSeek platform account with an API key and a small prepaid balance. Every live request in this project costs a fraction of a cent, so a few dollars covers all twelve steps with room for experiments. - A terminal and a text editor. Linux, macOS, and Windows all work. Windows users should run the PowerShell variants of the activation and environment commands shown in Steps 1 and 2. Budget about 45 minutes. Steps 1 and 2 take ten minutes including account setup. Steps 3 through 8 are the core build and take about 25. Steps 9 through 12 add the cost ledger, command-line interface, tests, and final run, and fit in the remaining ten. Skip nothing in Step 1, because the key-handling habits there protect you from the most common and most expensive mistake: a leaked key. DeepSeek API Pricing: Peak, Off-Peak and Cache Math Before writing code, read the rate card, because it shapes the architecture. V4.1 Flash pricing has two tiers. Off-peak rates are 50% of peak rates, according to DeepSeek’s pricing notice, and peak applies Monday through Friday from 01:00 to 04:00 UTC and from 06:00 to 10:00 UTC, excluding Chinese public holidays. The September launch brought the output price cut https://shattered.io/deepseek-v4-1-flash-70-percent-price-cut-2026/ into effect, and the October 17 update kept the same price framework. | Item | Off-peak USD per 1M tokens | Peak USD per 1M tokens | |---|---|---| | Input, cache hit | $0.003 | $0.006 | | Input, cache miss | $0.15 | $0.30 | | Output | $0.60 | $1.20 | | Search API call | $0.75 per 1,000 successful requests | same, billing from November 1 | | Fetch API call | $0.75 per 1,000 successful requests | same, billing from November 1 | Cache hits cost far less than cache misses at the same time of day 0.003 against 0.15 off-peak . That gap is the single biggest lever on a DeepSeek bill. A prompt that begins with a long, unchanging system message and examples, followed by the variable user text, gives the provider’s context cache the best chance to hit. Step 9 teaches the ledger to track hits and misses separately so you can see the effect in your own numbers rather than trusting a rule of thumb. The scenarios below use only the published rates and plain arithmetic. A request with 1,500 uncached input tokens and 400 output tokens costs $0.000225 for input plus $0.00024 for output, or $0.000465 off-peak. The same request at peak costs $0.00093. At 10,000 such requests a day the figures climb into real money, which is why scheduling matters. | Scenario | Off-peak | Peak | Blended uniform load | |---|---|---|---| | One request, 1,500 in uncached and 400 out | $0.000465 | $0.00093 | n/a | | 10,000 requests a day, no cache hits | $4.65 | $9.30 | Not listed | | 10,000 requests a day, 60% of input cached | Not listed | Not listed | Not listed | | 10,000 Search calls from Nov 1 | $7.50 | $7.50 | $7.50 | | 30 days, 10,000 requests a day, no cache, all off-peak | Not listed | n/a | n/a | | 30 days, same load, uniform spread, 60% cached | n/a | n/a | Not listed | The blended column assumes load spreads evenly across the week. Peak hours cover 5 out of every 24 hours on average 7 hours on each of 5 weekdays, out of 168 hours , so blended cost equals the off-peak cost plus 5/24 of the gap between peak and off-peak. The estimate ignores Chinese public holidays, which DeepSeek excludes from peak, so real bills can land slightly lower. Build the Foundation: Steps 1 to 4 Step 1: Create an API Key and Lock It Down Sign in to the DeepSeek platform console, open the API keys section, and create a key with a descriptive name such as deepseek-lab-dev . The console shows the secret once. Copy it straight into a password manager https://shattered.io/category/privacy/ , because a lost key cannot be recovered and has to be replaced. Add a small prepaid balance, since requests fail with a billing error when the balance hits zero. Create one key per project and per environment. A single shared key makes rotation painful, and it hides which service is spending money. If the platform offers spending limits or usage alerts, switch them on now. A $5 alert threshold catches a runaway loop within minutes, while a monthly invoice catches it after the damage is done. Never paste the key into source code, a notebook cell, or a chat message. Store it in an environment variable for local work and in your platform’s secret manager for deployment. The project reads it from a .env file that Git ignores, and the file permissions should block other users on the machine. .env never commit this file DEEPSEEK API KEY=sk-your-key-here DEEPSEEK MODEL=deepseek-flash macOS / Linux chmod 600 .env Windows PowerShell: set the key for the current session only $env:DEEPSEEK API KEY = "sk-your-key-here" Step 2: Create the Project and Install Dependencies A virtual environment keeps the dependencies isolated and makes the project reproducible on a teammate’s machine or a build server. The commands below create the folder, activate the environment, and install the five packages. The empty init .py turns the dslab folder into an importable package, which is what lets you run modules with python -m dslab.cli later. mkdir deepseek-lab && cd deepseek-lab python3 -m venv .venv source .venv/bin/activate Windows PowerShell: .venv\Scripts\Activate.ps1 python -m pip install --upgrade pip pip install openai tenacity "pydantic =2" python-dotenv pytest mkdir dslab tests && touch dslab/ init .py printf '.env\n.venv/\n pycache /\n' .gitignore pip freeze | grep -iE "^ openai|tenacity|pydantic|python-dotenv|pytest ==" The last command prints the pinned versions you ended up with. Copy them into requirements.txt once everything works, so a later install reproduces your exact setup. The .gitignore line matters more than it looks: it is the guardrail between your key and a public repository. Step 3: Add Configuration and a Retrying Client Two small files carry most of the production value. config.py reads the key, base URL, and model name from the environment, with deepseek-flash as the default model. Keeping the model name in configuration means the September-style surprises a routed alias, a retired name become a one-line change instead of a code hunt. python import os from dataclasses import dataclass from dotenv import load dotenv load dotenv @dataclass frozen=True class Settings: api key: str base url: str = "https://api.deepseek.com" model: str = "deepseek-flash" timeout: float = 60.0 def load settings - Settings: key = os.getenv "DEEPSEEK API KEY", "" .strip if not key: raise SystemExit "DEEPSEEK API KEY is not set. Add it to .env or export it." return Settings api key=key, base url=os.getenv "DEEPSEEK BASE URL", "https://api.deepseek.com" , model=os.getenv "DEEPSEEK MODEL", "deepseek-flash" , client.py builds the SDK client and wraps the call in a retry policy. The SDK has its own retry setting, so the code switches it off with max retries=0 and lets tenacity own the policy. Two retry layers multiply into long, confusing stalls. The policy retries only errors that a retry can fix rate limits, connection failures, timeouts, and 5xx responses and gives up after five attempts with jittered exponential backoff capped at 30 seconds. python import openai from openai import OpenAI from tenacity import retry, retry if exception type, stop after attempt, wait random exponential, from .config import Settings RETRYABLE = openai.RateLimitError, openai.APIConnectionError, openai.APITimeoutError, openai.InternalServerError, def make client settings: Settings - OpenAI: return OpenAI api key=settings.api key, base url=settings.base url, timeout=settings.timeout, max retries=0, retry policy = retry retry=retry if exception type RETRYABLE , wait=wait random exponential multiplier=1, max=30 , stop=stop after attempt 5 , reraise=True, @retry policy def chat once client: OpenAI, settings: Settings, messages: list, kwargs : return client.chat.completions.create model=settings.model, messages=messages, kwargs Authentication errors, bad requests, and insufficient-balance errors are deliberately absent from the retry list. Repeating a request with a bad key just burns time. Those errors should surface immediately so a human can fix the cause. Step 4: Make Your First DeepSeek API Call A ten-line script confirms the key, the endpoint, and the alias in one shot. Save it as hello.py in the project root and run it with the environment active. python from dslab.client import chat once, make client from dslab.config import load settings settings = load settings client = make client settings resp = chat once client, settings, {"role": "user", "content": "In one sentence, what is a mixture-of-experts model?"} , print resp.choices 0 .message.content print resp.usage Run python hello.py . The reply and the usage object should look like the example below. Your wording and counts will differ, and the usage object may carry extra fields, including cache counters, which Step 9 reads. bash $ python hello.py A mixture-of-experts model splits its parameters into many specialist sub-networks and routes each token to only a few of them, so it can be huge in total size yet cheap to run per token. CompletionUsage completion tokens=41, prompt tokens=17, total tokens=58 If the call fails, jump to the troubleshooting section. If it works, resist the urge to hard-code anything you saw in the response, including the model field the server echoes back. Server-side names can change while your alias stays valid, and the earlier V4 Pro 0813 release https://shattered.io/deepseek-v4-pro-0813-price-hike-2026/ showed how quickly a Pro-tier name and its pricing can shift under a project that pinned them. Add the Core Features: Steps 5 to 8 Step 5: Stream Responses Token by Token Streaming cuts perceived latency from the full generation time to the time of the first token. For chat interfaces and command-line tools, that difference decides whether the app feels alive or frozen. The OpenAI-style streaming protocol sends small chunks, each carrying a fragment of text in choices 0 .delta.content . The trap is billing visibility. Without an extra option, a streamed response does not report token usage, and your cost ledger stays blind. Passing stream options={"include usage": True} asks the server to append one final chunk that has an empty choices list and a populated usage field. The loop in chat.py handles both kinds of chunk, and it guards against empty choices so the final usage chunk never raises an IndexError . If your gateway ignores the option, the ledger simply reports zeros and you will know to check. Step 6: Keep Conversation Memory Without Blowing the Budget The chat API is stateless. Every request must carry the history it needs, which means cost grows with each turn, because turn 10 re-sends turns 1 through 9 as input. The Conversation class keeps a system message that never changes, a list of turns, and a sliding window of the most recent max turns exchanges. A window of 12 exchanges is generous for a support bot and wasteful for a quick-answer tool, so tune it to the job. python import sys from .client import chat once from .pricing import Ledger class Conversation: def init self, client, settings, system: str, ledger: Ledger, max turns: int = 12 : self.client = client self.settings = settings self.ledger = ledger self.system = {"role": "system", "content": system} self.turns: list dict = self.max turns = max turns def messages self - list dict : return self.system, self.turns -self.max turns 2 : def ask self, text: str, stream: bool = True, out=sys.stdout - str: self.turns.append {"role": "user", "content": text} if stream: answer = self. stream out else: resp = chat once self.client, self.settings, self. messages , temperature=0.3 self.ledger.add resp.usage answer = resp.choices 0 .message.content or "" self.turns.append {"role": "assistant", "content": answer} return answer def stream self, out - str: parts: list str = stream = chat once self.client, self.settings, self. messages , temperature=0.3, stream=True, stream options={"include usage": True}, for chunk in stream: if chunk.usage: self.ledger.add chunk.usage if chunk.choices and chunk.choices 0 .delta.content: piece = chunk.choices 0 .delta.content parts.append piece out.write piece out.flush out.write "\n" return "".join parts Two design choices deserve a comment. First, the system message sits at the front and never changes between calls. Context caching works on repeated prefixes in DeepSeek’s earlier releases, so a stable opening gives the cache a chance to hit and moves tokens from the 0.15 column to the 0.003 column of the off-peak rate card. Second, trimming old turns shifts the window start, which changes the prefix after the system message. If cache hits matter more than memory depth, summarize old turns into one stable block instead of dropping them one by one. Step 7: Get Validated JSON Output Free-form text cannot feed a database or an API. JSON mode asks the model to return a single JSON object, and DeepSeek has supported it through the response format parameter with {"type": "json object"} . Two habits make it dependable. The prompt must say the word JSON and show the schema, and the code must validate whatever comes back, because JSON mode guarantees syntax at best and never guarantees your schema. python import json import re from typing import Literal from pydantic import BaseModel, ValidationError from .client import chat once class Review BaseModel : summary: str risks: list str severity: Literal "low", "medium", "high" SYSTEM = "You review short technical notes. Reply with one JSON object and nothing else. " 'Schema: {"summary": string, "risks": string , "severity": "low" | "medium" | "high"}' def strip fences text: str - str: text = text.strip match = re.match r"^ ?:json ?\s . ? \s $", text, flags=re.S return match.group 1 if match else text def extract review client, settings, ledger, note: str - Review: messages = {"role": "system", "content": SYSTEM}, {"role": "user", "content": note}, last error = "" for attempt in range 2 : resp = chat once client, settings, messages, temperature=0, response format={"type": "json object"}, ledger.add resp.usage raw = resp.choices 0 .message.content or "" try: return Review.model validate json strip fences raw except ValidationError, json.JSONDecodeError as exc: last error = str exc messages.append {"role": "assistant", "content": raw} messages.append { "role": "user", "content": f"That JSON failed validation: {last error :300 }. Return corrected JSON only.", } raise ValueError f"Model never returned valid JSON: {last error :200 }" Pydantic does the schema check. When validation fails, the loop appends the bad reply and the error message to the conversation and asks for a corrected object, once. One repair attempt fixes most formatting slips cheaply, and a hard cap prevents an endless loop of paid retries. The strip fences helper handles a common quirk where a model wraps JSON in a Markdown code fence even when told not to. Temperature 0 keeps extraction deterministic enough to test. Step 8: Add Tool Calling Tool calling turns the model into a controller. You describe functions with JSON Schema, the model decides when to call one, your code runs it, and you send the result back for a final answer. DeepSeek’s materials stress agentic ability in the Flash line, and the Flash-1017 agent benchmark results https://shattered.io/deepseek-v4-1-flash-1017-deepswe-77-search-api-2026/ suggest the 1017 build leans further into that. The loop below is the same shape any agent framework uses underneath. python import json from datetime import datetime, timezone from .client import chat once from .pricing import cost usd TOOLS = { "type": "function", "function": { "name": "utc now", "description": "Return the current UTC time as an ISO 8601 string.", "parameters": {"type": "object", "properties": {}}, }, }, { "type": "function", "function": { "name": "estimate cost", "description": "Estimate the USD cost of one deepseek-flash call at the current peak or off-peak rate, assuming no cache hits.", "parameters": { "type": "object", "properties": { "input tokens": {"type": "integer", "minimum": 0}, "output tokens": {"type": "integer", "minimum": 0}, }, "required": "input tokens", "output tokens" , }, }, }, def run tool name: str, args: dict - dict: if name == "utc now": return {"utc": datetime.now timezone.utc .isoformat timespec="seconds" } if name == "estimate cost": usd = cost usd 0, int args "input tokens" , int args "output tokens" return {"usd": round usd, 8 } return {"error": f"unknown tool {name}"} def chat with tools client, settings, ledger, question: str, max rounds: int = 4 - str: messages = {"role": "system", "content": "Use the provided tools for time and cost questions. Be brief."}, {"role": "user", "content": question}, for in range max rounds : resp = chat once client, settings, messages, tools=TOOLS, temperature=0 ledger.add resp.usage msg = resp.choices 0 .message if not msg.tool calls: return msg.content or "" messages.append { "role": "assistant", "content": msg.content, "tool calls": { "id": c.id, "type": "function", "function": {"name": c.function.name, "arguments": c.function.arguments}, } for c in msg.tool calls , } for call in msg.tool calls: try: args = json.loads call.function.arguments or "{}" result = run tool call.function.name, args except ValueError, KeyError, TypeError as exc: result = {"error": str exc } messages.append {"role": "tool", "tool call id": call.id, "content": json.dumps result } raise RuntimeError f"Tool loop did not finish in {max rounds} rounds" Four details keep the loop safe. The tool list is explicit, so the model can only call functions you wrote. Arguments arrive as a JSON string and can be malformed, so the parser sits inside a try block that returns an error object to the model instead of crashing. The loop stops after four rounds, because a confused model can call tools forever at your expense. And the assistant message is rebuilt by hand from the tool calls, which avoids sending unexpected extra fields back to the endpoint. Both demo tools run locally and touch nothing sensitive. When you replace them with real ones a database lookup, a ticket creator, a web fetch , apply the same rules you would use for any public API: validate every argument, enforce an allowlist, and never let model output flow straight into a shell command or a SQL string. Ship It: Steps 9 to 12 Step 9: Track Cost With Peak and Off-Peak Awareness A ledger that multiplies tokens by one flat price is wrong half the time. The pricing.py module encodes the rate card from the table earlier, checks whether the current UTC moment falls in a peak window, and prices cache hits, cache misses, and output separately. The Ledger class reads the cache counters from the usage object with getattr and falls back to treating all input as a miss when the counters are absent. DeepSeek’s usage object has reported prompt cache hit tokens and prompt cache miss tokens in earlier releases, and the fallback keeps the code safe if that changes. python from dataclasses import dataclass from datetime import datetime, timezone @dataclass frozen=True class Rates: cache hit: float cache miss: float output: float USD per 1 million tokens OFF PEAK = Rates cache hit=0.003, cache miss=0.15, output=0.60 PEAK = Rates cache hit=0.006, cache miss=0.30, output=1.20 Monday to Friday, UTC hours start, end PEAK WINDOWS = 1, 4 , 6, 10 def is peak now: datetime | None = None - bool: now = now or datetime.now timezone.utc if now.weekday = 5: return False return any start <= now.hour < end for start, end in PEAK WINDOWS def cost usd hit: int, miss: int, out: int, now: datetime | None = None - float: r = PEAK if is peak now else OFF PEAK return hit r.cache hit + miss r.cache miss + out r.output / 1 000 000 @dataclass class Ledger: calls: int = 0 hit: int = 0 miss: int = 0 out: int = 0 usd: float = 0.0 def add self, usage, now: datetime | None = None - None: if usage is None: return hit = getattr usage, "prompt cache hit tokens", None or 0 miss = getattr usage, "prompt cache miss tokens", None if miss is None: miss = max usage.prompt tokens or 0 - hit, 0 out = usage.completion tokens or 0 self.calls += 1 self.hit += hit self.miss += miss self.out += out self.usd += cost usd hit, miss, out, now def summary self - str: total in = self.hit + self.miss ratio = self.hit / total in 100 if total in else 0.0 return f"{self.calls} call s | in {total in} tok {ratio:.0f}% cached | " f"out {self.out} tok | est. ${self.usd:.6f}" The module bakes in two simplifications, both on the safe side for budgeting. It does not model Chinese public holidays, which DeepSeek excludes from peak, so it may overstate cost on those days. And it prices the call at the moment the response arrives, so a request that straddles a window boundary gets the later tier. Treat the ledger as an estimate to compare against the invoice, never as the invoice. Step 10: Wrap Everything in a Command-Line Interface A CLI turns the modules into something you can demo, script, and put in a cron job. argparse subcommands cover the four workflows: interactive chat, JSON extraction, tool calling, and a rate command that prints whether the peak or off-peak tier applies right now. Every command ends by printing the ledger summary, so each run shows what it cost. python import argparse from .chat import Conversation from .client import make client from .config import load settings from .pricing import Ledger, is peak from .structured import extract review from .tools import chat with tools def main - None: parser = argparse.ArgumentParser prog="dslab", description="DeepSeek API lab" sub = parser.add subparsers dest="cmd", required=True sub.add parser "chat", help="interactive streaming chat" p ext = sub.add parser "extract", help="structured JSON review of a note" p ext.add argument "note" p tool = sub.add parser "tools", help="ask a question that may call local tools" p tool.add argument "question" sub.add parser "rate", help="show which price tier applies right now" args = parser.parse args if args.cmd == "rate": print "peak" if is peak else "off-peak" return settings = load settings client = make client settings ledger = Ledger if args.cmd == "chat": convo = Conversation client, settings, "You are a concise technical assistant.", ledger print "Type 'exit' to quit." while True: try: text = input "you " .strip except EOFError, KeyboardInterrupt : break if text.lower in {"exit", "quit"}: break if text: print "ai ", end="" convo.ask text elif args.cmd == "extract": review = extract review client, settings, ledger, args.note print review.model dump json indent=2 elif args.cmd == "tools": print chat with tools client, settings, ledger, args.question print f" {ledger.summary } " if name == " main ": main The rate command doubles as a scheduling helper. A shell script that runs a batch job can call it and sleep until the output reads off-peak, which is the cheapest optimization in this entire tutorial. It runs before load settings , so it works even without a key configured. Step 11: Write Tests That Cost Nothing Tests for an API client should never spend money. The pricing module is pure logic, so pytest covers it directly with fixed timestamps: a Wednesday at 07:00 UTC for peak, a Wednesday at 12:00 UTC for off-peak, a Saturday for the weekend rule, and the exact boundary at 03:59 and 04:00. The ledger tests use a plain namespace object in place of a real usage response, once with cache fields and once without. python from datetime import datetime, timezone from types import SimpleNamespace from dslab.pricing import Ledger, cost usd, is peak WED PEAK = datetime 2026, 10, 21, 7, 0, tzinfo=timezone.utc WED OFF = datetime 2026, 10, 21, 12, 0, tzinfo=timezone.utc SAT = datetime 2026, 10, 24, 7, 0, tzinfo=timezone.utc def test peak windows : assert is peak WED PEAK assert not is peak WED OFF assert not is peak SAT assert is peak datetime 2026, 10, 21, 3, 59, tzinfo=timezone.utc assert not is peak datetime 2026, 10, 21, 4, 0, tzinfo=timezone.utc def test cost matches rate card : assert round cost usd 0, 1 000 000, 0, WED OFF , 6 == 0.15 assert round cost usd 0, 1 000 000, 0, WED PEAK , 6 == 0.30 assert round cost usd 0, 0, 1 000 000, WED OFF , 6 == 0.60 assert round cost usd 0, 0, 1 000 000, WED PEAK , 6 == 1.20 assert round cost usd 1 000 000, 0, 0, WED OFF , 6 == 0.003 def test ledger falls back when cache fields missing : usage = SimpleNamespace prompt tokens=1500, completion tokens=400 ledger = Ledger ledger.add usage, WED OFF assert ledger.miss == 1500 and ledger.hit == 0 assert round ledger.usd, 6 == 0.000465 def test ledger uses cache fields when present : usage = SimpleNamespace prompt tokens=1500, completion tokens=400, prompt cache hit tokens=1000, prompt cache miss tokens=500, ledger = Ledger ledger.add usage, WED OFF assert ledger.hit, ledger.miss == 1000, 500 def test search budget blocks overspend : import pytest from dslab.budget import BudgetExceeded, SearchBudget budget = SearchBudget max usd=0.0015 budget.charge budget.charge with pytest.raises BudgetExceeded : budget.charge assert budget.calls == 2 For end-to-end checks without a key, the project was exercised against a 40-line local mock server that speaks the chat-completions format. Point DEEPSEEK BASE URL at it and the whole CLI runs offline, including streaming, JSON mode, and the tool loop. That trick also gives you a safe way to test failure handling: make the mock return a 429 or a malformed JSON string and watch the retry and repair paths fire. Step 12: Run the Project End to End With the files in place, run the tests first, then the offline commands, then one live call per feature. Run the live calls off-peak if you can, though at these prices the difference over a few requests rounds to nothing. The expected output for each command appears in the project walkthrough below. python -m pytest -q python -m dslab.cli rate python -m dslab.cli extract "Rotating API keys by hand every quarter, no alert if one leaks." python -m dslab.cli tools "What would 1,500 input tokens and 400 output tokens cost right now?" python -m dslab.cli chat If all five run cleanly, the foundation is finished: configuration, retries, streaming, memory, validated JSON, tool calling, cost tracking, and tests. Everything after this point is hardening and extension. Search and Fetch APIs: What Changed on October 17 DeepSeek’s October 17 release did more than swap a checkpoint. It also opened two retrieval services to outside developers and all users. The Search API returns up to 50 ranked web results per query, and the Fetch API converts a single web page into Markdown. Each is priced at $0.75 per 1,000 successful requests, which works out to $0.00075 per call. Per-request billing starts at 00:00 Beijing time on November 1, 2026 that is 16:00 UTC on October 31 , and until then, according to DeepSeek’s documentation, calls made through DeepSeek Harness, Claude Code, or Codex are free. That pricing matters because retrieval is where agent bills quietly balloon. A research agent that issues 8 searches and fetches 12 pages per task makes 20 billable calls, or $0.015 per task before a single model token. At 1,000 tasks a day, the retrieval line alone costs $15 a day, and a loop bug that retries forever turns that into a four-figure surprise overnight. The request and response schema for both services lives in the Search guide of DeepSeek’s API docs, which is the authority to follow when you wire them in. The tutorial project does not hard-code a guessed schema. What the project does provide is the guardrail that belongs in front of any per-call tool. SearchBudget counts calls, converts them into dollars at the published rate, and raises an exception the moment the next call would pass a cap you set. Call budget.charge before each Search or Fetch request and a runaway agent stops at a number you chose. class BudgetExceeded RuntimeError : pass class SearchBudget: """Hard spending cap for per-request tools such as Search and Fetch.""" USD PER CALL = 0.75 / 1000 def init self, max usd: float : self.max usd = max usd self.calls = 0 @property def spent self - float: return self.calls self.USD PER CALL def charge self - None: if self.calls + 1 self.USD PER CALL self.max usd: raise BudgetExceeded f"cap of ${self.max usd:.2f} reached after {self.calls} calls" self.calls += 1 A final caution on third-party tooling. Several tracker sites already publish unofficial summaries of the Search and Fetch endpoints. Some of them are accurate, and some copy each other’s mistakes. Before you build on a request shape, confirm it against the official docs and test it with a handful of paid calls, because the rate of $0.75 per 1,000 leaves plenty of room for that experiment. The Complete Project: Layout, Run Commands and Expected Output The finished project has one package, one test file, and a few small support files. Every file appeared in full in the steps above, so the tree below doubles as a checklist. Related model families and their own API setups are collected in the AI and machine learning section https://shattered.io/category/ai-machine-learning/ for readers who want to compare providers. deepseek-lab/ ├── .env secrets, ignored by Git ├── .gitignore ├── requirements.txt ├── hello.py Step 4 smoke test ├── dslab/ │ ├── init .py │ ├── config.py Step 3 │ ├── client.py Step 3 │ ├── chat.py Steps 5-6 │ ├── structured.py Step 7 │ ├── tools.py Step 8 │ ├── pricing.py Step 9 │ ├── cli.py Step 10 │ ├── budget.py Search and Fetch spending cap │ └── fanout.py concurrent batch runner Advanced Tips └── tests/ └── test pricing.py Step 11 Illustrative output follows. Token counts, wording, and cost will differ on the live API, and only the test line is a literal capture. The shape is what matters: each command prints its result, then one ledger line. bash $ python -m pytest -q ..... 100% 5 passed in 0.03s $ python -m dslab.cli rate off-peak $ python -m dslab.cli extract "Rotating API keys by hand every quarter, no alert if one leaks." { "summary": "Manual quarterly key rotation with no leak alerting.", "risks": "Leaked keys stay valid until the next rotation", "No detection of unauthorized usage" , "severity": "high" } 1 call s | in 120 tok 83% cached | out 30 tok | est. $0.000021 $ python -m dslab.cli tools "What would 1,500 input tokens and 400 output tokens cost right now?" About $0.000465 at the off-peak rate. 2 call s | in 240 tok 83% cached | out 60 tok | est. $0.000043 $ python -m dslab.cli chat Type 'exit' to quit. you Explain context caching in two sentences. ai Context caching stores the processed form of a repeated prompt prefix so later requests skip that work. Cached input tokens are billed at a small fraction of the normal input price. you exit 1 call s | in 64 tok 0% cached | out 38 tok | est. $0.000032 Read the ledger line as a feedback loop. A first call shows 0% cached. Repeat the same system prompt and a long shared prefix a few times and the percentage climbs, and the estimated cost per call drops with it. That visible drop is the cheapest education in prompt design you will get. Common Pitfalls With the DeepSeek API Most DeepSeek API failures come from a short list of avoidable habits. Each one below has cost someone real money or an afternoon of debugging. 1. Pinning a version suffix or a retired alias. Code that sends DeepSeek-V4.1-Flash-1017 or deepseek-v4-pro as the model name depends on names that DeepSeek’s own notices describe as a checkpoint label and a routed alias. Use deepseek-flash from configuration, and log the model the server reports so you can see changes. 2. Budgeting with one flat price. Peak output costs $1.20 per million tokens against $0.60 off-peak. A cost model without the time-of-day split can be off by a factor of two, and a factor of two is the difference between an approved and a rejected budget. 3. Ignoring the cache. At $0.003 against $0.15 per million tokens off-peak, a cache hit is far cheaper than a miss. Putting the variable user text first and the stable instructions last wipes out that discount. Stable prefix first, variable tail last. 4. Streaming without usage reporting. Leave out stream options and the ledger reads zero for every streamed reply. Teams discover this at invoice time. 5. Trusting JSON mode as a schema guarantee. It promises parseable JSON in the best case. Field names, types, and enum values still need validation, which is why Step 7 uses Pydantic and a single repair pass. 6. Letting the tool loop run free. A model that keeps requesting tools drains money and time. Cap the rounds, cap the per-run spend, and fail loudly when the cap trips. 7. Stacking retry layers. The SDK retries, your wrapper retries, and your job queue retries. Three layers of three attempts make nine calls for one failure and hammer a provider that is already struggling. Pick one owner for retry policy. 8. Committing the key. Automated scanners watch public repositories for leaked secrets. If a key lands in Git history, revoke it immediately, because deleting the file does not remove the history. 9. Skipping timeouts. A hung connection with no timeout holds a worker forever. The project sets a 60-second default, and long generations should raise it deliberately rather than remove it. A quiet tenth pitfall deserves a mention: treating a model’s confident answer as verified fact. Validation catches malformed output and does nothing for a wrong answer in the right format. For anything that touches money, health, or legal text, keep a human or a deterministic check in the loop. Troubleshooting: 10 Errors and Fixes Each entry lists the symptom, the likely cause, and the fix. The HTTP status codes below follow standard conventions. DeepSeek’s docs carry the authoritative error table, so check it when a code behaves unexpectedly. - 401 Unauthorized or authentication failed. The key is missing, mistyped, revoked, or pasted with a trailing space or newline. Print len settings.api key to check for stray characters, regenerate the key if needed, and confirm the shell session actually exports the variable. - 402 or an insufficient-balance error. The prepaid balance reached zero. Top up in the console. No retry will fix it, which is why the retry list excludes this error. - 404 or model not found. The model string is wrong or retired. Reset DEEPSEEK MODEL to deepseek-flash . Names from September-era tutorials, including deepseek-v4-pro , can behave differently now that DeepSeek routes that name to Flash. - 429 Too Many Requests. You hit a rate limit. Lower concurrency in fanout.py the Semaphore value , keep the jittered backoff, and spread batch work over time. If you run the model through Cloudflare Workers AI instead, remember its separate default limits. - Server errors or long hangs. Provider-side trouble. Secondary reports described an outage lasting about five hours before the 1017 release, so plan for it: keep timeouts short, let the retry policy absorb brief blips, and add a fallback provider for critical paths. - JSONDecodeError or a Pydantic ValidationError in JSON mode. The reply is wrapped in a code fence, truncated by a low max tokens , or missing a field. The repair loop handles the first case. For truncation, raise max tokens . For missing fields, tighten the schema description in the system prompt. - Ledger shows zero tokens on streamed calls. stream options={'include usage': True} is absent, or a proxy strips the final usage chunk. Call the endpoint directly with the option set and compare. Some gateways need explicit configuration to forward that chunk. - Tool call arguments fail to parse. Models occasionally emit malformed argument strings. The loop returns an error object to the model, which usually self-corrects on the next round. If it keeps failing, simplify the schema, mark fewer fields as required, and add a one-line example to the tool description. - ModuleNotFoundError: No module named 'dslab' . You ran a script from the wrong folder or without -m . Run commands from the project root with python -m dslab.cli , and confirm the empty init .py exists. - SSL errors, proxy errors, or connection resets. Corporate proxies and outdated certificate bundles cause these. Upgrade certifi with pip, set HTTPS PROXY if your network requires it, and test with a bare curl to the base URL to separate network problems from code problems. When none of those match, log the full request ID and status code from the exception, then reproduce with the smallest possible call: one user message, no tools, no streaming. Add features back one at a time until the failure returns. That bisection finds the culprit faster than any amount of staring at a stack trace. Advanced Tips for Cost, Speed and Reach Shape Prompts for Cache Hits Put everything stable at the top: system rules, style guides, few-shot examples, reference documents. Put everything that varies at the bottom: the user’s question, the retrieved snippets. Keep the stable block byte-identical between calls, because a single changed character early in the prompt can invalidate the cached prefix. Log the hit ratio the ledger prints and treat anything under 50% on a repetitive workload as a bug to chase. Run Batch Work Off-Peak and in Parallel Peak pricing doubles every token, so batch jobs belong in the off-peak windows: weekends, 04:00 to 06:00 UTC, and after 10:00 UTC on weekdays. The asynchronous runner below fans prompts out with a semaphore that caps concurrency at 8, which keeps you under rate limits while still finishing a 1,000-prompt job in a fraction of the serial time. Rate cards also change, so keep them in one module where a price update is a one-line edit and a failing test reminds you to review it. python import asyncio from openai import AsyncOpenAI from .config import load settings from .pricing import Ledger async def run batch prompts: list str , concurrency: int = 8 - tuple list str , Ledger : settings = load settings client = AsyncOpenAI api key=settings.api key, base url=settings.base url, timeout=settings.timeout, max retries=3, ledger = Ledger gate = asyncio.Semaphore concurrency async def one prompt: str - str: async with gate: resp = await client.chat.completions.create model=settings.model, messages= {"role": "user", "content": prompt} , temperature=0, ledger.add resp.usage return resp.choices 0 .message.content or "" try: answers = await asyncio.gather one p for p in prompts finally: await client.close return list answers , ledger if name == " main ": out, led = asyncio.run run batch f"Give one fact about the number {n}." for n in range 1, 6 print out, sep="\n" print led.summary Reach the Same Model Through Cloudflare Workers AI Cloudflare’s changelog says DeepSeek V4.1 Flash 1017 became available on Workers AI on October 19, and it lists the model under a @cf/deepseek-ai identifier. According to Cloudflare’s changelog entry https://developers.cloudflare.com/changelog/post/2026-10-19-deepseek-v4.1-flash-workers-ai/ , using it requires a Workers Paid plan or prepaid AI Gateway credits, with default limits reported at 20 requests per minute on standard Workers AI billing and 50 requests per minute with prepaid AI Gateway credits. A second route gives you a fallback when the primary endpoint struggles. The cost is a different SDK call shape, different limits, and a different bill, so wrap both behind one function and test the failover path before an outage forces it. Self-hosting is the other route. DeepSeek reportedly open-sourced DeepInfer, the inference framework behind its production services, on October 20 details in our coverage of the DeepInfer release https://shattered.io/deepseek-deepinfer-open-source-inference-pressure-2026/ . Running your own inference takes GPU capacity and operations work, so it suits teams with steady volume or strict data rules and rarely suits a weekend prototype. Two more habits pay off in production. First, log the token counts and the ledger estimate for every request with a request ID, so a spike traces back to one caller in minutes. Second, add a canary: a scheduled job that sends one tiny, fixed prompt every few minutes and alerts on errors or latency jumps. Providers rarely announce brownouts, and a canary tells you before your users do. Security and Compliance Checks Before Production Key hygiene comes first. Rotate keys on a schedule and immediately after any suspected exposure, scope one key to one service, and set spending alerts. Keep keys out of client-side code entirely: a mobile app or browser bundle that carries a provider key hands it to every user who opens a network inspector. Route client traffic through your own backend, which holds the key, authenticates the user, and rate-limits per account. Policy risk is the second check. On October 20, 2026, the US administration barred DeepSeek from civilian federal agency systems, CNBC reported https://www.cnbc.com/2026/10/20/us-deepseek-federal-agency-ban.html . Our breakdown of the DHS ban under the 2018 law https://shattered.io/dhs-bans-deepseek-federal-systems-2018-law-2026/ covers the legal mechanics. If you build for government customers, contractors, or regulated industries, ask the customer’s compliance team about DeepSeek before you design around it, because a policy change can make a working integration unusable without any change in the code. Data handling is the third check. Anything you send to a hosted API leaves your network. Strip personal data, secrets, and customer identifiers from prompts unless a contract and a data-processing review say otherwise. Log prompts carefully, since logs become a second copy of that data. Reports say DeepSeek released weights alongside the 1017 update, and a self-hosted deployment is one path for teams that cannot send data out. Read the model card for license terms and hardware needs before you commit to that route. Prompt injection is the fourth. Any text a tool fetches from the web or a user uploads can carry instructions aimed at your model. Treat tool output as untrusted data, never concatenate it into the system prompt, restrict which tools a retrieval-driven agent can call, and require confirmation for any action with side effects. The Search and Fetch APIs make this concrete, because they pipe arbitrary web content straight into the model’s context. Finally, log what matters and nothing more. Model name, token counts, latency, status code, and request ID belong in the log. Full prompts and completions belong there only when you have a reason and a retention rule. FAQ What model name should I use for the DeepSeek API in October 2026? Use deepseek-flash . It points at the latest V4.1 Flash build, currently V4.1-Flash-1017, released October 17, 2026. DeepSeek’s changelog says to use that alias, and the API calling method did not change with the update. Is the DeepSeek API compatible with the OpenAI Python SDK? Earlier DeepSeek releases accepted the OpenAI chat-completions format, and this project relies on that by changing only the base URL and key. Confirm the current details on DeepSeek’s API docs page titled Your First API Call, since this guidance depends on that format staying stable. How much does the DeepSeek API cost? For deepseek-flash , off-peak rates are $0.003 per million cache-hit input tokens, $0.15 per million cache-miss input tokens, and $0.60 per million output tokens. Peak rates are double: $0.006, $0.30, and $1.20. Search and Fetch calls cost $0.75 per 1,000 successful requests, billed from November 1, 2026. When is the DeepSeek API at peak pricing? Peak applies Monday through Friday from 01:00 to 04:00 UTC and from 06:00 to 10:00 UTC, excluding Chinese public holidays, based on DeepSeek’s pricing notice. Everything else counts as off-peak at half the peak rate. Does deepseek-v4-pro still work? Since September 14, 2026, requests using deepseek-v4-pro route to V4.1 Flash and bill at Flash rates, a routing DeepSeek said would last until V4.1-Pro launches. Switch your configuration to deepseek-flash so the name matches what you actually get. Are the Search and Fetch APIs free? Not permanently. Per-request billing at $0.75 per 1,000 successful requests begins at 00:00 Beijing time on November 1, 2026. Until then, according to DeepSeek’s documentation, calls made through DeepSeek Harness, Claude Code, or Codex are free. Put a spending cap in front of both before the date arrives. How do I cut my DeepSeek API bill the most? Three levers matter most. Put stable instructions at the front of every prompt to raise cache hits. Run batch jobs off-peak. Cap tool rounds and retrieval calls so an agent bug cannot loop. The ledger in this project shows the effect of each change in dollars. Can US government contractors use the DeepSeek API? That depends on contract terms and agency policy. The US administration barred DeepSeek from civilian federal agency systems on October 20, 2026, according to CNBC. Ask your contracting officer or compliance team before building on the service for any federal work.