Skip to main content

typhoon-ocr2

Read Thai and English documents (PDFs and images) with Typhoon OCR. For every page you get:

  • the page as Markdown (tables as HTML, formulas as LaTeX), and
  • with Typhoon OCR V2, the layout elements in reading order: each one has a category (title, text, table, …), a bounding box and its text.

Links: Typhoon OCR V2 2B · Typhoon OCR V2 0.8B · Playground · Typhoon API docs

This package continues scb-10x/typhoon-ocr (pip install typhoon-ocr) with the Typhoon team's permission. It has a new name, so code that uses typhoon-ocr keeps working.

Install

pip install typhoon-ocr2

PDF support is included; you do not need to install Poppler.

Quick start: Typhoon API (no GPU needed)

import os
from typhoon_ocr2 import TyphoonOCR

os.environ["TYPHOON_API_KEY"] = "YOUR_KEY"       # your Typhoon API key (or pass api_key="YOUR_KEY" below)

ocr = TyphoonOCR(
    base_url="https://api.opentyphoon.ai/v1",    # the Typhoon API
    version="v2",                                # Typhoon OCR V2 ("v1.5" for Typhoon OCR 1.5)
    upscale=True,                                # enlarge images under 1,600 px (recommended; see Settings)
)

pages = ocr.parse("report.pdf", pages="1-3")     # a PDF or an image; pages: "1-3", 2, [1, 4] or None for all
for page in pages:
    print(f"--- page {page.page}")
    print(page.markdown)

Typhoon OCR V2 or 1.5

Switch with version; the rest of the code stays the same.

ocr_v15 = TyphoonOCR(
    base_url="https://api.opentyphoon.ai/v1",
    version="v1.5",
)
print(ocr_v15.parse("page.jpg")[0].markdown)
version="v2" version="v1.5"
model used on the Typhoon API typhoon-ocr-v2 typhoon-ocr-v1.5
page.markdown yes yes
page.elements (category, box, text) yes no (empty list)
ask() / extract() yes not tested

version or model? version tells the package which prompt to send and how to read the answer. model is the model's name on the server. On the Typhoon API you only need version: the package picks the matching model for you. You need model when you run the model yourself (next section), because there the name depends on how you started the server.

Run the model yourself

Start a server, then point base_url and model at it:

# vLLM 0.33.0 or newer (GPU)
vllm serve typhoon-ai/typhoon-ocr2-2b --max-model-len 40960 \
  --limit-mm-per-prompt '{"image": 1}' \
  --mm-processor-kwargs '{"min_pixels": 2048, "max_pixels": 16777216}'

# Ollama 0.40.15 or newer
ollama pull SuraponOCR/typhoon-ocr2-2b

# llama.cpp (GGUF)
llama-server -hf typhoon-ai/typhoon-ocr2-2b-GGUF:Q8_0 --jinja --reasoning off -c 40960 --image-max-tokens 16384
Server base_url model
vLLM "http://localhost:8000/v1" "typhoon-ai/typhoon-ocr2-2b"
Ollama "http://localhost:11434" "SuraponOCR/typhoon-ocr2-2b"
llama.cpp "http://localhost:8080/v1" "typhoon-ocr2" (llama-server serves one model; any name works)
ocr = TyphoonOCR(
    base_url="http://localhost:8000/v1",
    model="typhoon-ai/typhoon-ocr2-2b",
    version="v2",
    upscale=True,
)

For the smaller page parser, use typhoon-ai/typhoon-ocr2-0.8b (Ollama: SuraponOCR/typhoon-ocr2-0.8b). On vLLM older than 0.33.0, add --enforce-eager to vllm serve: in the default compiled mode of vLLM 0.27.1, accuracy dropped by about 9 points on MDPBench. Other OpenAI-compatible servers work the same way.

What you get back

ocr.parse() returns one PageResult per page:

Field Meaning
page.page page number (1-based)
page.markdown the page as Markdown; by default without headers, footers and page footnotes (see below)
page.elements V2 only: every element the model returned, as {"bbox": [x1, y1, x2, y2], "category": "...", "text": "..."}, in reading order
page.image_size (width, height) of the page image the boxes refer to
page.truncated True if the output stopped at the token limit (usually a repetition loop); elements keeps the complete ones
page.raw the model's raw output

Headers, footers and other categories

The model always returns header, footer and page_footnote elements, and they are always in page.elements. Only page.markdown leaves them out by default, because that is how the model was evaluated (and running page numbers and headers usually get in the way of reading). You choose what the Markdown keeps:

pages = ocr.parse("report.pdf")                                   # default: no header, footer, page_footnote
pages = ocr.parse("report.pdf", keep_header_footer=True)          # keep every element
pages = ocr.parse("report.pdf", exclude=["footer"])               # keep headers, drop footers only
pages = ocr.parse("report.pdf", exclude=["header", "footer", "page_footnote", "figure_caption"])  # your own list

footers = [e["text"] for e in pages[0].elements if e["category"] == "footer"]   # the elements themselves

On the command line: --keep-header-footer, or --exclude footer (several names allowed; --exclude with no names keeps everything).

The 13 categories are header, title, text, figure, table, formula, figure_caption, table_caption, formula_caption, figure_footnote, table_footnote, page_footnote and footer. Boxes use a 0–1000 grid relative to the page, so they do not depend on the image size:

from typhoon_ocr2 import draw_layout, load_pages, to_pixels

image = load_pages("report.pdf", pages=[1])[0]
first = pages[0].elements[0]
print(first["category"], to_pixels(first["bbox"], *image.size))   # box in pixels
draw_layout(image, pages[0].elements).save("page1_layout.png")     # boxes numbered in reading order

Questions and key information (Typhoon OCR V2 2B)

answer = ocr.ask("receipt.jpg", "What is the total amount?")      # plain text
fields = ocr.extract("receipt.jpg", ["Date", "Total", "VAT 7%"])   # dict with these keys
everything = ocr.extract("receipt.jpg")                            # every field; the model picks the keys

The 0.8B model is a page parser only; it is much weaker at questions and extraction. Check extracted values before you use them: key-information extraction is the 2B model's weakest task.

Command line

export TYPHOON_API_KEY=YOUR_KEY
API=https://api.opentyphoon.ai/v1

typhoon-ocr2 report.pdf --base-url $API                                  # Markdown of every page (V2)
typhoon-ocr2 report.pdf --base-url $API --pages 2-4 --format json        # layout elements
typhoon-ocr2 receipt.jpg --base-url $API --ask "What is the total amount?"
typhoon-ocr2 receipt.jpg --base-url $API --extract Date Total
typhoon-ocr2 page.jpg --base-url $API --version v1.5                     # Typhoon OCR 1.5
typhoon-ocr2 page.jpg --base-url http://localhost:11434 --model SuraponOCR/typhoon-ocr2-2b   # Ollama

To avoid repeating --base-url, set TYPHOON_OCR_BASE_URL. Add --no-upscale to turn upscaling off.

Settings

All settings of TyphoonOCR(...). The defaults are the settings the models were evaluated with.

Setting Default What it does
base_url $TYPHOON_OCR_BASE_URL, else "http://localhost:8000/v1" server address
model on the Typhoon API, chosen from version; on Ollama, "SuraponOCR/typhoon-ocr2-2b"; on other servers, the first model they serve model name on the server; needed only for your own server
api_key $TYPHOON_API_KEY key for the Typhoon API
version "v2" (or follows model, if you set one) "v2" or "v1.5": the prompt and output format
upscale True enlarge images whose long side is under 1,600 px. On ThaiOCRBench this raised Typhoon OCR V2 2B from 59.9 to 62.9 and 0.8B from 37.3 to 41.3; large pages are not changed. False turns it off; a number sets another size
max_tokens 16384 output limit per page
pdf_long_side 2048 PDF pages are rendered with this long side, in pixels
max_workers 4 PDF pages sent in parallel
timeout 600 seconds to wait for one page
backend "auto" "openai" or "ollama"; auto picks Ollama for http://localhost:11434
figure_language "Thai" v1.5 only: language of figure descriptions

The prompts are the ones the models were trained with (typhoon_ocr2.PARSE_PROMPT for V2), and decoding is greedy with thinking turned off.

Coming from typhoon-ocr

typhoon-ocr typhoon-ocr2
from typhoon_ocr import ocr_document from typhoon_ocr2 import ocr_document (same idea), or TyphoonOCR for more
ocr_document(path, page_num=1, base_url=..., model=...) ocr_document(path, pages=1, base_url=..., model=...)
returns a Markdown string TyphoonOCR.parse() also returns layout elements with boxes
one page per call pages="1-3,5" or all pages, sent in parallel
needs Poppler for PDFs no system dependency

Development

pip install -e ".[dev]"
pytest                                                                  # unit tests, no GPU needed
TYPHOON_OCR2_TEST_URL=http://localhost:8000/v1 pytest -m integration    # against a running server

Releasing

  1. On pypi.org, add a Trusted Publisher for project typhoon-ocr2: this GitHub repository, workflow publish.yml, environment pypi.
  2. Bump __version__ in src/typhoon_ocr2/__init__.py, commit, then tag and push: git tag v0.1.0 && git push origin v0.1.0.

License

Apache-2.0. See LICENSE and NOTICE. Use of the Typhoon models is subject to the OpenTyphoon Terms and Conditions.

Metadata

Release files for typhoon-ocr2 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for typhoon-ocr2 0.1.0
File Size Uploaded Provenance Actions
typhoon_ocr2-0.1.0.tar.gz 27.5 kB With provenance Details for typhoon_ocr2-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for typhoon-ocr2 0.1.0
File Interpreter ABI Platform Actions
typhoon_ocr2-0.1.0-py3-none-any.whl Python 3 none any Details for typhoon_ocr2-0.1.0-py3-none-any.whl

Total release size: 50.4 kB

Release files / typhoon_ocr2-0.1.0.tar.gz

Download URL typhoon_ocr2-0.1.0.tar.gz
Size 27.5 kB
Tags Source
SHA-256 checksum
How to use checksums
468ae8a310e4ccdfd48b35eda7e9a0416286e4ac49813aad69c416ea32d36444
BLAKE2b-256 checksum
How to use checksums
d0f8707cba9c70bd0e168987a714f82ccf2e6e30e07437379e6b9e248a60baf2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.

Transparency log

Release files / typhoon_ocr2-0.1.0-py3-none-any.whl

Download URL typhoon_ocr2-0.1.0-py3-none-any.whl
Size 22.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d72a468c1505b8dfb02ae86b926579912b58d316e3bd7bfee2941c149a86e4dd
BLAKE2b-256 checksum
How to use checksums
ea062f05c0745e4d90910ae08f8a827d800252cf1ee3ab007fc93ca182ae1f52
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.

Transparency log

Security for typhoon-ocr2 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for typhoon_ocr2-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page