DeepSeek-V4-Flash-Vision-Exp
| Tier | Input | Output | Cached input |
|---|---|---|---|
PriorityLearn more | 116.5 | 349.3 | 3.8 |
FlexLearn more | 62.1 | 186.3 | 1.98 |
DeepSeek V4 Flash Vision Exp is an experimental vision-enabled build of V4 Flash, and it takes an unusual approach to image cost: every image is capped at 384 tokens regardless of its resolution. A two-megapixel scan and a twenty-five-megapixel one bill identically, because both are resized to the same working size before inference. That turns document processing from an open-ended cost into arithmetic you can do before sending the request. It accepts up to six hundred images per call, matches the text model it is built on across agents, reasoning, and world knowledge, and holds the same million-token context. On DEVUP AI it is the choice when the workload is pages rather than paragraphs.

Use DeepSeek-V4-Flash-Vision-Exp in VS Code, Cursor and Antigravity
DEVUP Code, the DEVUP AI extension, is free to install. Requests are paid from your DEVUP balance.
DeepSeek V4 Flash Vision Exp
Overview
DeepSeek V4 Flash Vision Exp is an experimental vision-enabled build of DeepSeek V4 Flash. It is the same Mixture-of-Experts model — 284B total parameters, 13B activated per token, with a one-million-token context window — with image understanding added.
On text it is on par with the model it is built from: agents, reasoning, and world knowledge are unchanged. On agent benchmarks that require visual understanding, DeepSeek reports a significant leap over the text-only version.
What distinguishes it operationally is not the vision quality but how images are priced in tokens. Every image is capped at 384 tokens no matter its resolution. That single property changes what kinds of workloads are viable.
Image Token Cost Is Bounded
Before inference, every image is automatically resized:
- Images below roughly 384×384 total pixels are scaled up, preserving aspect ratio.
- Larger images are scaled down, preserving aspect ratio, to roughly the pixel count of an 800×800 image.
The consequence is a hard ceiling: 384 tokens per image, maximum. A 2000×2000 image and a 5000×5000 image consume the same number of tokens after resizing. With multiple images, each is counted independently under the same rule — there is no separate multi-image calculation.
This makes image cost calculable in advance rather than discovered afterwards:
def max_image_tokens(image_count: int) -> int:
"""Upper bound on tokens consumed by images in a single request."""
return image_count * 384
# A 40-page scanned document, one page per image:
max_image_tokens(40) # 15,360 tokens — known before the request is sentMost vision models bill by resolution, which means a high-resolution page and a thumbnail differ by an order of magnitude and the total is only known once the response returns. Here it is arithmetic.
Trading detail for cost
The detail field on image_url inputs controls processing:
| Value | Behaviour |
|---|---|
low | Downscaled to 512×512 before inference — faster and cheaper when fine detail does not matter |
high | Keeps the original image (equivalent to original) |
original | Keeps the original image |
auto | Automatic; currently equivalent to original |
Use low for classification, layout detection, or anything where you are not reading small text.
Keep the default for documents.
Sending Images
Three methods, all using the standard OpenAI-compatible content-array format.
Base64 inline
Simplest for local files. Counts toward the 48 MiB request body limit.
{
"type": "image_url",
"image_url": { "url": "data:image/jpeg;base64,<BASE64_DATA>" }
}External URL
The model downloads the image. The URL must be at most 8192 characters, the file at most 32 MiB, and the download must complete within 60 seconds.
{
"type": "image_url",
"image_url": { "url": "https://example.com/image.jpg" }
}Uploaded file reference
Best when the same image is reused across requests, or when it would push the body over the inline limit. Files referenced this way may be up to 64 MiB and are not subject to the 32 MiB per-image check.
{ "type": "file", "file_id": "file-api-xxxxxxxxxxxxxxxx" }Limits
| Limit | Value |
|---|---|
| Supported formats | JPEG, PNG, GIF, WebP |
| Format detection | From file content — not filename or declared MIME type |
| External URL length | 8,192 characters |
| External URL download timeout | 60 seconds |
| Request body size | 48 MiB |
| Max single image (inline or URL) | 32 MiB |
| Max single image (uploaded file) | 64 MiB |
| Max images per request | 600 |
| Max total image size per request | 64 MiB inline; up to 200 MiB including uploaded files |
| Max image dimension | 8,192 px per side — drops to 4,096 px when a request contains 15 or more images |
That last row is easy to miss and produces a confusing failure. A page that passes on its own will be rejected once it is part of a fifteen-image batch. If your pipeline batches variable numbers of pages, size against 4,096 px, not 8,192.
Restrictions
Three documented cases that return HTTP 400:
- Images in
systemorassistantmessages. Images are supported inusermessages only. Any middleware that relocates image content into a system preamble will break this. - Images sent to a non-vision model. Returns
"This model does not support image". Route by capability, not by model family — the text model this one is built from will reject the same payload. - User text containing the reserved image placeholder token. Sanitise user-supplied text before embedding it in a prompt.
Capabilities
| Capability | Value |
|---|---|
input_types | text, image |
output_types | text |
context_window | 1048576 |
max_output_tokens | 131072 |
max_images_per_request | 600 |
max_tokens_per_image | 384 |
image_formats | jpeg, png, gif, webp |
image_message_roles | user only |
reasoning | Inherited from the base model — low, high, max |
reasoning_field | reasoning_content — separate from content |
streaming | Supported |
tool_calling | Supported |
structured_output | Supported — JSON schema |
requires_prompt | Yes — text prompt required, image optional |
status | Experimental |
Recommended Use Cases
- High-volume document processing — the workload the token cap makes viable. Invoices, receipts, forms, contracts, scanned archives, where cost per page must be predictable.
- Screenshot and interface reading — extracting text and structure from UI captures.
- Chart and diagram analysis — reading figures out of reports rather than out of tables.
- Multimodal agent workflows — where DeepSeek reports the largest gain over the text-only model. Agents that look at output, judge it, and act.
- Batch page ingestion — up to 600 images in a single request, subject to the dimension rule above.
- Mixed text-and-visual pipelines — text performance is unchanged from the base model, so one model covers both paths.
Using DeepSeek V4 Flash Vision Exp on DEVUP AI
Base URL: https://api.devupai.com/v1 · Model ID: deepseek-ai/DeepSeek-V4-Flash-Vision-Exp
Single image — Python
import os
import base64
from openai import OpenAI
client = OpenAI(
api_key=os.environ["DEVUP_API_KEY"],
base_url="https://api.devupai.com/v1",
)
with open("invoice.png", "rb") as handle:
encoded = base64.b64encode(handle.read()).decode("utf-8")
response = client.chat.completions.create(
model="deepseek-ai/DeepSeek-V4-Flash-Vision-Exp",
messages=[
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {"url": f"data:image/png;base64,{encoded}"},
},
{
"type": "text",
"text": (
"Extract every line item as JSON with fields: description, quantity, "
"unit_price, line_total. Report the currency exactly as printed. "
"Use null for any illegible field — do not infer."
),
},
],
}
],
temperature=1.0,
top_p=0.95,
max_tokens=16384,
)
print(response.choices[0].message.content)Note the image sits in a user message. Placing it in a system message returns a 400.
Instructing the model to return null rather than infer is not optional in document work. An
invented figure is undetectable downstream.
Multi-page document — Python
The workload the token cap exists for. Note the batch size guard.
import base64
from pathlib import Path
MAX_TOKENS_PER_IMAGE = 384
DIMENSION_LIMIT_SMALL_BATCH = 8192
DIMENSION_LIMIT_LARGE_BATCH = 4096
LARGE_BATCH_THRESHOLD = 15
def encode_page(path: Path) -> dict:
"""Build an image content block from a local file."""
data = base64.b64encode(path.read_bytes()).decode("utf-8")
return {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{data}"}}
pages = sorted(Path("scans").glob("*.png"))
# The per-side dimension limit tightens once a request carries 15 or more images.
dimension_limit = (
DIMENSION_LIMIT_LARGE_BATCH
if len(pages) >= LARGE_BATCH_THRESHOLD
else DIMENSION_LIMIT_SMALL_BATCH
)
print(f"{len(pages)} pages — resize any side above {dimension_limit}px before sending")
print(f"Image token ceiling for this request: {len(pages) * MAX_TOKENS_PER_IMAGE:,}")
content = [encode_page(p) for p in pages]
content.append(
{
"type": "text",
"text": (
"These are consecutive pages of one contract. Identify every payment obligation, "
"citing the page number and clause reference exactly as printed. "
"Report nothing you cannot cite."
),
}
)
response = client.chat.completions.create(
model="deepseek-ai/DeepSeek-V4-Flash-Vision-Exp",
messages=[{"role": "user", "content": content}],
temperature=1.0,
top_p=0.95,
max_tokens=32768,
)
print(response.choices[0].message.content)Low-detail mode for classification
When you are sorting pages rather than reading them, downscale.
response = client.chat.completions.create(
model="deepseek-ai/DeepSeek-V4-Flash-Vision-Exp",
messages=[
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {
"url": f"data:image/png;base64,{encoded}",
"detail": "low",
},
},
{
"type": "text",
"text": "Classify this page: invoice, delivery note, contract, or other. One word.",
},
],
}
],
temperature=1.0,
top_p=0.95,
max_tokens=64,
)Node.js — DEVUP AI SDK
npm install devupaiimport DevupAI from "devupai";
import { readFileSync } from "node:fs";
const client = new DevupAI({
apiKey: process.env.DEVUP_API_KEY,
});
const encoded = readFileSync("screenshot.png").toString("base64");
const response = await client.chat.completions.create({
model: "deepseek-ai/DeepSeek-V4-Flash-Vision-Exp",
messages: [
{
role: "user",
content: [
{
type: "image_url",
image_url: { url: `data:image/png;base64,${encoded}` },
},
{
type: "text",
text:
"Identify every interactive element in this interface. For each, give its label, " +
"its type, and its approximate position. Report nothing you cannot actually see.",
},
],
},
],
temperature: 1.0,
top_p: 0.95,
max_tokens: 16384,
});
console.log(response.choices[0].message.content);Streaming with usage
stream = client.chat.completions.create(
model="deepseek-ai/DeepSeek-V4-Flash-Vision-Exp",
messages=[{"role": "user", "content": content}],
temperature=1.0,
top_p=0.95,
max_tokens=32768,
stream=True,
stream_options={"include_usage": True},
)
for chunk in stream:
if not chunk.choices:
if chunk.usage:
print(f"\n\nTokens — in: {chunk.usage.prompt_tokens}, out: {chunk.usage.completion_tokens}")
continue
delta = chunk.choices[0].delta
if getattr(delta, "content", None):
print(delta.content, end="", flush=True)Compare prompt_tokens against your predicted ceiling. On this model the two should agree
closely, which makes it a useful check that your resize and batching assumptions hold.
Delegating access with a scoped JWT
Document endpoints are frequently public-facing, which is exactly where an unrestricted key should never be. Issue a token limited to this model with an expiry and a spending limit:
curl -X POST "https://api.devupai.com/v1/scoped-jwt" \
-H "Authorization: Bearer $DEVUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"api_key_name": "auto",
"models": ["deepseek-ai/DeepSeek-V4-Flash-Vision-Exp"],
"expires_delta": 3600,
"spending_limit": 200
}'The returned token is used exactly like an API key in the Authorization header. Requests for
any other model, or past the expiry or spending limit, are rejected — a hard ceiling on what an
uploaded batch can consume.
Recommended Generation Parameters
| Parameter | Value |
|---|---|
temperature | 1.0 |
top_p | 0.95 |
reasoning_effort | Inherited from the base model — set it explicitly |
DeepSeek's published evaluations for this model use the maximum effort level with
temperature = 1.0 and top_p = 0.95.
Best Practices
- Compute your image token cost before sending. Image count × 384 is the ceiling; there is no reason to be surprised by it.
- Keep images in
usermessages. Nowhere else works. - Size against 4,096 px if a request may carry 15 or more images. The dimension limit tightens at that threshold.
- Use
detail: "low"for classification and layout work. Reserve full detail for reading. - Do not pre-extract text from documents. Send the page as an image; layout, tables, and figures are part of what the model reads.
- Instruct the model to return
nullrather than infer on any extraction task. - Sanitise user-supplied text before embedding it in a prompt — reserved placeholder tokens are rejected.
- Route by capability, not model family. The text-only sibling rejects images outright.
- Verify format by content, not extension. A
.pngthat is actually a BMP will fail.
Limitations
- Experimental. DeepSeek labels this model experimental. Behaviour, availability, and interface may change without notice; treat it accordingly in production planning.
- Text output only. Images in, text out. No image generation.
- Images only, no video or audio.
- Resolution is capped by design. Every image is resized to roughly 800×800 before inference. Very fine detail — small print, dense tables at low contrast, fine engineering drawings — may be lost. The token cap and the detail ceiling are the same mechanism.
systemandassistantmessages cannot carry images.- The dimension limit is conditional on how many images a request carries, which makes it easy to pass in testing and fail in production.
- Not a safety layer. Apply your own moderation and validation before acting on model output in a production system.