DeepSeek Integration Guide
What is DeepSeek?
Mixture-of-Experts models, OpenAI-compat API, MIT-licensed weights.
V3 is a 671B-parameter MoE with ~37B active per token; R1 is the reasoning variant that emits visible chain-of-thought before the final answer. The hosted API uses the same `/v1/chat/completions` schema as OpenAI — point your SDK at `https://api.deepseek.com` and most code Just Works. Function calling is supported. Weights are available on Hugging Face under MIT — you can run them yourself on an 8×H100 box, distill them, or fine-tune. For codeAmani, DeepSeek is the cost-optimisation lever: route the cheapest ~80% of traffic to V3, fall back to Claude when quality matters.
Five reasons DeepSeek is worth integrating
Open weights + cheap API + OpenAI-compatible schema = unusually low switching cost.
██████╗ ███████╗███████╗██████╗ ███████╗███████╗███████╗██╗ ██╗
██╔══██╗██╔════╝██╔════╝██╔══██╗██╔════╝██╔════╝██╔════╝██║ ██╔╝
██║ ██║█████╗ █████╗ ██████╔╝███████╗█████╗ █████╗ █████╔╝
██║ ██║██╔══╝ ██╔══╝ ██╔═══╝ ╚════██║██╔══╝ ██╔══╝ ██╔═██╗
██████╔╝███████╗███████╗██║ ███████║███████╗███████╗██║ ██╗
╚═════╝ ╚══════╝╚══════╝╚═╝ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝DeepSeek Integration Guide
Focus: Cost-effective AI inference and chain-of-thought reasoning — DeepSeek-V3 and DeepSeek-R1 via OpenAI-compatible SDK.
Overview
DeepSeek provides two flagship models: DeepSeek-V3 (fast, cost-effective chat/code) and DeepSeek-R1 (step-by-step chain-of-thought reasoning). Both are compatible with the OpenAI SDK — swap the base URL and API key, keep the same code. Used in codeAmani products as a cost-optimization alternative for tasks that don't require Anthropic's highest capability tier.
Here's the big picture — the same OpenAI SDK call simply points at DeepSeek and routes to one of two models:
Official Documentation
| Resource | URL |
|---|---|
| API Docs | https://platform.deepseek.com/api-docs |
| API Reference | https://platform.deepseek.com/api-docs/api/create-chat-completion |
| Models | https://platform.deepseek.com/api-docs/quick_start/pricing |
| OpenAI Compatibility | https://platform.deepseek.com/api-docs/quick_start/compatibility_guide |
SDK Setup
DeepSeek is OpenAI API-compatible — use the official OpenAI SDK with a custom base URL.
npm install openaiimport OpenAI from "openai";
const deepseek = new OpenAI({
apiKey: process.env.DEEPSEEK_API_KEY!,
baseURL: "https://api.deepseek.com",
});Models
| Model ID | Alias | Best For | Context |
|---|---|---|---|
deepseek-chat | DeepSeek-V3 | Fast chat, code generation, structured output | 64K tokens |
deepseek-reasoner | DeepSeek-R1 | Step-by-step reasoning, math, logic, analysis | 64K tokens |
Core Patterns
Standard Chat Completion
const response = await deepseek.chat.completions.create({
model: "deepseek-chat",
messages: [
{ role: "system", content: "You are a helpful assistant for codeAmani Labs." },
{ role: "user", content: "Summarize this M-Pesa transaction log." },
],
max_tokens: 1024,
});
console.log(response.choices[0].message.content);Reasoning (R1) — Chain-of-Thought
const response = await deepseek.chat.completions.create({
model: "deepseek-reasoner",
messages: [
{ role: "user", content: "Why is my Supabase RLS policy blocking authenticated users?" },
],
});
// R1 exposes its reasoning in reasoning_content before the final answer
const choice = response.choices[0];
// @ts-expect-error — deepseek-specific field
console.log("Reasoning:", choice.message.reasoning_content);
console.log("Answer:", choice.message.content);Streaming Response
const stream = await deepseek.chat.completions.create({
model: "deepseek-chat",
stream: true,
messages: [{ role: "user", content: prompt }],
});
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content ?? "";
process.stdout.write(delta);
}Next.js App Router Streaming Route
// app/api/ai/deepseek/route.ts
import OpenAI from "openai";
import { NextRequest } from "next/server";
const deepseek = new OpenAI({
apiKey: process.env.DEEPSEEK_API_KEY!,
baseURL: "https://api.deepseek.com",
});
export async function POST(req: NextRequest) {
const { messages } = await req.json();
const stream = await deepseek.chat.completions.create({
model: "deepseek-chat",
stream: true,
messages,
});
const encoder = new TextEncoder();
const readable = new ReadableStream({
async start(controller) {
for await (const chunk of stream) {
const text = chunk.choices[0]?.delta?.content ?? "";
if (text) controller.enqueue(encoder.encode(text));
}
controller.close();
},
});
return new Response(readable, {
headers: { "Content-Type": "text/plain; charset=utf-8" },
});
}AI Routing: When to Use DeepSeek
In codeAmani's AI routing strategy, DeepSeek slots in as a cost-optimization tier:
This decision flow shows exactly where DeepSeek earns its place alongside Anthropic — pick the right tier and you save cost without losing quality:
// lib/ai.ts
type TaskComplexity = "high" | "medium" | "low";
function selectModel(complexity: TaskComplexity, requiresReasoning: boolean) {
if (complexity === "high" || requiresReasoning) {
return { provider: "anthropic", model: "claude-sonnet-4-6" };
}
if (complexity === "medium") {
return { provider: "deepseek", model: "deepseek-chat" };
}
// Low complexity: fast classification / simple Q&A
return { provider: "anthropic", model: "claude-haiku-4-5-20251001" };
}| Task | Recommended Model |
|---|---|
| Complex reasoning, agents | claude-sonnet-4-6 |
| Step-by-step math / logic | deepseek-reasoner |
| Standard Q&A, summaries | deepseek-chat |
| Fast classification | claude-haiku-4-5-20251001 |
| Structured JSON output | gpt-4o |
Error handling, retries & fallback
The routing section above treats DeepSeek as a cost tier, not a hard dependency — so any call that hits DeepSeek must be able to fall back to Anthropic Claude when DeepSeek throttles or errors. DeepSeek's own docs explicitly suggest this: on a 429, they recommend you "temporarily switch to alternative LLM providers."
Documented status codes
These are the status codes DeepSeek documents on its error codes page. Treat the transient ones as retry-then-fallback, and the terminal ones as fail-fast (retrying won't help):
| Code | Meaning | Class | Action |
|---|---|---|---|
400 | Invalid request body format | terminal | Fix the request — do not retry |
401 | Authentication fails (wrong API key) | terminal | Fix DEEPSEEK_API_KEY |
402 | Insufficient balance | terminal | Top up; fall back immediately |
422 | Invalid parameters | terminal | Fix params — do not retry |
429 | Rate limit reached (concurrency limit) | transient | Back off, then fall back |
500 | Server error | transient | Retry after a brief wait |
503 | Server overloaded (high traffic) | transient | Retry after a brief wait |
DeepSeek does not publish a fixed requests-per-second limit. Instead it documents a per-user_id concurrency limit (rate limit docs); exceeding the number of in-flight connections is what returns 429. The docs give no prescribed backoff schedule, so the pattern below uses standard exponential backoff with jitter.
Try DeepSeek with backoff, then fall back to Claude
This mirrors the router in lib/ai.ts — selectModel chooses the tier, this wrapper makes the DeepSeek tier resilient. Terminal errors (4xx except 429) skip retries and fall straight through to Claude.
// lib/ai-resilient.ts
import OpenAI from "openai";
import Anthropic from "@anthropic-ai/sdk";
const deepseek = new OpenAI({
apiKey: process.env.DEEPSEEK_API_KEY!,
baseURL: "https://api.deepseek.com",
});
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! });
// Transient per DeepSeek docs: 429 (rate limit), 500 (server error), 503 (overloaded).
const RETRYABLE = new Set([429, 500, 503]);
const MAX_RETRIES = 3;
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
/**
* Run a prompt through DeepSeek with exponential backoff on transient errors,
* then fall back to Anthropic Claude if DeepSeek is unavailable or non-retryable.
*/
export async function completeWithFallback(prompt: string): Promise<string> {
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
try {
const res = await deepseek.chat.completions.create({
model: "deepseek-chat",
messages: [{ role: "user", content: prompt }],
max_tokens: 2048,
});
return res.choices[0]?.message.content ?? "";
} catch (err) {
// OpenAI SDK surfaces the HTTP status on err.status
const status = (err as { status?: number }).status;
// Non-retryable (400/401/402/422) or out of attempts → break to fallback.
if (!status || !RETRYABLE.has(status) || attempt === MAX_RETRIES) break;
// Exponential backoff with full jitter: ~0.5s, 1s, 2s (+ jitter).
const base = 500 * 2 ** attempt;
await sleep(base + Math.random() * base);
}
}
// Fallback tier — Anthropic Claude (consistent with lib/ai.ts router).
const msg = await anthropic.messages.create({
model: "claude-haiku-4-5-20251001",
max_tokens: 2048,
messages: [{ role: "user", content: prompt }],
});
const block = msg.content[0];
return block.type === "text" ? block.text : "";
}Gotcha — empty lines are not errors. While a request waits to be scheduled, DeepSeek keeps the TCP connection alive by sending empty lines (non-streaming) or
: keep-aliveSSE comments (streaming) rather than data. The OpenAI SDK handles these for you, but if you parse the raw HTTP/SSE stream yourself, skip those blank/comment lines — do not treat them as a malformed response or trip your retry logic on them. Connections also close after ~10 minutes if inference never starts, so set a client timeout below that and let the fallback path catch it.
JSON / Structured Output
const response = await deepseek.chat.completions.create({
model: "deepseek-chat",
response_format: { type: "json_object" },
messages: [
{
role: "system",
content: "Respond only with valid JSON.",
},
{
role: "user",
content: "Extract: name, amount, phone from this SMS: 'Confirmed. Ksh500 sent to 0712345678 on 12/5/26'",
},
],
});
const data = JSON.parse(response.choices[0].message.content ?? "{}");
// { name: null, amount: 500, phone: "0712345678" }Environment Variables
# Required
DEEPSEEK_API_KEY=sk-...
# No separate base URL needed — set in code: https://api.deepseek.comCost Reference
DeepSeek is significantly cheaper than GPT-4o / Claude for many tasks. Check current pricing at platform.deepseek.com — rates change frequently.
| Model | Approx Input | Approx Output |
|---|---|---|
deepseek-chat (V3) | ~$0.07 / 1M tokens | ~$0.28 / 1M tokens |
deepseek-reasoner (R1) | ~$0.14 / 1M tokens | ~$2.19 / 1M tokens |
R1 reasoning tokens (thinking) are also billed. Use for tasks where quality justifies cost.
Troubleshooting
| Issue | Fix |
|---|---|
Authentication fails | Verify DEEPSEEK_API_KEY — starts with sk- |
model not found | Use exact IDs: deepseek-chat or deepseek-reasoner |
reasoning_content undefined | Only available on deepseek-reasoner model |
| Streaming stops mid-response | Check max_tokens — default is low; increase to 4096+ |
TypeScript errors on reasoning_content | Use // @ts-expect-error — field not in OpenAI types |