

We release jina-ocr-v1, a 3.4B-parameter document parser with about 570M active decoder parameters per token. It scores 91.14 on OmniDocBench v1.6 and 83.4 on olmOCR-Bench, and at 2.57 pages per second it has the highest page throughput of the fourteen systems we measured. On an NVIDIA L4 its speculative decoding head nearly doubles decoding speed while keeping decoding lossless.
The model builds on the compressed-vision encoder and mixture-of-experts decoder of DeepSeek-OCR and adds two things. A FastMTP draft head applies one block recursively for three prediction steps, so draft parameters do not grow with depth. Post-training runs under dense verifiable rewards, where every check is deterministic code against a reference and every check is graded. Against that backbone, post-training adds 7.4 points on olmOCR-Bench and improves every OmniDocBench column.


tagModel and training
Long outputs are what make document parsing expensive to decode. DeepSeek-OCR removed most of that cost with a compressed vision encoder and a compact mixture-of-experts decoder, and jina-ocr-v1 inherits both and targets the autoregressive bottleneck that remains.

OCR output is near-deterministic and locally structured, which makes it a favorable workload for speculative decoding. The usual construction attaches one draft head per prediction depth, so draft parameters grow with how far ahead the model looks. FastMTP uses a single dense block applied recursively for K = 3 steps. The verifier checks each proposal greedily and accepts the longest prefix on which draft and verifier agree, so the committed sequence equals the greedy sequence of the verifier and speculation changes only how long the output takes.
| Component | Specification |
|---|---|
| Vision encoder | DeepEncoder (~380M): SAM (80M) → 16x conv → CLIP-L (300M) |
| Vision tokens | 256 @ 1024x1024 (Base); 256+100n, n ≤ 9 (Gundam, ≤ 1,156/page) |
| Decoder | DeepSeek-3B-MoE: 12 layers, d = 1280, 64 routed + 2 shared, top-6 |
| Active / total params | ~570M / ~3B (decoder); < 1B / ~3.4B (whole model) |
| Vocabulary | 129,280 |
| Position limit | 32,768 (RoPE, θ = 106) |
| MTP head | 1 shared dense block, recursive K = 3 steps (FastMTP) |
Model specification. The decoder emits Markdown, with tables in HTML and formulas in LaTeX.
Training data draws on public OCR corpora including olmOCR-mix, FinePDFs, LightOnOCR, MMTab and UniMER, plus deliberately hard sources such as Europeana newspapers, Library of Congress transcripts and NARA pension files. A rule-based filter drops degeneration loops and duplicates, and a vision-language pass relabels the hard sources. We also synthesize pages for one specific reason: on natural pages the formula and table reward terms apply to very few samples, so most rollouts carry no structural signal. JinaOCRSynth packs each page with scorable formulas and tables and ships unit tests with it.
Post-training runs supervised alignment, robustness fine-tuning on degraded pages, and GRPO, repeated across the rounds of an outer loop. The GRPO reward is a product of verifiable terms, each computed by deterministic code against a reference transcription.
| Component | Signal | Role |
|---|---|---|
| Content | Normalized edit distance on mixed LaTeX/HTML | Textual fidelity |
| Formula | Formula string matching | Formula correctness |
| Table | TEDS, TEDS-S, table edit distance | Structure recovery |
| Structural validity | Brace balance, tag closure, table integrity | Well-formedness |
| Unit tests | Fraction of olmOCR-style presence, order, math and table tests passed | Dense feedback |
| Repetition and format | Repetition penalty, HTML conformance | Degeneration control |
Multiplicative reward composition. Most terms carry a floor, since under a product one failed check would remove the gradient from an otherwise correct page. The repetition term has none, because degenerate loops are the failure mode that most readily inflates the content score.
Each round leaves a pool of candidate checkpoints. An agent searches merge configurations under a fixed evaluation budget and scores them with unit-test and edit-distance checks, and errors in the selected merge drive the next round of collection. The draft head is fitted last, on the verifier the loop selects.
tagResults
| Model | Params | ArXiv | OldScans-Math | Tables | OldScans | Multi-col | LongTiny | Hdr/Ftr | Base | Overall |
|---|---|---|---|---|---|---|---|---|---|---|
| Gemini 3 Flash | – | 80.1 | 73.6 | 64.6 | 45.8 | 75.3 | 90.3 | 27.4 | – | – |
| Qwen3-VL-235B | 235B/22B | 88.4 | 81.2 | 86.7 | 49.6 | 85.9 | 88.9 | 33.6 | – | – |
| DeepSeek-OCR | 3B/570M | 77.5 | 74.5 | 77.3 | 33.1 | 67.3 | 83.0 | 96.1 | 99.3 | 76.0 |
| dots.mocr | 3B | 85.9 | 85.5 | 90.7 | 48.2 | 85.3 | 81.6 | 94.0 | 99.7 | 83.9 |
| olmOCR-2 | 8B | 82.9 | 82.1 | 84.3 | 48.3 | 84.3 | 81.4 | – | 99.7 | 82.4 |
| LightOnOCR-2 | 1B | 89.6 | 85.6 | 89.0 | 42.2 | 84.8 | 91.4 | 19.7 | 99.6 | 83.2 |
| chandra-ocr-2 | 4B | 86.9 | 89.1 | 92.1 | 51.1 | 82.1 | 93.7 | 91.4 | 99.9 | 85.8 |
| jina-ocr-v1 | 3B/570M | 86.1 | 82.3 | 88.8 | 42.6 | 85.5 | 93.2 | 88.7 | 99.9 | 83.4 |
olmOCR-Bench. jina-ocr-v1 reaches 83.4 overall, 7.4 points above the DeepSeek-OCR backbone it post-trains and ahead of the 8B olmOCR-2. The Hdr/Ftr column tests text absence and rewards omitting headers and footers, so faithful full-page transcription scores low there.
| Method | Params | Overall ↑ | TextEdit ↓ | FormulaCDM ↑ | TableTEDS ↑ | TableTEDS-S ↑ | ROEdit ↓ |
|---|---|---|---|---|---|---|---|
| Gemini 3 Flash | – | 92.62 | 0.066 | 95.16 | 89.29 | 93.51 | 0.172 |
| Qwen3-VL-235B | 235B/22B | 89.78 | 0.063 | 92.55 | 83.07 | 86.75 | 0.166 |
| DeepSeek-OCR-2 | 3B/570M | 90.25 | 0.050 | 91.84 | 83.89 | 87.75 | 0.144 |
| HunyuanOCR-1.5 | 1B | 94.74 | 0.039 | 94.50 | 93.67 | 94.71 | 0.129 |
| PaddleOCR-VL-1.6 | 0.9B | 96.34 | 0.033 | 97.53 | 94.76 | 97.10 | 0.128 |
| jina-ocr-v1 | 3B/570M | 91.14 | 0.046 | 93.28 | 84.68 | 89.01 | 0.142 |
OmniDocBench v1.6. jina-ocr-v1 reaches 91.14 at 570M active parameters, ahead of DeepSeek-OCR-2 on every column and ahead of the much larger Qwen3-VL-235B.
tagSpeculative decoding on an L4
| Mode | k | Output tok/s ↑ | Speedup S ↑ | Acceptance rate | τ | c ↓ |
|---|---|---|---|---|---|---|
| Eager | 0 | 42.7 | 1.00x | – | – | 1.00 |
| Eager | 1 | 64.0 | 1.50x | 82.6% | 1.83 | 1.22 |
| Eager | 2 | 77.9 | 1.82x | 69.1% | 2.38 | 1.30 |
| Eager | 3 | 83.1 | 1.95x | 57.6% | 2.73 | 1.40 |
| Graph | 0 | 158.3 | 1.00x | – | – | 1.00 |
| Graph | 1 | 185.6 | 1.17x | 82.9% | 1.83 | 1.56 |
| Graph | 2 | 183.8 | 1.16x | 69.3% | 2.38 | 2.05 |
| Graph | 3 | 172.9 | 1.09x | 57.9% | 2.74 | 2.51 |
FastMTP on olmOCR-Bench, NVIDIA L4, vLLM 0.20.1, batch size 1. τ is the mean number of tokens committed per speculative step including the bonus token, and c = τ/S is the cost of one speculative step in units of one autoregressive step. Measured on a different device from the figures above.
Draft quality does not depend on the execution mode, since τ is 2.73 in eager mode and 2.74 under CUDA graphs at k = 3. The baseline does. CUDA graphs raise autoregressive decoding from 42.7 to 158.3 tokens per second while the overhead of a speculative step stays near 9 ms, so its cost rises from 1.40 to 2.51 autoregressive steps. The gain tracks the cost of the verifier step it replaces, which puts the best depth at k = 3 in eager mode and k = 1 under graphs.
tagGetting started
The quickest way to run it is Jina Reader. Point r.jina.ai at a URL and add one header: Reader fetches the page or PDF, renders it, runs jina-ocr-v1 over the result and hands back Markdown. Nothing to deploy, no image plumbing to write, and the same API key as the rest of the platform.
curl "https://r.jina.ai/https://example.com/document.pdf" \
-H "Authorization: Bearer $JINA_API_KEY" \
-H "X-Respond-With: jina-ocr-v1"Add X-Page to transcribe one page of a multi-page document. Both parameters are in the Reader API editor, where the toggle writes the header for you.
For direct access to the model, the hosted endpoint is OpenAI-compatible and needs only an API key from jina.ai.
curl https://api.jina.ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"model": "jina-ocr-v1",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "Transcribe the provided document image into a clean Markdown format, preserving the natural reading order."},
{"type": "image_url", "image_url": {"url": "https://example.com/document.png"}}
]
}]
}'
To serve it yourself, the weights and the custom modeling code ship in one Hugging Face repository loaded with trust_remote_code=True. FastMTP needs vLLM 0.21 or later and a one-time architecture registration before the engine starts.
import sys
from huggingface_hub import snapshot_download
from PIL import Image
from vllm import LLM
sys.path.insert(0, snapshot_download('jinaai/jina-ocr-v1'))
from deepseek_ocr_mtp import DEFAULT_OCR_PROMPT, register, vllm_llm_kwargs, vllm_sampling_params
register()
llm = LLM(**vllm_llm_kwargs('jinaai/jina-ocr-v1',
num_speculative_tokens=3,
mtp_heads=1,
mtp_recursive=True))
image = Image.open('document.png').convert('RGB')
outputs = llm.chat(
[{'role': 'user', 'content': [{'type': 'image_pil', 'image_pil': image},
{'type': 'text', 'text': DEFAULT_OCR_PROMPT}]}],
sampling_params=vllm_sampling_params(max_tokens=4096),
)
print(outputs[0].outputs[0].text)
One detail decides whether the speedup appears. The helper registers the head with method="eagle", since FastMTP is trained with recursive hidden-state feedback and the default method="mtp" re-grounds every draft step on the target. The Transformers path runs the MoE decoder alone and ignores the MTP weights.
The model also handles element-level transcription of tables and formulas, captioning, document VQA and key-information extraction, in English and Chinese. Weights are released under CC BY-NC 4.0.
tagConclusion
At 570M active parameters jina-ocr-v1 lies on the accuracy-per-parameter frontier of both benchmarks and has the highest page throughput of the systems we measured. Two levers do that work and neither needs a larger model: a graded reward on every verifiable check, and a draft head trained against the final verifier.
Output length is worth a closer look. Token throughput and page throughput rank systems differently, and output length is independent of parsing quality, so conciseness can be optimized on its own. jina-ocr-v1 has the shortest outputs of any system scoring above 83.






