Qwen3-VL-30B-A3B-Instruct
| Tier | Input | Output | Cached input |
|---|---|---|---|
PriorityLearn more | 81 | 324 | — |
FlexLearn more | 43.2 | 172.8 | — |
Qwen3-VL-30B-A3B-Instruct runs the same vision stack as the flagship of its series on roughly three billion active parameters. DeepStack, interleaved multi-axis positional encoding, timestamp-grounded event localisation, and OCR across 32 languages — all present, none reduced. What scales down is the language reasoning over what it sees, not the seeing itself. That combination is unusual and it decides the workload: document archives, receipt pipelines, video indexing at volume, where per-page cost determines whether the corpus gets processed at all rather than how well.

Qwen3-VL-30B-A3B-Instruct
The same eyes as the flagship. A seventh of the compute.
What Does Not Shrink
The Qwen3-VL series documents its vision capabilities identically across every size, and that is the fact worth leading with.
| 30B-A3B | 235B-A22B | |
|---|---|---|
| Active per token | ~3B | ~22B |
| DeepStack | ✅ | ✅ |
| Interleaved-MRoPE | ✅ | ✅ |
| Timestamp alignment | ✅ | ✅ |
| OCR languages | 32 | 32 |
| Context | 262,144 | 262,144 |
The perception is the same. The reasoning over it is not.
Which means the question is not "how well does it see." It is "how hard is the question you are asking about what it saw."
Transcribe this invoice is a perception task. Both models read the same characters.
Reconcile these three invoices against the contract and explain the discrepancy is a language task, and that is where twenty-two billion active parameters do something three billion cannot.
Three Vision Mechanisms
All present at this size, and each solves a different problem.
DeepStack
Fuses multi-level ViT features to capture fine-grained details and sharpen image–text alignment.
A vision transformer processes an image through many layers. The conventional approach hands the language model the final layer only — which holds semantics and has discarded most of what earlier layers saw.
Early layers carry edges, texture, small structure. Late layers carry meaning. Reading only the last one produces a model that knows what the photograph shows and has lost the fine print inside it.
DeepStack reads several levels. That is why small text in a photograph survives.
Interleaved-MRoPE
Full-frequency allocation over time, width, and height.
Video has three positional axes. A scheme handling two well and the third as an afterthought produces a model that describes frames rather than reasoning across them. Full-frequency allocation across all three is what long-horizon video reasoning requires.
Text–Timestamp Alignment
Moves beyond T-RoPE to precise, timestamp-grounded event localization.
"At what second does this happen" rather than "what happens in this video." Localisation rather than description — and on a model cheap enough to run across an archive, that turns a video library into a searchable index.
OCR Built for Photographs
32 languages, up from 19. And the robustness claims name conditions rather than accuracy:
Low light. Blur. Tilt.
Those are the conditions of a photograph taken by a person — a receipt on a table, an invoice shot at an angle, a sign in a dim room. Not the conditions of a scan.
Also documented: rare and ancient characters, domain jargon, and improved long-document structure parsing.
That last one is what separates OCR output from usable data. Reading characters is one problem; recognising that a page has a header, a table with six columns, and a footnote is another — and it is the one that decides whether the result can go into a database.
Three Billion Active Parameters Changes the Workload
The number that decides what this model is for.
Vision work is high-volume by nature. A document archive is thousands of pages. A receipt pipeline is thousands of photographs a day. A video library is hundreds of hours.
On a flagship model, per-item cost is what decides whether the archive gets processed at all. Not how well — whether.
At three billion active parameters, that calculation changes. The same OCR, the same structure parsing, the same timestamp localisation, at a fraction of the compute per item.
And the pairing that follows. Run this model across everything. Escalate the cases it flags — low-confidence transcriptions, unusual documents, contradictions — to the flagship in the same series. Same three vision mechanisms on both sides, same request shape, one changed model identifier.
That is a real pipeline rather than a compromise, because the perception does not degrade at the first stage. Only the reasoning does, and the first stage is not doing reasoning.
Specifications
| Model ID | Qwen/Qwen3-VL-30B-A3B-Instruct |
| Architecture | Mixture-of-Experts vision-language |
| Model class | Qwen3VLMoeForConditionalGeneration |
| Total parameters | ~30B |
| Activated per token | ~3B |
| Context length | 262,144 tokens |
| Input | Text, image, video |
| Output | Text |
| OCR languages | 32 |
| Vision mechanisms | DeepStack, Interleaved-MRoPE, text–timestamp alignment |
| Thinking | Not supported — Instruct edition |
| Licence | Apache 2.0 |
| Developer | Qwen Team, Alibaba |
The series spans dense and MoE architectures from edge to cloud, with Instruct and reasoning-enhanced Thinking editions at each size.
This is the Instruct edition. It answers directly. A Thinking edition exists at this size for work where deliberation over an image earns its cost.
Official FP8 and GGUF builds are published, with community AWQ and lower-bit quantisations alongside.
Capabilities
| Capability | Value |
|---|---|
input_types | text, image, video |
output_types | text |
context_window | 262144 |
ocr_languages | 32 |
video_localization | Timestamp-grounded |
reasoning | Not supported — Instruct edition |
streaming | Supported |
tool_calling | Supported |
structured_output | Supported |
multi_image | Supported |
requires_prompt | Yes — text prompt required, media optional |
Using Qwen3-VL-30B-A3B on DEVUP AI
Base URL: https://api.devupai.com/v1 · Model ID: Qwen/Qwen3-VL-30B-A3B-Instruct
Python
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["DEVUP_API_KEY"],
base_url="https://api.devupai.com/v1",
)
response = client.chat.completions.create(
model="Qwen/Qwen3-VL-30B-A3B-Instruct",
messages=[
{"role": "user", "content": "Hello world!"}
],
max_tokens=1024,
)
print(response.choices[0].message.content)Node.js
import DevupAI from "devupai";
const client = new DevupAI({
apiKey: process.env.DEVUP_API_KEY,
});
async function main() {
const response = await client.chat.completions.create({
model: "Qwen/Qwen3-VL-30B-A3B-Instruct",
messages: [{ role: "user", content: "Hello world!" }],
max_tokens: 1024,
});
console.log(response.choices[0].message.content);
}
main();cURL
curl -X POST "https://api.devupai.com/v1/chat/completions" \
-H "Authorization: Bearer $DEVUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen3-VL-30B-A3B-Instruct",
"messages": [
{ "role": "user", "content": "Hello world!" }
],
"max_tokens": 1024
}'A Document Pipeline at Volume
The workload this model exists for, written to survive a run of several thousand.
import base64
import json
from pathlib import Path
SCANS = Path("receipts")
OUTPUT = Path("extracted")
OUTPUT.mkdir(exist_ok=True)
PROMPT = (
"Transcribe this receipt. Return a single JSON object with keys: merchant, date, currency, "
"total, line_items (an array of {description, quantity, amount}). Use null for any field the "
"receipt does not show. Where a value is present but you cannot read it cleanly, use the string "
"\"UNREADABLE\" rather than guessing. Reply with JSON only."
)
def encode(path: Path) -> str:
return base64.b64encode(path.read_bytes()).decode("utf-8")
for path in sorted(SCANS.glob("*.jpg")):
target = OUTPUT / f"{path.stem}.json"
if target.exists():
continue # already done — never pay twice
try:
response = client.chat.completions.create(
model="Qwen/Qwen3-VL-30B-A3B-Instruct",
messages=[
{
"role": "user",
"content": [
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{encode(path)}"}},
{"type": "text", "text": PROMPT},
],
}
],
max_tokens=4096,
temperature=0.1,
)
data = json.loads(response.choices[0].message.content)
except Exception as exc:
print(f"{path.name}: {type(exc).__name__} — {exc}")
continue # one bad file must not stop the run
target.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
unreadable = json.dumps(data).count("UNREADABLE")
print(f"{path.name}: {unreadable} unreadable field(s)")Three lines do the operational work.
Skip-if-exists. A run that dies at file 3,400 of 5,000 resumes rather than restarting.
Catch-and-continue. One corrupted image should not end the job.
Counting UNREADABLE. That number is your escalation signal — files with any unreadable field are
exactly the ones worth sending to the larger model, and you get the list for free.
Escalating What the First Pass Flags
The two-tier pattern the shared vision stack makes possible.
FAST = "Qwen/Qwen3-VL-30B-A3B-Instruct"
DEEP = "Qwen/Qwen3-VL-235B-A22B-Instruct"
def extract(path: Path, model: str) -> dict:
"""Run the same extraction on either tier — identical request shape."""
response = client.chat.completions.create(
model=model,
messages=[
{
"role": "user",
"content": [
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{encode(path)}"}},
{"type": "text", "text": PROMPT},
],
}
],
max_tokens=4096,
temperature=0.1,
)
return json.loads(response.choices[0].message.content)
result = extract(path, FAST)
if "UNREADABLE" in json.dumps(result):
result = extract(path, DEEP)
result["_escalated"] = TrueThe request shape is identical across tiers. One function, one changed argument — no second integration and no divergent prompt to maintain.
Escalate on a signal, not on a guess. Unreadable fields, missing required values, or a document type your first pass does not recognise. Escalating everything defeats the purpose; escalating nothing defeats the safety net.
Record which tier produced each result. Six months from now, knowing whether a figure came from the fast pass or the careful one is the difference between a spot check and a re-run.
Video Indexing
Where timestamp alignment plus low per-item cost becomes a product.
response = client.chat.completions.create(
model="Qwen/Qwen3-VL-30B-A3B-Instruct",
messages=[
{
"role": "user",
"content": [
{"type": "video_url", "video_url": {"url": video_url}},
{
"type": "text",
"text": (
"List every distinct topic discussed or shown, with the timestamp in seconds "
"where it begins. Describe only what is visible or audible. If the video "
"covers one topic throughout, say so with a single entry."
),
},
],
}
],
max_tokens=8192,
temperature=0.2,
)Indexing a library is the use case. One pass per video produces timestamps; those timestamps become a search index; a query returns a moment rather than a file.
On a flagship model that is a project. At three billion active parameters it is a batch job.
Video consumes input tokens rapidly. Measure one short clip before processing an archive — the 262,144-token window fills faster than a minute count suggests, and discovering that at file two hundred is expensive.
Multi-Page Documents
content = []
for index, path in enumerate(sorted(Path("contract_pages").glob("*.jpg")), start=1):
content.append({"type": "text", "text": f"Page {index}:"})
content.append({"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{encode(path)}"}})
content.append({
"type": "text",
"text": (
"Transcribe each page, preserving its structure. Label each transcription with its page "
"number. Do not summarise or merge pages."
),
})
response = client.chat.completions.create(
model="Qwen/Qwen3-VL-30B-A3B-Instruct",
messages=[{"role": "user", "content": content}],
max_tokens=32768,
)Labelling each page before it appears gives the model something to cite and gives you something to verify against.
"Do not summarise or merge" is the operative instruction on a transcription task. A model asked to process several pages will helpfully condense them unless told not to — and a condensed contract is not a transcription.
Keep the analysis on the larger model. Cross-page contradiction and reconciliation are language tasks; transcription is not. This model does the second at a fraction of the cost.
It Answers Directly
The Instruct edition — no reasoning blocks, no thinking mode, no reasoning_content field.
max_tokens covers the answer alone, which matters on document work where a full transcription is
already a large output.
Latency tracks input and output size, not question difficulty — which is what makes throughput calculable on a batch pipeline.
And a hard visual question gets a direct answer rather than a worked one. The Thinking edition at this size exists for those, and it is a separate model.
Self-Hosting
The model class is Qwen3VLMoeForConditionalGeneration — distinct from the text models in this
generation, and a common cause of loading errors.
Enable flash attention 2. Qwen recommend it explicitly for acceleration and memory saving, especially in multi-image and video scenarios — which is where this model lives.
Enable expert parallelism on the MoE builds.
Reduce context length first on out-of-memory. Community configurations default to 32,768 rather than the full figure, which tells you how often that comes up — and at three billion active parameters, the weights are not what fills the card. The context is.
Official FP8 and GGUF builds are published, with community AWQ alongside. At this size the quantised builds are genuinely accessible rather than a data-centre proposition.
Where It Fits
High-volume document processing — invoices, receipts, forms, identity documents — across 32 languages.
Photograph-based OCR, where low light, blur, and tilt are the normal conditions rather than edge cases.
Video indexing at archive scale, using timestamp localisation on a model cheap enough to run across everything.
The first tier of a two-model pipeline, escalating flagged items to the larger model in the same series.
Screen recording analysis for support and QA tooling.
Self-hosted deployment, where the footprint makes local vision work practical.
Not for complex reasoning about what it saw. That is the larger model, or the Thinking edition at this size.
Not for image generation. It reads; it does not draw.
Practical Notes
Send photographs as taken — the model is built for low light, blur, and tilt.
Ask for structure, not just characters.
Use an explicit UNREADABLE marker rather than allowing reconstruction, and count it as your
escalation signal.
Build batch runs with skip-if-exists and catch-and-continue from the start.
Label pages and images so answers can cite them.
Measure video token consumption before processing an archive.
Keep reasoning tasks on the larger model — the vision does not degrade here, the language does.
If self-hosting: the MoE class, flash attention 2, expert parallelism, and reduced context on OOM.
Limitations
Roughly three billion active parameters. The vision stack is the flagship's; the reasoning over it is not.
No thinking mode. The Instruct edition answers directly; a Thinking edition exists at this size.
Text output only. Three input modalities, one output modality.
Video fills the context quickly, and 262,144 tokens is reached sooner than a minute count suggests.
OCR covers 32 languages. Others are outside the documented capability.
Thirty billion parameters must be loaded even though three run per token — the MoE saving is in compute, not memory.
Out-of-memory is a context problem here, not a weights problem. Reduce the configured length first.
A distinct model class from the text models in this generation. Loading it with the wrong class fails.
OCR robustness is not infallibility. A confidently transcribed wrong digit looks exactly like a correct one — which is why the unreadable marker and the escalation path matter more on the cheap tier than on the expensive one.