Skip to content
BlogRetrieval & eval11 min read

Embed the page, not the OCR text

EmbeddingGemma 2 embeds a page image in 280 tokens and matches it against text in one space. Indexing a million scanned pages is $3.36 — and the 6× storage cut costs 18.83% of visual retrieval.

Google DeepMind published EmbeddingGemma 2 on 6 October 2026 under Apache 2.0 — the announcement and the model card. The line most coverage will lead with is MTEB Code going from 68.76 to 78.68, which the post calls "a significant 9.92-point improvement."

The number that changes a running product is 280. That is the default token cost of one <|image|> placeholder, and it means a scanned page can be an input to the index instead of an OCR job you run before the index.

Everything below is arithmetic on Google's published numbers and on rates published by Cloudflare and Anthropic. I did not run the model, and none of these figures are my measurements.

A page image is 2.3× cheaper to embed than the text of that same page, and the image-token line is 465× cheaper than sending that page to a vision model. The catch is that the Matryoshka cut saving you 6× storage spends the visual retrieval quality you adopted the model for.

What actually changed

Multimodal embedding models are not new. What is new is that this one is 270M parameters for the text-only path, Apache 2.0, and reports a score on MMEB v2 – VisDoc of 67.84. That is a published visual-document-retrieval number, and it is higher than the same model's natural-image score of 57.28 by 10.56 points. The model is better at documents than at photographs, which is the opposite of what people assume when they hear "vision encoder."

So the pipeline that becomes available is: rasterise the page, embed the page image, retrieve against a text index in one shared space. No OCR step, no intermediate description, no layout reconstruction.

The token budget is the whole story

The model card gives the per-modality budgets, and they are the reason this is affordable rather than merely possible:

InputTokensFits in the 8,192 context
One page image280 (default)29 images
One page image70–1120 (configurable soft-token range)~114 at the low end
One video frame140 at 1 fps58 frames
Audio25 per second327 s ≈ 5.5 min

Two things fall out of that table. First, at the low vision budget a page costs about 72 tokens, which is less than a sentence. Second, the audio and video budgets are wildly asymmetric: 8,192 tokens buys 327 seconds of audio but only 58 seconds of video, a 5.65× difference per second of input. If you are indexing recorded meetings you have a lot more headroom than if you are indexing screen recordings.

Now the comparison that matters. A dense A4 page of roughly 500 words is about 650 tokens of OCR text at the usual ~1.3 tokens per word. Embedding that text costs 650 tokens. Embedding the page image costs 280. The visual route is 2.3× cheaper in tokens than the text route for the same page, before you count anything you spend on OCR.

The arithmetic on a million pages

Take a corpus of one million scanned pages — a mid-size claims archive or contract set.

1,000,000 pages × 280 tokens = 280,000,000 tokens = 280 MTok

Cloudflare publishes $0.012 per million input tokens for @cf/qwen/qwen3-embedding-0.6b, the closest published hosted rate for an open-weight embedder in this size class (Workers AI pricing page, updated 1 October 2026):

280 MTok × $0.012/MTok = $3.36          = $0.00000336 per page

For contrast, at OpenAI's published $0.020/MTok small-embedding rate it is $5.60 for the same million pages. Either way the indexing bill is a rounding error.

The reason it is worth writing down is what it replaces. From my own per-page figure, sending a 2000×1500 scan to a vision model costs 1,564 visual tokens, which at Claude Haiku 4.5's $1/MTok input is $0.001564 per page for the image line alone — $1,564 per million pages — and $0.004644 per page all-in once instructions and output are counted, which is $4,644 per million.

tokens:  1,564 ÷ 280   =  5.59× fewer
rate:    $1 ÷ $0.012   = 83.33× cheaper per token
image line: 5.59 × 83.33 = 465× cheaper
all-in:  $0.004644 ÷ $0.00000336 = 1,382× cheaper

I want to be precise about what that 1,382× does and does not mean, because it is the sort of number that gets repeated without its caveat.

It does not remove your extraction pass. If a vision model is reading your pages to pull out invoice totals and line items, you are still paying for that. Structured extraction and indexability are different jobs and this model does not do the first one.

It removes the indexing pass. If you were paying a vision model to read pages purely so that the pages could be found, that bill goes to roughly zero. That is a real and common shape — a lot of document RAG systems describe every page during ingest and then embed the description, because that was the only way to get a scanned page into a vector index.

The storage cut has a cliff in it

The announcement leads with Matryoshka truncation and "up to 6x storage reduction." At 768 dimensions a float32 vector is 3,072 bytes:

768d × 4B = 3,072 B/vector  →  3.072 GB per million vectors
128d × 4B =   512 B/vector  →  0.512 GB per million vectors
3072 ÷ 512 = 6×

Six times is exactly right. The model card also publishes the quality cost at each truncation point, and this is where the decision actually lives:

Benchmark768d512d256d128d
MTEB multilingual v261.3661.1760.4157.89
MTEB eng v268.4668.4167.7865.68
MTEB code v178.6877.2476.1871.41
MIEB lite64.6464.3263.1359.06
MMEB v2 overall59.0158.3856.2445.65
MSEB retrieval69.5469.1866.7656.71
MAEB49.3949.2148.9146.92

Read the marginal loss per halving rather than the totals, because that is what you are choosing between:

MMEB v2 overall:   768→512  −1.07%   512→256  −3.67%   256→128  −18.83%
MTEB multilingual: 768→512  −0.31%   512→256  −1.24%   256→128   −4.17%

Text retrieval degrades gracefully all the way down. Multimodal retrieval does not — the last halving costs 18.83% on MMEB against 4.17% on multilingual MTEB, a 4.51× steeper cliff. MSEB retrieval tells the same story: −0.52%, −3.50%, then −15.05%.

The actionable version: take 512 dimensions, not 128. Going 768 → 512 buys a third of your storage back for 1.07% of multimodal quality on the published numbers. Going 256 → 128 buys one more halving for 18.83%. If you truncate to 128 because the "6× storage reduction" bullet sounded free, you have spent exactly the capability you adopted the model for — and you will find out from your retrieval eval, not from the storage dashboard.

What this breaks in a system that is already running

Your chunking layer stops existing. A page is one vector now. The whole class of problems I have written about — tables split across chunk boundaries, carrying context between pages — evaporates, because you are no longer cutting a page into pieces. Page boundaries become chunk boundaries, which is a simplification and also a hard constraint: you can never retrieve half a page.

Citations regress to page level. A 280-token vector of a page image cannot tell you which sentence the answer came from. If you currently ship span-level citations, this is a downgrade you have to decide you are willing to take. The honest fix is to keep the text layer alongside the vector and use it only for the citation, not for the ranking.

Exact strings still need lexical search. A 280-token page embedding is a compression of a page down to less than a sentence's worth of information. No amount of visual fidelity lets a vector hold a 12-digit invoice number reliably. Hybrid search is not optional here — keep BM25 over the OCR text you were already producing, and let the page vectors handle the semantic half.

Your eval set does not measure this. A query-to-expected-chunk eval set does not grade a visual index. You need query-to-expected-page, and you need it before you cut over, not after.

Vectors from two models do not mix. EmbeddingGemma 2's space is its own. Adopting it means the dual-write migration — two indexes, both live, segmented comparison before the flag flips.

What I would actually do

Nothing this week. The model shipped today, the hosted rate I used is a proxy rate for a different model, and there is no published price for serving EmbeddingGemma 2 itself — the real cost is whatever instance you run it on, which is a fixed cost floor the per-token arithmetic above does not capture. Two hundred and seventy million parameters at roughly 191 MB of active RAM for the text-only path is small enough that the floor is not scary, but it is not zero and it is not a number Google published.

What I would do is take one document type where OCR is currently the weak link — degraded scans, dense tables, anything with handwriting in a printed field — rasterise a few hundred pages, and run the same queries against a page-vector index and your existing text index. Page-level recall, segmented by document type. If the page vectors win on the scans and lose on the clean PDFs, that is a routing rule, and it is a much better answer than replacing one index with the other.

If you are running document retrieval where the scans are the problem and the clean text is fine, this is the first model where the fix is to stop OCRing rather than to OCR better. That is a retrieval problem worth evaluating properly, and usually it is cheaper to rescue the pipeline you have than to rebuild it around a model that shipped this morning.


More in embedding a million documents, a retrieval eval you can build in a day, and retrieval systems that are actually evaluated. Model figures from blog.google and the google/embeddinggemma-2 model card, checked 6 October 2026; Cloudflare Workers AI rates from their pricing page updated 1 October 2026; the vision-model per-page figures are my own arithmetic at Anthropic's published rates.

Something here

the audit is the cheapest way to find out for certain.