Mistral OCR, DeepSeek-OCR or an OCR API for Your Documents?

Posted on | Last updated on
Three magnifying lenses compare OCR results on a document

A user uploads a scanned contract, and your app needs the text out of it. Two kinds of tool can read that page, and they hand back different things.

Mistral OCR and DeepSeek-OCR are model-based OCR. They read a page the way a vision model does and return markdown, with headings, lists and tables kept in shape. A classic OCR API returns plain text plus the position of every word on the page, and the same file gives the same words and positions on every request. Pick a model when you want a clean, readable copy of the page to feed a language model. Pick an OCR API when your app needs to know where each word sits, to highlight it, crop it, redact it or match a value to its label.

Filestack OCR, part of our API for uploading, processing and delivering files, is a classic OCR API. It reads a stored file by its handle, turning a PDF page into an image in the same request, and returns every line and word with a bounding box. We wrote a short Node script for this post that runs it on one page of a scanned agreement.

Key takeaways

  • Mistral OCR, a hosted API, and DeepSeek-OCR, an open model, both return each page as markdown that keeps headings and tables.
  • An OCR API returns every word with a four-point bounding box in the image’s pixels.
  • Word positions are what let an app highlight a search hit, pair a value with its label, or pixelate one field.
  • Filestack OCR reads images, so a PDF page is converted to PNG in the same URL before OCR runs.
  • Whichever reads the text, the file still needs intake, storage and delivery around it.

What do Mistral OCR and DeepSeek-OCR return?

Mistral OCR and DeepSeek-OCR both return the page as markdown. A heading stays a heading, a table comes back as a table, and reading order follows the layout rather than the raw position of each line.

First, Mistral OCR is a hosted API. The Mistral OCR documentation describes a pages array where each page carries markdown, extracted images, tables, hyperlinks, a header, a footer and the page dimensions. With include_blocks turned on, it adds a bounding box for each paragraph-level block of text. Confidence scores are available per page, per block or per word. Mistral bills OCR per 1,000 pages.

By contrast, DeepSeek-OCR is an open model, and DeepSeek doesn’t host it. The DeepSeek-OCR repository published the original 3B-parameter model under the MIT license in October 2025.

DeepSeek-OCR 2 followed in January 2026 at the same size, under Apache 2.0. Both run through vLLM or Transformers, and DeepSeek’s examples are tested on NVIDIA GPUs. A prompt such as <|grounding|>Convert the document to markdown. returns the page as markdown in either version, and the grounding mode also locates regions on the page.

As a result, Mistral OCR and DeepSeek-OCR are both strong at layout. A contract with numbered clauses, or an invoice with a table, comes out as structured text that a language model can read directly. In document parsing versus OCR, structured output is the main difference, and it decides how much cleanup the text needs before search or extraction.

What does an OCR API return that a model doesn’t?

An OCR API returns the position of every word. Each word carries a four-point bounding box in the pixels of the page image. Words are grouped into lines and text areas, alongside the flat text of the whole page.

Page 1 of a scanned services agreement with all 178 OCR words outlined in orange, beside the first five words and their pixel coordinates
Page 1 of a scanned services agreement with all 178 OCR words outlined in orange, beside the first five words and their pixel coordinates

read-words.mjs reads page 1 of the agreement in the figure with Filestack OCR and prints the first words with their boxes. Filestack OCR reads images, so the script converts the PDF page to a PNG in the same URL. The request is signed with a policy that allows read and convert for this one file. The script is plain Node with no packages, and it needs Node 20.6 or later for --env-file:

import { createHmac } from 'node:crypto';

// A five-minute policy that lets this request read and convert one file
function signedUrl(tasks, handle) {
  const expiry = Math.floor(Date.now() / 1000) + 300;
  const policy = Buffer.from(JSON.stringify({ call: ['read', 'convert'], handle, expiry }))
    .toString('base64')
    .replace(/\+/g, '-')
    .replace(/\//g, '_');
  const signature = createHmac('sha256', process.env.FILESTACK_APP_SECRET).update(policy).digest('hex');
  return `https://cdn.filestackcontent.com/security=p:${policy},s:${signature}/${tasks}/${handle}`;
}

const [handle, page = '1'] = process.argv.slice(2);

// OCR reads images, so the PDF page is turned into a PNG in the same request
const res = await fetch(signedUrl(`output=format:png,page:${page}/ocr`, handle));
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const ocr = await res.json();

// Every word on the page, with the box it sits in, in the page image's pixels
const words = ocr.document.text_areas.flatMap((area) => area.lines.flatMap((line) => line.words));
for (const word of words.slice(0, 5)) {
  const xs = word.bounding_box.map((p) => p.x);
  const ys = word.bounding_box.map((p) => p.y);
  console.log(word.text.padEnd(12), `x ${Math.min(...xs)}-${Math.max(...xs)}`, `y ${Math.min(...ys)}-${Math.max(...ys)}`);
}
console.log(`${words.length} words on a ${ocr.page_width} x ${ocr.page_height} page`);

The script reads your app secret from a .env file in the folder you run it from. Copy the secret from the Security section of your application in the Developer Portal:

FILESTACK_APP_SECRET=your_app_secret

Run it with the handle of a PDF stored in your app and the page number:

node --env-file=.env read-words.mjs HANDLE 1

For the agreement, it prints:

MASTER       x 165-234 y 55-70
SERVICES     x 240-323 y 54-69
AGREEMENT    x 327-436 y 52-68
Agreement    x 217-265 y 89-102
No.          x 267-284 y 89-100
178 words on a 612 x 792 page

OCR by handle needs a signed policy, which signedUrl adds. A request without one returns 403 security required for tasks: ocr. A PDF sent to ocr without the conversion step returns 400 with unsupported mimetype, so keep output=format:png in front of ocr. The full response shape, with text areas, lines and words, is in the OCR task reference.

When are bounding boxes the deciding factor?

Bounding boxes decide the choice whenever your app acts on a place on the page rather than on the text alone. Four common cases:

  • Highlighting. A search hit or a citation in a chat answer points at the exact line on the page image.
  • Label and value pairing. A value printed under the label ORDER NUMBER belongs to that label. The bounding boxes show which value sits under which label.
  • Redaction. A name, an account number or a signature gets pixelated or cropped at its coordinates.
  • Form checks. A field that should sit in the top right of a form can be checked by position.

Label pairing also finds the total on an invoice, and we build that lookup step by step in invoice totals from bounding boxes. If none of these apply, and a language model reads the page next, markdown is often the easier input. Mistral OCR and DeepSeek-OCR both return it.

Filestack Discord

Which one gives the same output on every run?

Filestack OCR returns the same words and boxes for the same file on every request. For the agreement, read-words.mjs prints the same five words and boxes each time it runs.

However, a model that generates its output token by token can phrase or order the same page differently between runs, depending on the model and its sampling settings.

Therefore, run-to-run variation is fine for a reading copy. It’s a problem when your app stores a field’s position, compares two versions of a document, or needs an audit trail that reproduces. To compare engines on your own pages, you can benchmark OCR accuracy against a test set with known text.

What are the options for reading text from uploaded documents?

There are four common ways to get text out of documents your users upload:

Option What it returns Best for The catch
Tesseract on your own server Text with word boxes Clean scans, offline use You run and tune it, and photos and skewed pages need preprocessing
DeepSeek-OCR on your own GPU Markdown, with grounding on request Layout-heavy pages, data that stays on your hardware You run the GPU, the model and its updates
Mistral OCR Markdown per page, block boxes on request Clean reading copies for a language model Word-level positions aren’t part of the response
Filestack OCR Text, lines and words with bounding boxes Highlighting, redaction, label pairing, the same output every run It returns positions and text, not markdown

Tesseract is open source, and its code and trained models live in the Tesseract repository. In Python, the other common Python OCR libraries are EasyOCR and PaddleOCR, and both run on your own server the way Tesseract does. The options also combine. You can keep Filestack OCR for positions and send the same page to a model when you want a markdown copy for a language model.

What happens to the file before and after OCR?

The file needs work on both sides of the OCR step, whichever engine reads it. Before OCR, the upload is received, checked and stored, and a PDF page or a phone photo is turned into a clean image. After OCR, the text is saved with the file, the page is served when someone opens the citation, and the file is deleted when it’s no longer needed.

In addition, our API covers that work around OCR. The upload lands through our file picker or upload API, a workflow can scan it for viruses, and Document Detection straightens a photographed page. The CDN then serves the page image when a user follows a citation back to it. Put together, those steps make an intelligent document processing pipeline with OCR as one stage.

Questions people ask

Is Mistral OCR better than an OCR API?

Mistral OCR is better when you want the page as markdown, with tables and headings kept. An OCR API is better when you need each word’s position on the page or the same output on every request. Highlighting, redaction and label matching all work from coordinates, so an app that does any of them needs an OCR API.

Does DeepSeek-OCR have an API?

DeepSeek-OCR has no hosted API from DeepSeek. The weights are open, so you serve the model with vLLM or Transformers behind your own endpoint, or call it through a third-party inference provider that hosts it. DeepSeek-OCR 2 works the same way.

Can an LLM like GPT or Claude do OCR?

Vision language models such as GPT and Claude can read text from an image. They return it in any format you ask for, including markdown or JSON. Their responses carry no position for each word, and their output can vary between runs. So they suit reading and summarizing more than redaction or audit trails.

Does OCR work on PDFs?

OCR works on PDFs once each page is an image. With Filestack, output=format:png,page:1/ocr in one URL converts page 1 and reads it. A PDF that already has a text layer can skip OCR, because the text can be extracted directly.

What to try next

OCR runs on the Start, Grow and Scale plans. To run read-words.mjs on your own documents, create a free account and choose one of those plans. Then upload a scanned PDF with the file picker or the upload API. The handle is the last part of the file URL the upload returns, and it’s what the script takes.

Sign up free for Filestack

Read More →