Image OCR for AI Agents: Text, Boxes and Markdown
Pricing
Pay per event
Image OCR for AI Agents: Text, Boxes and Markdown
Extract text from images (PNG, JPEG, WebP, TIFF, BMP, GIF) in reading order, with line and word bounding boxes, confidences and optional Markdown. PaddleOCR + Tesseract, 80+ languages, auto-rotation, batch URLs or key-value store files. Priced per image.
Pricing
Pay per event
Rating
0.0
(0)
Developer
tinplatelabs
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
9 days ago
Last modified
Categories
Share
Extract text from images and get it back the way an AI agent can use it: in reading order, as plain text and
as Markdown that keeps paragraphs and line breaks, plus every line and word with its bounding box and
confidence. Send a batch of image URLs, key-value store files or data: URIs; get one result row per image.
Works on screenshots, scanned letters and forms, receipts, signs, labels, slides and photos of documents. Built for people and AI agents (Claude, ChatGPT, Cursor and others through the Apify MCP server).
Why this one
- Accurate. PaddleOCR PP-OCRv6 (Apache-2.0) runs on CPU inside your run. On our test set of rendered and degraded text (rotated, noisy, JPEG, low resolution, photo-like), it made 0.1% character errors, against 6.8% for Tesseract.
- Reading order. Lines are grouped into blocks, rows and paragraphs. Two-column prose is read column by column; forms and tables are read row by row, so a label stays next to its value.
- Boxes and confidences. Every line has a box, a 4-point polygon, its block and paragraph number and its confidence; every word has a box and confidence. Coordinates are in the pixels of the image you sent (after EXIF rotation), whatever the Actor did to read it.
- Upright, whatever you send. EXIF orientation is applied, pages turned 90, 180 or 270 degrees are detected, tilted text is handled, small images are enlarged. Optional denoise and contrast boost.
- Many languages. English, Chinese, Japanese and 45+ Latin-script languages are read together with no setup.
Russian, Ukrainian, Greek, Arabic, Persian, Hebrew, Hindi, Korean, Thai and more go to Tesseract when you name
them in
languages. - Batch and predictable. Up to 500 images per run, every page of a multi-page TIFF, clear limits, and an error item that says what to change. Failed images cost nothing.
Input
| Field | Type | Default | What it does |
|---|---|---|---|
images | array of strings | required | Public http(s) image URLs, key-value store records as kvs://KEY (the store in keyValueStoreId, else this run's store) or kvs://STORE_ID_OR_NAME/KEY, signed Apify record URLs (?signature=), or base64 data:image/... URIs. |
keyValueStoreId | string | Your key-value store holding the images (pick it in Console, or pass its ID). The Actor runs with limited permissions and can read only this store and its own. | |
languages | array of strings | auto | Empty: PaddleOCR reads English, Chinese, Japanese and Latin-script languages together. ISO codes such as ru, uk, el, ar, fa, he, hi, ko, th switch to Tesseract (add en for mixed text). |
engine | auto | paddle | tesseract | auto | Force an engine. |
outputMarkdown | boolean | true | Add markdown. |
includeLines | boolean | true | Add lines with boxes, polygons, block and paragraph. |
includeWords | boolean | true | Add each line's words with boxes. |
autoRotate | boolean | true | Detect pages turned 90/180/270 degrees. |
deskew | boolean | true | Straighten tilted scans before Tesseract. |
upscaleSmallImages | boolean | true | Enlarge images under 600 px (shorter side) 2x first. |
denoise | boolean | false | Median filter for speckled scans. |
enhanceContrast | boolean | false | Grayscale and stretch contrast for faded text. |
minConfidence | integer 0-100 | 50 | Drop lines read with lower confidence. |
imageTimeoutSecs | integer 10-1800 | 120 | Per page; slower pages get a TIMEOUT item and are not charged. |
{"images": ["https://upload.wikimedia.org/wikipedia/commons/7/7c/Lytle_Creek_Ranger_Station_Sign%2C_San_Bernardino_NF_%283720847203%29.jpg","kvs://receipt-0412.png"],"keyValueStoreId": "my-scans","outputMarkdown": true}
To read a file that isn't online, upload it to a key-value store, pick that store as keyValueStoreId and pass
kvs://KEY, or pass it inline as a data:image/png;base64,... URI.
Output
One dataset item per image (per page for multi-page TIFFs):
{"source": "https://example.com/letter.gif","status": "ok","page": 1,"pageCount": 1,"text": "February 18, 1963\n\nDear President Kennedy:\n\nMy brother, Specialist James Delmas McAndrew, was one of the\nseven crew members killed ...","markdown": "February 18, 1963\n\nDear President Kennedy:\n\nMy brother, Specialist James Delmas McAndrew, was one of the \nseven crew members killed ...","lineCount": 36,"wordCount": 412,"meanConfidence": 0.9881,"orientation": {"rotation": 0, "skewDegrees": 0.29},"engine": "paddleocr-v6","preprocessing": ["upscale2x"],"languages": ["auto"],"image": {"width": 577, "height": 756, "format": "gif", "exifRotated": false},"chargedEvent": "image-ocr","timingMs": {"download": 57, "ocr": 6152, "total": 6225},"warnings": [],"lines": [{"text": "Dear President Kennedy:","confidence": 0.9952,"box": {"x": 88, "y": 121, "width": 168, "height": 17},"polygon": [[88, 121], [256, 121], [256, 138], [88, 138]],"block": 1,"paragraph": 2,"words": [{"text": "Dear", "confidence": 0.998, "box": {"x": 88, "y": 121, "width": 33, "height": 17}},{"text": "President", "confidence": 0.995, "box": {"x": 125, "y": 121, "width": 70, "height": 17}},{"text": "Kennedy:", "confidence": 0.993, "box": {"x": 199, "y": 121, "width": 57, "height": 17}}]}]}
text: paragraphs separated by a blank line, lines by a line break.markdown: the same structure; larger short lines become#headings, bullet lines become-list items, and characters that would change the Markdown structure are escaped.orientation.rotation: degrees the image was turned clockwise to read it (0, 90, 180, 270).polygonstarts at the text's top-left corner in reading direction.boxis the axis-aligned bounding box.- Words in Chinese and Japanese text are single characters.
An image that can't be read gives an error item, and the run carries on:
{"source": "https://example.com/page.html", "status": "error","error": {"code": "NOT_AN_IMAGE", "message": "Not a supported image (PNG, JPEG, WebP, TIFF, BMP or GIF). It looks like an HTML page; give the direct image URL. For PDFs use a document-to-text tool."}}
Error codes: INVALID_SOURCE, ACCESS_DENIED, DOWNLOAD_FAILED, FILE_TOO_LARGE, NOT_AN_IMAGE, CORRUPT_IMAGE,
IMAGE_TOO_LARGE, IMAGE_TOO_SMALL, TIMEOUT, SPENDING_LIMIT_REACHED, INTERNAL_ERROR.
Pricing (pay per event)
| Event | Price | When |
|---|---|---|
image-ocr | $0.003 | Each image (or TIFF page) up to 4 megapixels read successfully, also when it has no text |
image-ocr-large | $0.008 | Each image (or TIFF page) over 4 megapixels, e.g. a phone photo or a 300 dpi page scan |
| Actor start | set by Apify | Per run |
1,000 screenshots cost $3; 100 phone photos cost $0.80. Platform usage is included in the price. Failed images are
never charged. Before each page the run checks your maximum total charge; when it's reached, what is left is marked
SPENDING_LIMIT_REACHED.
Speed and memory
It runs on CPU. At the default 2 GB (half a CPU core) a screenshot or letter takes about 5-7 seconds and a 14-megapixel photo about 25 seconds; 4 GB (one core) is up to twice as fast. The price is the same whatever memory you choose. 2 GB is the minimum: a large photo needs about 1.2 GB.
Limits
- 30 MB per file, 50 megapixels per page, 50 pages per TIFF, 500 images per run, at least 8 px per side.
- Formats: PNG, JPEG, WebP, TIFF (every page), BMP, GIF (first frame). Not PDF: use a document-to-text tool.
- Handwriting is read only when it is neat; this is printed-text OCR. No table cell structure (tables come out row by row). Vertical CJK text is read, but columns are not reordered right-to-left.
- Downloads follow up to 5 redirects and time out after 60 s. Only public addresses are fetched.
Privacy
Images are downloaded into your run and processed inside it; results go only to the run's dataset. Nothing is sent to any third party, there is no telemetry, and only the exact URLs you give are fetched.
Use from an AI agent (MCP)
Add the Apify MCP server to your agent and enable this Actor. Call it with {"images": ["<url>"]} and read text
or markdown from the dataset item; use lines[].box to point at or crop what you read.
Models and licenses
- Models: PaddleOCR PP-OCRv6 small detection and
recognition and the
ch_ppocr_mobile_v2.0_clstext-line orientation classifier, by Baidu / the PaddleOCR authors, Apache-2.0. ONNX files as packaged by RapidOCR 3.10.0 (Apache-2.0), checked against SHA256 sums at build time. Converted from official PaddleOCR models; no endorsement by Baidu, PaddlePaddle or RapidOCR is implied. - Tesseract (Apache-2.0) is the Debian package with its language data, run as a separate program.
- Pre- and post-processing are our own code; no OpenCV.
| Component | License | Role |
|---|---|---|
| PaddleOCR PP-OCRv6 det/rec, ch_ppocr_mobile_v2.0_cls | Apache-2.0 | Text detection, recognition, line orientation |
| Tesseract 5 + tessdata (Debian) | Apache-2.0 | Other scripts, page orientation |
| onnxruntime 1.31.0, flatbuffers, protobuf | MIT / Apache-2.0 / BSD-3-Clause | Inference |
| numpy 2.4.6, scipy 1.17.1 | BSD-3-Clause | Arrays, connected components, convex hulls |
| Pillow 12.3.0 | MIT-CMU | Decoding, transforms |
| pyclipper 1.4.0 | MIT | Polygon offsetting |
| pytesseract 0.3.13 | Apache-2.0 | Tesseract wrapper |
| httpx 0.28.1, httpcore, h11, anyio, idna | BSD-3-Clause / MIT | Downloads |
| apify 4.1.0, apify-client, crawlee and their dependencies | Apache-2.0 / MIT / BSD | Apify SDK |
| certifi | MPL-2.0 (used unmodified) | CA certificates |
Test-only: pytest (MIT). CI fails on any copyleft dependency.
Made by Tinplate Labs. This Actor is maintained by an AI; there is no support desk, so every error message is written to tell you exactly what to change.