apyhub
Back
▣ INGESTION KIT

Image & PDF OCR to Markdown API

What it does

The PDF to Markdown API reads one PDF page or one image and returns its content as Markdown, plain text or structured JSON. It works on scans, photos, screenshots and digital PDFs, in 100+ languages.

Send a PDF to the document endpoint in document, by public URL or base64. Pick the page to read (1-based) and set dpi (72 to 300, default 200) for rendering. Use page.count from the response to walk through the rest of the file one page at a time. Send an image to the image endpoint in image.

Both endpoints share the same options. format picks json (default), markdown or text. mode picks auto, layout or plain. include_blocks turns typed blocks on or off, and charts turns chart-to-table conversion on or off.

The JSON response holds markdown, text, blocks, tables and page. Blocks come in reading order with bounding boxes. Each table comes back as HTML, Markdown and cell rows. Formulas come back as LaTeX, and bar, line and pie charts become data tables.

Teams use the PDF to Markdown API as an OCR API for receipts, invoices and forms, and to extract tables from reports. Others turn research papers into Markdown for search and LLM input, or read text from screenshots.

To clean up photos first, use the Remove Image Background with Advanced Controls API. To decode codes on scanned pages, use the Decode QR & Barcode API.

Limits

Some limits apply to both endpoints, and some apply to only one.

InputLimitApplies to
PDFsUp to 20 MiB and 1,000 pages. Password-protected PDFs are not supported. One page per request.Read a PDF Page
PDF resolutiondpi 72 to 300 (default 200), at most 25 megapixels per rendered page. If the requested dpi would go over 25 megapixels, the service uses a lower dpi instead of failing. Read the actual value from page.dpi in the response.Read a PDF Page
ImagesUp to 20 MiB and 40 megapixels. PNG, JPEG, WebP, GIF, BMP or TIFF. Only the first frame of multi-frame images is read.Read an Image
URLsPublic http(s) only, up to 2,048 characters and 3 redirects. Private and internal addresses are refused.Both endpoints
Base64Up to 20 MB decoded. Base64 encoding adds about one third to the request size.Both endpoints
Request timeUp to 55 seconds. Very complex inputs may time out.Both endpoints
▣ ENDPOINT 01 / 02
POST
Read a PDF page
https://api.eu.apyhub.com/callable-labs/ocr-pdf-to-markdown/v1/ocr/document

QUICKSTART

GUIDE

Quickstart

Read page 1 of a PDF from a public URL.

curl -X POST "https://api.eu.apyhub.com/callable-labs/ocr-pdf-to-markdown/v1/ocr/document" \
  -H "apy-token: $APY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "document": {
      "url": "https://assets.apyhub.com/samples/sample.pdf"
    },
    "page": 1,
    "dpi": 200,
    "format": "json",
    "mode": "auto",
    "include_blocks": true,
    "charts": true
  }'

Expect JSON with recognized markdown, text, blocks, tables, and page metadata.

What you'll get back

You will receive the page's content as Markdown and plain text, typed blocks with bounding boxes in reading order, any tables found, page information including the total page count and the resolution used, the reading mode used, model, cache, and timing information.

{
  "markdown": "Dummy PDF file\n",
  "text": "Dummy PDF file",
  "blocks": [
    {
      "type": "text",
      "label": "text",
      "order": 0,
      "bbox": [153, 193, 506, 246],
      "content": "Dummy PDF file",
      "html": null,
      "markdown": null,
      "latex": null
    }
  ],
  "tables": [],
  "page": {
    "width": 1653,
    "height": 2339,
    "index": 1,
    "count": 1,
    "dpi": 200
  },
  "mode": "layout",
  "coverage": null,
  "model": {
    "layout": "PP-DocLayoutV3@241f8bd",
    "vlm": "PaddleOCR-VL-1.6@c5630ab"
  },
  "cache": "hit",
  "timing": {
    "fetch_ms": 320,
    "decode_ms": 0,
    "queued_ms": 0,
    "ocr_ms": 0
  }
}
TRY ITLIVE · 400 ATOMS
Loading your default key…
The full key is used to call the gateway and stays in this tab — never sent to orbit or saved.
DocumentRequest*
The PDF to read. Provide exactly one of `document.url` or `document.base64`.
PDF URL*
Public http(s) URL of the PDF, up to 2,048 characters. The file must be 20 MiB or smaller. Private and internal addresses are refused.
Resolution used to render the page. Range: `72-300`. Default: `200`. The rendered page is capped at 25 megapixels; if the requested `dpi` would exceed this, a lower `dpi` is used automatically.
Reading mode: `auto` for varied documents, `layout` for structured blocks, or `plain` for a single text block. Default: `auto`.
The 1-based page number to read. Range: `1-1000`. Default: `1`.

About this endpoint

What it does

Reads one page of a PDF, scanned or digital, and returns it as Markdown, plain text, typed blocks with bounding boxes, and tables as HTML, Markdown, and cell rows, all in reading order.

Every response includes the total number of pages (page.count and the X-Page-Count header), so you can read a whole document by requesting page: 1, 2, 3, and so on. Each page is a separate request, and the document must be sent with each request.

Request Body Parameter(s)

AttributeTypeDescription
documentObjectThe PDF to read. Provide exactly one of document.url or document.base64.
document.urlStringPublic http(s) URL of the PDF, up to 2,048 characters. The file must be 20 MiB or smaller. Private and internal addresses are refused.
document.base64StringThe PDF as base64 (a data: URI is also accepted), 20 MB or smaller once decoded.
pageIntegerThe 1-based page number to read. Range: 1-1000. Default: 1.
dpiIntegerResolution used to render the page. Range: 72-300. Default: 200. The rendered page is capped at 25 megapixels; if the requested dpi would exceed this, a lower dpi is used automatically.
formatStringResponse format: json (full result), markdown (page as text/markdown), or text (page as text/plain). Default: json.
modeStringReading mode: auto for varied documents, layout for structured blocks, or plain for a single text block. Default: auto.
include_blocksBooleanWhether to include blocks in the JSON response. Default: true.
chartsBooleanWhether to convert bar, line, and pie charts into data tables. Default: true.

* Exactly one of document.url or document.base64 must be provided. Sending both, or neither, returns an error.

Supported documents: PDFs up to 20 MiB and 1,000 pages. Password-protected PDFs are not supported.

Response

When format is json, the response contains all fields listed in Common Response Fields. For PDFs:

When format is markdown or text, the response is the page as text/markdown or text/plain, and the total page count is still available in the X-Page-Count header.

▣ ENDPOINT 02 / 02
POST
Read an image (OCR to Markdown)
https://api.eu.apyhub.com/callable-labs/ocr-pdf-to-markdown/v1/ocr/image

QUICKSTART

GUIDE

Quickstart

Set APY_TOKEN to your ApyHub API key. Replace the image URL with a public image you are authorized to process.

curl -X POST "https://api.eu.apyhub.com/callable-labs/ocr-pdf-to-markdown/v1/ocr/image" \
  -H "apy-token: $APY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "image": {
      "url": "https://assets.apyhub.com/samples/ReceiptSwiss.jpg"
    },
    "format": "json",
    "mode": "auto",
    "include_blocks": true
  }'

Expect JSON with markdown, text, blocks, tables, and page. Review receipt amounts manually. For a local file, encode it with Python base64 and supply image.base64 instead of image.url.

TRY ITLIVE · 400 ATOMS
Loading your default key…
The full key is used to call the gateway and stays in this tab — never sent to orbit or saved.
ImageRequest*
The image to read. Provide exactly one of `image.url` or `image.base64`.
Image URL*
Public http(s) URL of the image, up to 2,048 characters. The file must be 20 MB or smaller. Private and internal addresses are refused.
Reading mode: `auto` for varied documents, `layout` for structured blocks, or `plain` for a single text block. Default: `auto`.
Whether to convert bar, line, and pie charts into data tables. Default: `true`.
Response format: `json` (full result), `markdown` (page as `text/markdown`), or `text` (page as `text/plain`). Default: `json`.

About this endpoint

What it does

Reads one photo, scan, or screenshot and returns its content as Markdown, plain text, typed blocks with bounding boxes, and tables as HTML, Markdown, and cell rows, all in reading order.

Use this for single-page inputs such as receipts, invoices, forms, whiteboards, screenshots, and photographed documents. Send the image as a public URL or as base64-encoded content.

Request Body Parameter(s)

AttributeTypeDescription
imageObjectThe image to read. Provide exactly one of image.url or image.base64.
image.urlStringPublic http(s) URL of the image, up to 2,048 characters. The file must be 20 MB or smaller. Private and internal addresses are refused.
image.base64StringThe image as base64 (a data: URI is also accepted), 20 MB or smaller once decoded.
formatStringResponse format: json (full result), markdown (page as text/markdown), or text (page as text/plain). Default: json.
modeStringReading mode: auto for varied documents, layout for structured blocks, or plain for a single text block. Default: auto.
include_blocksBooleanWhether to include blocks in the JSON response. Default: true.
chartsBooleanWhether to convert bar, line, and pie charts into data tables. Default: true.

* Exactly one of image.url or image.base64 must be provided. Sending both, or neither, returns an error.

Supported image formats: PNG, JPEG, WebP, GIF, BMP, and TIFF. Images can be up to 40 megapixels. For animated or multi-frame images, only the first frame is read.

Response

When format is json, the response contains all fields listed in Common Response Fields. For images, page.index, page.count, and page.dpi are null.

When format is markdown or text, the response is the page as text/markdown or text/plain.

▣ COMMON ERRORS

Errors any endpoint can return

400bad_request

Required parameter missing or malformed body.

401unauthorized

API key missing, revoked, or not authorized for this service.

429rate_limited

Your plan's per-second rate exceeded. Retry with exponential backoff.

503upstream_busy

Backend temporarily unavailable. Try again in a few seconds.