DeepSeek Vision API: Sending Images to deepseek-v4-flash-vision-exp
By the Deep Seek AI editorial desk · August 23, 2026 · 8 min read
In short: DeepSeek's vision model deepseek-v4-flash-vision-exp accepts images alongside text through the same OpenAI-compatible Chat Completions endpoint you already use. Three input methods, a hard ceiling of 384 tokens per image, and up to 600 images per request.
One model only — and it is experimental
Images work exclusively with deepseek-v4-flash-vision-exp. Any other DeepSeek model returns a 400 with "This model does not support image". The -exp suffix signals experimental status, so keep the model string in configuration, not scattered through your code.
What DeepSeek vision can do
The vision model takes images in the same message as your text prompt, which makes the obvious workloads immediately available: describing photographs, reading text out of screenshots, and analysing charts and diagrams. Image format is detected from the actual file content — JPEG, PNG, GIF and WebP — not from the extension or the declared MIME type, so a mislabelled upload still works as long as the bytes are valid.
The interesting part for anyone building a pipeline is the batch ceiling: a single request may carry up to 600 images. Combined with the flat token cap per image, document triage and screenshot classification become genuinely cheap rather than merely possible.
The three ways to send an image
All three use the standard OpenAI-compatible Chat Completions format, where content is an array of blocks instead of a plain string. The same three methods exist in the Responses API, where images travel in input_image content parts. Base URL for everything below: https://api.deepseek.com.
1. Base64 inline (local files)
Encode the file and embed it as a data: URL. Simplest option for local images; the encoded bytes count toward the 48 MiB request body limit.
import base64
from openai import OpenAI
client = OpenAI(api_key="<DeepSeek API Key>", base_url="https://api.deepseek.com")
with open("image.jpg", "rb") as f:
b64 = base64.b64encode(f.read()).decode("utf-8")
response = client.chat.completions.create(
model="deepseek-v4-flash-vision-exp",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "What is in this image?"},
{
"type": "image_url",
"image_url": {"url": f"data:image/jpeg;base64,{b64}"},
},
],
}
],
)
print(response.choices[0].message.content)2. External image URL
Pass a publicly reachable http(s) link and DeepSeek downloads the image for you. The URL must be at most 8,192 characters, the file at most 32 MiB, and the download must finish inside 60 seconds — otherwise fall back to base64 or the Files API.
response = client.chat.completions.create(
model="deepseek-v4-flash-vision-exp",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "Describe this image."},
{
"type": "image_url",
"image_url": {"url": "https://example.com/image.jpg"},
},
],
}
],
)3. Files API reference
Upload once, reference the returned file_id (shaped file-api-…) as often as you like. This is the right choice for reused images, and the only route for images above 32 MiB — Files API images may reach 64 MiB and skip the per-image inline check.
response = client.chat.completions.create(
model="deepseek-v4-flash-vision-exp",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "What is in this image?"},
{"type": "file", "file_id": "file-api-xxxxxxxxxxxxxxxx"},
],
}
],
)A file block can alternatively carry the image inline as base64 through file_data plus filename. file_data and file_id are mutually exclusive.
Detail levels: the cheapest knob you have
For image_url inputs you can set an optional detail field that controls how the image is processed before inference.
| Value | Behaviour |
|---|---|
low | The image is downscaled to 512×512 before inference. Faster and cheaper when fine visual detail does not matter. |
high | Keeps the original image. Provided for OpenAI compatibility; equivalent to original. |
original | Keeps the original image. |
auto | Automatic selection. Currently equivalent to original. |
If your task is coarse recognition — "is this a receipt or an invoice?" — set detail: "low" and let DeepSeek downscale to 512×512. Keep original for small print, dense tables and OCR-style work.
Token usage: 384 tokens per image, maximum
Images become tokens based on their dimensions, billed together with your text tokens. Before inference every image is resized automatically: below roughly 384×384 total pixels it is scaled up preserving aspect ratio; larger images are scaled down preserving aspect ratio to roughly the pixel count of an 800×800 image.
The practical consequence is an upper bound of 384 tokens per image. A 2000×2000 photo and a 5000×5000 scan cost the same, so there is no billing reason to downscale before upload — only a bandwidth one. In multi-image requests each image is counted independently under exactly the same rule; there is no separate multi-image formula.
Every documented limit
| Limit | Value |
|---|---|
| Supported image formats | JPEG, PNG, GIF, WebP (detected from file content, not the file name or MIME type) |
| External URL length | 8,192 characters |
| Request body size | 48 MiB |
| Max single image (base64 or external URL) | 32 MiB |
| Max single image (Files API file_id) | 64 MiB |
| Max images per request | 600 |
| Max total image size per request | 64 MiB without file_id images; up to 200 MiB including file_id images |
| Max image dimension | 8,192 px per side — drops to 4,096 px per side when a request contains 15 or more images |
| External image download timeout | 60 seconds |
Restrictions that will bite you
- User messages only. An image inside a
systemorassistantmessage returns a400. If you replay conversation history containing assistant-side images, strip them. - Vision model only. Non-vision models reject images with a
400and "This model does not support image". - Reserved placeholder token. User text containing the reserved image placeholder token is rejected with a
400. - Dimension cliff at 15 images. The 8,192 px per-side maximum drops to 4,096 px per side as soon as a request contains 15 or more images — a surprisingly easy way to break a working batch job by adding one more page.
Using images through the Anthropic-compatible endpoint
If your stack already speaks Anthropic, point base_url at https://api.deepseek.com/anthropic and use the /messages shape. The only difference is the image block: instead of image_url, Anthropic uses an image block with a source object whose type is base64, url or file — the three variants mirroring the OpenAI methods above.
import anthropic
# ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic
client = anthropic.Anthropic()
message = client.messages.create(
model="deepseek-v4-flash-vision-exp",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "What is in this image?"},
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": "<BASE64_DATA>",
},
},
],
}
],
)
print(message.content)A sane default configuration
- Batch, but stay under 15 images per request unless you are certain every page is within 4,096 px per side.
- Prefer the Files API for anything reused — the same invoice template re-uploaded 500 times is pure wasted bandwidth.
- Default to
detail: "low"for classification and routing; escalate tooriginalonly on the pages that need reading. - Budget 384 input tokens per image as a worst case — it is the actual ceiling, so your estimate can never be short.
Frequently asked questions
This is an independent, fan-run guide. It is not affiliated with, endorsed by or operated by DeepSeek. Values above are taken from DeepSeek's official vision documentation on the date shown — verify against the official docs before shipping.