# Datalab in Practice: Marker, Surya, Chandra and the API

> Install Marker, convert a PDF, read the output, then do the same through the Datalab API. Every command comes from the official READMEs and docs.

- Source: https://www.zarifautomates.com/blog/datalab-in-practice-marker-surya-chandra
- Published: 2026-09-26
- Updated: 2026-09-26
- Pillar: Agents & AI Engineering
- Tags: document ai, ocr, ocr llm, document processing
- Author: Zarif

---

Datalab ships three open-source projects and a hosted API, and the first decision is which one you are actually installing. This guide takes the path most builders need: run Marker locally on a PDF, read what it produces, then make the same request through the API.

This is part 3 of the Document AI series. Every command below comes from the official READMEs and docs, read on September 26, 2026. None of the commands were run for this article. Where an output is shown, it is the shape the documentation describes, labeled as expected output from the docs.

## Pick the right repository first

The three projects overlap, and the READMEs are clear about the division of labor.

| Project | What it is | Use it when | Install |
| --- | --- | --- | --- |
| [Marker](https://github.com/datalab-to/marker) | A conversion pipeline: text layer first, Surya VLM where needed, tables, images, metadata | You want markdown, HTML, JSON, or RAG chunks out of whole documents | `pip install marker-pdf` |
| [Surya](https://github.com/datalab-to/surya) | A 650M-parameter OCR model with layout, reading order, and table recognition | You want raw OCR lines, layout boxes, or table cells to build your own pipeline | `pip install surya-ocr` |
| [Chandra](https://github.com/datalab-to/chandra) | A full-page document VLM, version 2 released March 2026 | Scans, handwriting, math, and forms where a text-layer pipeline struggles | `pip install chandra-ocr` |

Marker uses Surya under the hood, so installing Marker gets you both. Chandra is separate. The Marker README points to Chandra when documents need full-page VLM OCR, such as math-heavy pages and scans.

Check the model license before you build on any of them. The code is Apache 2.0 in all three. The model weights use a modified OpenRAIL-M license. The [Marker](https://github.com/datalab-to/marker) and [Surya](https://github.com/datalab-to/surya) READMEs make the weights free for research, personal use, and startups under $5M in funding or revenue. The [Chandra](https://github.com/datalab-to/chandra) README sets the threshold at $2M and adds that the weights cannot be used competitively with Datalab's API. Above those lines, commercial use of the weights needs a license from Datalab.

## Step 1: install Marker and its inference backend

The README asks for Python 3.10 or newer and [PyTorch](https://pytorch.org/get-started/locally/). Then:

```bash
pip install marker-pdf
```

Add the `full` extra only if you will convert DOCX, PPTX, XLSX, HTML, or EPUB files as well as PDFs:

```bash
pip install marker-pdf[full]
```

Marker runs its VLM through a local inference server that it starts on first use. On an NVIDIA GPU that means vLLM in Docker, which needs Docker and the NVIDIA Container Toolkit. On a CPU or Apple Silicon machine it means llama.cpp. The README's macOS instruction is:

```bash
brew install llama.cpp
```

This is the step most likely to trip you up, because the Python install succeeds either way. If neither backend is present, the first conversion that needs OCR has no server to call.

## Step 2: convert one PDF

Put your file in a working folder. The synthetic Harbor Lane invoice from [part 2](/blog/parse-vs-extract-vs-classify-vs-split) is the running example, saved as `invoice.pdf`. Then run the single-file command from the README:

```bash
marker_single invoice.pdf --output_format markdown --output_dir ./out
```

The options that matter most on a first run, all documented in the README:

- `--mode fast` or `--mode balanced`. The default depends on the device: balanced on a GPU, fast on CPU and Apple Silicon. Balanced uses the VLM for layout and re-OCRs any page with bad embedded text. Fast keeps VLM calls to equations, garbled blocks, and scanned pages.
- `--disable_ocr` turns off every VLM call. It is the pure CPU text-layer path, and it skips equations and scanned pages entirely.
- `--force_ocr` OCRs every page even when the PDF has a text layer. The README suggests it when you see garbled text.
- `--use_llm` adds an external LLM to merge tables across pages, format tables, and extract form values. It defaults to a Gemini model and needs an API key for whichever service you configure.
- `--page_range "0,5-10"` limits the pages. Page numbers are 0-indexed.

For a folder of files, the batch command takes the same options:

```bash
marker ./invoices --output_dir ./out
```

## Step 3: read what came back

Expected output from the docs, not from a run: with markdown output, the README says you get the converted text with formatted tables, image links, and LaTeX equations fenced with double dollar signs, and the extracted images are saved in the same folder. Every output format also returns a metadata dictionary.

The metadata is where you check whether the conversion did what you think. The README documents two keys. `table_of_contents` lists detected headings with their page and position. `page_stats` records, for each page, the `text_extraction_method` used and the count of each block type. If a page you expected to be OCR'd shows the embedded text layer as its method, and the text looks wrong, rerun with `--force_ocr`.

Switch to JSON when you need positions:

```bash
marker_single invoice.pdf --output_format json --output_dir ./out
```

The README describes the JSON as a list of pages, each a tree of blocks. Every block carries an `id` such as `/page/0/Table/3`, a `block_type` such as `SectionHeader`, `Table`, `Form`, or `Handwriting`, an `html` rendering, a four-corner `polygon` in page coordinates, and its `children`. Child blocks also carry a `section_hierarchy`. The page-level `html` uses `content-ref` placeholders for its children, so you have to resolve those to rebuild full HTML. The README points to a helper in `marker/output.py` for that.

That block tree is what makes a conversion auditable. A value you later extract from the invoice can be traced back to a block ID, and the block ID gives you a polygon to highlight on the page.

## Step 4: go lower with Surya, or heavier with Chandra

If you only want the OCR layer, Surya's CLI commands each write a `results.json`, according to the [Surya README](https://github.com/datalab-to/surya):

```bash
surya_ocr invoice.pdf
surya_layout invoice.pdf
surya_table invoice.pdf
```

`surya_ocr` returns text with boxes, labels, confidence, and reading order. `surya_layout` returns labeled regions such as Text, Table, and SectionHeader with reading positions. `surya_table` returns rows, columns, cells, and HTML for each table.

For scans and handwriting, the [Chandra README](https://github.com/datalab-to/chandra) gives two routes. The recommended one starts a vLLM server in Docker and then converts:

```bash
pip install chandra-ocr
chandra_vllm
chandra invoice.pdf ./output
```

The other runs the model locally through Hugging Face and needs torch:

```bash
pip install chandra-ocr[hf]
chandra invoice.pdf ./output --method hf
```

Expected output from the docs: each file gets its own subdirectory holding a `.md` file, an `.html` file, and a `_metadata.json` file with page information and token counts, with extracted images saved in the output directory.

## Step 5: the same conversion through the Datalab API

The hosted API runs Datalab's newer models without a GPU on your side. The [quickstart](https://documentation.datalab.to/docs/welcome/quickstart) installs the SDK and reads your key from the environment:

```bash
pip install datalab-python-sdk
export DATALAB_API_KEY=your_api_key_here
```

Then convert:

```python
from datalab_sdk import DatalabClient, ConvertOptions

client = DatalabClient()
options = ConvertOptions(output_format="markdown", mode="balanced")
result = client.convert("invoice.pdf", options=options)
print(result.markdown)
result.save_output("output/")
```

The SDK polls for you. Over plain HTTP, the [conversion docs](https://documentation.datalab.to/docs/recipes/conversion/conversion-api-overview) show a two-step pattern: submit, then poll the `request_check_url` from the response.

```bash
curl -X POST https://www.datalab.to/api/v1/convert \
  -H "X-API-Key: $DATALAB_API_KEY" \
  -F "file=@invoice.pdf" \
  -F "output_format=markdown" \
  -F "mode=balanced"
```

Two details in those docs are easy to miss. A job can return `status` complete with `success` false and an error, so check both. And requests processed in the EU return a signed `result_url` to download instead of inline content. The docs warn that Python SDK 0.5.0 does not follow that URL, so an empty result from the SDK does not prove the document was empty.

The API has three modes, `fast`, `balanced`, and `accurate`. The docs recommend balanced for most work and accurate for scans, complex tables, and dense layouts. They also note that the REST default is fast, so pass the mode explicitly.

## Step 6: extract fields instead of converting pages

Conversion gives you the whole invoice. To get fields, send a JSON schema to the extract endpoint. This is the quickstart from the [structured extraction docs](https://documentation.datalab.to/docs/recipes/structured-extraction/api-overview), trimmed to the invoice:

```python
import json
from datalab_sdk import DatalabClient, ExtractOptions

client = DatalabClient()
schema = {
    "type": "object",
    "properties": {
        "invoice_number": {"type": "string", "description": "Invoice ID or number"},
        "total_amount": {"type": "number", "description": "Total amount due"},
        "vendor_name": {"type": "string", "description": "Company or vendor name"},
    },
    "required": ["invoice_number", "total_amount"],
}
options = ExtractOptions(page_schema=json.dumps(schema), mode="balanced")
result = client.extract("invoice.pdf", options=options)
print(json.loads(result.extraction_schema_json))
```

Expected output from the docs: `extraction_schema_json` holds your fields, and each field gets a companion list of citations, such as `total_amount_citations`, naming the source block IDs. Balanced and accurate extraction also return `_meta` fields with an extraction status, reasoning, and verification result.

Watch the two mode settings. `extraction_mode` chooses turbo, fast, balanced, or accurate extraction. `mode` chooses parsing quality. When you omit `mode`, the docs say balanced and accurate extraction parse in accurate mode, which is billed separately. If you will extract more than once from the same file, convert with `save_checkpoint=true` and pass the checkpoint to extract so the document is not parsed twice.

## What it costs, as of September 26, 2026

Datalab's [pricing page](https://www.datalab.to/pricing) lists conversion at $4 per 1,000 pages in fast or balanced mode and $10 in accurate mode, and extraction at $6 for turbo or fast, $15 for balanced, and $20 for accurate, with possible compute fees on the last two. The [billing docs](https://documentation.datalab.to/platform/billing) say processors are additive, describe a free monthly allowance of $20 for work-email accounts and $10 for personal ones, and list a Team plan at $400 a month that includes $400 of usage.

So a balanced extraction on a two-page invoice, with the default accurate parse, costs the extraction rate plus the conversion rate on both pages. The [document processing cost calculator](/lab/document-processing-cost-calculator) carries these Datalab rates as presets and adds the human review time that usually dominates the bill.

The local route has no per-page fee, but it has a GPU or a slow CPU, the inference server to keep running, and the weights license to respect. For a startup under the threshold with a GPU already on hand, Marker locally is a reasonable way to learn the output format before paying for anything.

## Where this leaves you

You now know which repository to install, what each output format contains, and how the API maps onto the local tools. The part to test on your own documents is the one this article could not: whether fast mode is good enough on your scans, and whether the extra accuracy of the hosted models is worth the per-page price.

For how Datalab fits a wider vendor shortlist, see [where Datalab fits among enterprise tools](/blog/best-enterprise-ai-document-processing-tools).

Previous: [Parse, extract, classify, split](/blog/parse-vs-extract-vs-classify-vs-split). Next: [Extend in practice: schemas, evals and workflows](/blog/extend-in-practice-schemas-evals-workflows).


