OCR API - Image to Text, Photo to Text & Receipt OCR avatar

OCR API - Image to Text, Photo to Text & Receipt OCR

Pricing

from $2.40 / 1,000 image transcribeds

Go to Apify Store
OCR API - Image to Text, Photo to Text & Receipt OCR

OCR API - Image to Text, Photo to Text & Receipt OCR

Bulk image OCR and photo to text for receipts, screenshots and document photos. Extract text from image URLs, datasets or base64 as plain text or Markdown; optional receipt fields and table rows. $0.003 per image with text on Free and Bronze; no-text and failed results are free.

Pricing

from $2.40 / 1,000 image transcribeds

Rating

0.0

(0)

Developer

Yukai Lin

Yukai Lin

Maintained by Community

Actor stats

0

Bookmarked

5

Total users

2

Monthly active users

9 hours ago

Last modified

Features

Limited permissions

Share

OCR API for image OCR and photo to text: extract text from receipts, screenshots and photos in bulk via URLs, datasets or base64 as plain text, Markdown or conservative table rows.

Try the prefilled public road sign (one image, no account needed):

{"urls":["https://upload.wikimedia.org/wikipedia/commons/6/67/Japanese_Road_Sign_119_%28Street_name%29.jpg"]}

Example output from this image in plain text: {"success":true,"text":"関門大通り\nKanmon-odori","language":"ja","charged":true}. The text is kept in its original language.

Price: $0.003 per image with returned text on the Free and Bronze plans ($3 per 1,000); no start fee, and images without text or with failed results are not charged. Higher plans have discounts.

For batches, put one image URL per line in Image URLs, pick a dataset, or send base64 through the API. Choose Markdown (tables kept) for tables, then export JSON, CSV or Excel. Start with a clear image; crop or enlarge small text if needed. GIF reads its first frame only; PDF URLs are read page by page (up to 15 MB); SVG is unsupported.

Receipt / invoice fields

Choose Receipt / invoice fields ("mode":"receipt_fields") for an image of a receipt or invoice. The dataset row keeps the full OCR text and adds receipt: {vendor, date, total, currency, review}. For example, an image transcribed as Vendor: ACME SUPPLIES\nInvoice Date: 2026-10-18\nTotal: USD 105.00 yields {"vendor":"ACME SUPPLIES","date":"2026-10-18","total":105,"currency":"USD","review":false}. Only explicit labels are used; an unlabeled shop name is not guessed, and Subtotal, Tax and Invoice No are not extracted as the total or vendor. Taiwan invoices: 發票日期/日期 (Gregorian dates such as 2024-07-16, 2024/7/16 or 2024年7月16日) and 總計/應付總額 are read, with 新臺幣, NT$ or TWD as currency TWD; for example 發票日期:2024-07-16\n總計:NT$945 yields "date":"2024-07-16","total":945,"currency":"TWD". 小計, 營業稅/稅額 and 發票號碼 are never the total; ROC-year dates (113/07/16), a bare $ or 元 and two different totals give null or review:true. Brazilian receipts (NFC-e, nota fiscal): Data de emissão/Emissão and Valor a Pagar/Valor total are read, with R$ or BRL as currency BRL; when both totals are printed, Valor a Pagar (after discounts) is the total, so Data de emissão: 28/10/2026\nValor total R$ 138,74\nValor a Pagar R$ 130,74 yields "date":"2026-10-28","total":130.74,"currency":"BRL". Spanish receipts and facturas: Fecha/Fecha de emisión and Total/Importe total, with € or EUR; Fecha: 28/10/2026\nTotal: 24,50 € yields "date":"2026-10-28","total":24.5,"currency":"EUR". Day-first dates (28/10/2026) and decimal commas (130,74, 1.234,56) are read only next to these Brazilian and Spanish labels. Indonesian receipts: a Total with Rp or IDR next to the amount is read as rupiah, with a dot or comma grouping thousands, so TOTAL Rp 38.000 and TOTAL Rp 95,000 yield "total":38000 and "total":95000 with "currency":"IDR"; Subtotal, Cash, Change and tax lines are not the total, and a dot amount without Rp/IDR (Total 38.000) or a rupiah amount with a dot before two digits (Rp 38.00) is left null with review:true. Valor total dos tributos, Desconto, IVA, Subtotal, due dates and a bare $ (never taken as reais) are not used; two different amounts with the same label rank give review:true. Missing or ambiguous values are null, with review:true when the date, total or currency needs checking. Even review:false is not an accuracy guarantee: check fields against the source image before accounting use.

This mode supports image and PDF URLs. Each PDF page with text uses one existing image-ocr charge; blank and failed pages are free. The price and event are unchanged.

Choose Table rows ("mode":"table_rows") for a regular table in an image or PDF. The result keeps the Markdown transcription in text and adds tables: [{"columns":["SKU","Qty"],"rows":[["A1","2"]]}] when a complete pipe table has a header, separator and consistent rows. Outer pipes are optional, as in GitHub Markdown: SKU | Qty / --- | --- / A1 | 2 gives the same result as | SKU | Qty |; a row with fewer or more cells is never padded or trimmed. tableReview:true with tables:[] means no complete regular table was found, or a row is irregular; check text against the source. Empty or merged-looking cells, broken lines, partial image transcriptions and images sent through the long-page OCR route also need review. OCR can still misread a cell even when tableReview:false, so check important values. Each successful image or individual PDF page has one row and the existing single image-ocr event. In mergePdfPages:true, each item of pages keeps its pageNumber, text, and, for successful pages, tables and tableReview; charges remain per text-bearing page.

Review reasons. Every successful row also has reviewReasons, one list that downstream code can filter on in any mode: truncated (only part of a long text was read, truncated:true), table_irregular (tableReview:true) and receipt_ambiguous (receipt.review:true). It is [] when none of these flags is set. A receipt with no Total line sets receipt.review:true, so its row has ["receipt_ambiguous"], for example {"success":true,"text":"Subtotal 10.00\nTax 0.80","receipt":{"total":null,"review":true,...},"reviewReasons":["receipt_ambiguous"],"charged":true}. It is not a confidence or accuracy score: an empty list does not mean the text was read correctly, so still check important values against the source. In mergePdfPages:true, each successful item of pages has its own list and the file row lists each reason found on any page once. Failed and no-text rows have no reviewReasons. Charges are unchanged: one image-ocr event per text-bearing image or PDF page.

Form checkboxes ("preserveCheckboxes": true, images only, off by default). A form checkbox with readable label text next to it and a clear state is written in its place as ☐ (empty), ☑ (check mark) or ☒ (X mark: selected, not "void"), for example ☑ I agree\n☐ Send me news. A box whose state is unclear (also a partly filled or indeterminate box) keeps only its label, and no words such as "checked" or "unchecked" are added. An image whose answer is only ☐/☑/☒, whitespace and Markdown list or table layout (- * + | :) is no_text and not charged; anything else visible, including a plain letter X, a digit or a symbol such as @, is one image-ocr event as usual. The event and its price are unchanged; the only billing difference is that such a symbols-only image, charged with the option off, is free with it on. Limits: when a long or dense image is read through the page route (route:"page"), that route gets no checkbox instruction, so symbols are not guaranteed and the row has checkboxReview:true (not part of reviewReasons). PDF pages ignore this option, are read as before and never have checkboxReview. Box states are read by an AI model and can be wrong: check important answers against the image. With the option off, prompts, rows and charges are unchanged.

What does Image to Text OCR do?

Give it images and get their text back:

  • 🖥️ Screenshots to text: terminal output, app screens, web pages, chats
  • 🧾 Receipts, invoices and photos of documents: line items and totals, in reading order
  • 📦 Bulk OCR: a whole list of image links in one run, several at a time; every row says which image it belongs to, and one bad link never stops the rest
  • 🪧 Signs and labels in photos
  • 🌏 Many languages and scripts: English, German, Dutch..., Chinese, Japanese, Korean; the text stays in its original language (nothing is translated)
  • 📊 Tables: kept as Markdown tables in Markdown mode, or as one line per row in Plain text mode

Each result has the text, lineCount, charCount and a language guess. Images come from URLs (Google Drive, Dropbox and OneDrive share links work), from another Actor's dataset, or as base64. Images without text can be reported as no_text; no_text results are not charged.

The OCR is done by an AI vision model (Llama 4 Scout) with a strict "transcribe verbatim, do not translate or summarize" instruction. The Actor downloads each image itself (so sites that block cloud servers still work), straightens it using its EXIF rotation, puts transparent images on white and scales very large images down to 2,048 px. A link to a web page instead of an image (for example a Wikimedia Commons file page) reads that page's main image.

Use cases

  • Expense and receipt capture: turn receipt photos into text for bookkeeping or an AI extractor
  • Screenshot search: make screenshots and scanned notes searchable
  • E-commerce and marketplace data: read text printed on product images and labels
  • Social media and meme archives: text inside images for moderation or search
  • AI agents: "What does this image say?" as a tool call

How much does it cost?

EventPrice
Image transcribed$3.00 / 1,000 images ($0.003 per image)

Example: 1,000 receipt photos with text cost 1,000 × $0.003 = $3.00 on the Free plan; images with no text are free.

No start fee. Higher Apify plans get volume discounts (see the Pricing tab). Images with no text, images that cannot be downloaded and unsupported files are not charged; they carry an errorType (no_text, not_found, blocked, too_large, unsupported, network, invalid_input, model_refused, ocr_unavailable...). Every row has charged: true or false, and failed rows end with "(not charged)".

Control your cost

  • Before anything is read, the status and run log show two amounts: if all sources are single-page images (one event per source), and the true upper limit if every unknown URL is a PDF with up to maxPdfPages pages (50 by default). For one image URL on Free, these are $0.003 and $0.15. SUMMARY.costPlan.singlePageImageScenarioUsd and worstCaseUsd keep both amounts. The upper limit is compared with your maximum charge per run; it is not the price of one image. Images and PDF pages without text, and failed items, are free.
  • Charged: an image whose text was returned ($0.003). This includes a long text that was cut off (route: "image-truncated", used only when the long-text route fails): the part that was read is returned and charged.
  • Free: images without text (a plain one-colour image such as a blank white page is no_text without being read, and so is an AI answer that only says the image is blank or has no text; a single word or digit is text and charged once), images that could not be downloaded or read, images that hit the time limit per image (180 seconds by default), lines that are not a URL, and the same URL given twice (processed once). A failed download says why when the server does, e.g. image returned HTTP 400: Use thumbnail sizes listed at ....
  • Maximum charge per run: set it in the run options (Console) or with maxTotalChargeUsd (API). When the next image could exceed it, no new image is started; the run ends with status LIMIT_REACHED, and the SUMMARY record lists the images not processed (notProcessed: the count and up to 100 inputs).
  • If Apify restarts the run (server migration or Resurrect), items already finished are skipped and not charged again (SUMMARY.resumedSkipped).

Price comparison (checked September 2026)

ActorPrice per image
Image to Text OCR (this Actor)$0.003, no start fee, images without text free
zen-studio/google-lens-ocr$0.00499
zenomastro/image-ocr-text-extractor$0.005
s-r/google-lens-ocr$0.006
lergassy/ocr-text-extractor$0.002 per file

How to use it

  1. First try one clear image with visible text. Paste its image URL (or more links, one per line, for a batch); you can also upload a text/CSV file of links in the URL list below, pick a dataset from another run (one image URL or an array of up to 100 per item), or send base64 images through the API.
  2. Choose the output format: Plain text (default), Markdown (tables kept), Table rows or Receipt / invoice fields.
  3. Optional: a language hint such as "Japanese" for small or stylized script.
  4. Click Start and export the results as JSON, CSV or Excel.

A text-free image can return no_text; a no_text result is free. If visible text was missed, try cropping or enlarging the text area and retry; Language hint (optional) may help. Only the first GIF frame is read, and PDF URLs are read page by page; SVG is unsupported.

Input example

This is the single-image prefill; it previously produced the Japanese sign text shown above:

{
"urls": [
"https://upload.wikimedia.org/wikipedia/commons/6/67/Japanese_Road_Sign_119_%28Street_name%29.jpg"
]
}

Output examples (real results, September 2026)

A supermarket receipt photographed on a table (Dutch), Markdown mode:

{
"imageUrl": "https://thumb.wikimedia.org/wikipedia/commons/thumb/b/b3/Sahan_Supermarket_receipt%2C_Hillegersberg%2C_Rotterdam_%282021%29_02.jpg/1920px-...jpg",
"success": true,
"mode": "markdown",
"text": "# SAHAN Supermarkt\nBergweg 170\n3036 BL Rotterdam\nTel: 010-265.33.99\n\n## KASSABON\n\nNummer 2-00005155\nDatum: 26/01/2021\nTime : 11.29\nCaissier Tuge\nKassa: 1\n\n| Omschrijving | Bedrag |\n|--------------|--------|\n| MIRAS OLijfoute (glas) 1L | 3,99 |\n| MERAY TRAKY A POMPOENPIT 200 | 1,99 |\n| PINAR POMPOENPITTEN 200G | 1,99 |\n| ULKER PETIBOR BISKUVI 1KG | 2,49 |\n| BROOD SIMIT * 5 x 0.75 | 3,75 |\n\nTOTAAL Incl: 14,21\nPIN Betaling 14,21\nTeruggave 0,00\nBTW Bedrag: 1,17\n\n6 Artikel / 9 Stuks\nD. uwel en tot ziens",
"lineCount": 23,
"charCount": 529,
"language": "nl",
"languageName": "Dutch",
"imageFormat": "jpeg",
"width": 1920,
"height": 3413,
"route": "image",
"imageBytes": 2315283
}

All prices, totals, the date and the address are right. Mistakes on this crumpled receipt: "OLijfoute (glas)" (printed OLIJFOLIE (GLAS); the Plain text run read it correctly), "TRAKY A" (TRAKYA), "Tuge" (Tugce), "11.29" (11:29).

A Japanese road sign, Plain text mode: "text": "関門大通り\nKanmon-odori", "language": "ja" (exact).

A Japanese terminal screenshot (24 lines, English and Japanese): every process line, the uname output, the Japanese date 2006年 10月 14日 土曜日 19:53:00 JST and こんにちはウィキペディア were read correctly.

A landscape photo without text: "success": false, "errorType": "no_text", not charged.

If the AI model declines to read an image (an answer like "I can't help with this request") the image is asked once more with another prompt; if it still declines, the row is "success": false, "errorType": "model_refused" and not charged. If the OCR service itself does not answer (busy or a server error), the row is "errorType": "ocr_unavailable" and not charged, and the image can simply be run again. A refusal is never returned as the transcription.

route says how the text was read: image (one vision call), page (long or very dense text: when the first answer reaches its length limit of about 2,000 tokens, or the vision call cannot finish in time, the image is read again through our page-OCR route, which allows answers of up to about 4,096 tokens), image-tiles (a tall image whose first answer was cut off and whose page read failed: the upper and lower regions were both read), or image-truncated (only partial text could be kept, with truncated: true). Region reads may repeat a line near their overlap; identical receipt items are preserved. A SUMMARY record in the key-value store has the run status (SUCCESS, PARTIAL_RESULTS, FAILED, NO_RESULTS, LIMIT_REACHED) and counts of transcribed, no-text and failed images. An image without text is a valid answer, not a failure: a run where every image was either transcribed or had no text ends as SUCCESS. Every row has index (the input order: URLs first, then base64 images) and input; rows from a dataset also have datasetItemIndex, and base64 images sent as {"id": ..., "data": ...} return their id.

Accuracy (our test, September 2026)

We checked the output against the images by eye:

ImageResult
Receipt photo (Dutch, crumpled)All numbers, totals, date and address right; 2-4 misread words per run
Japanese road signExact
Japanese + English terminal screenshotCorrect, including the Japanese lines
Wikipedia history table (screenshot, 7 rows × 13 columns)All dates, sizes, size changes and comments right; some cells shifted by one column and the user names (hidden in the image) left empty
English terminal ls -l screenshot (long text)All file names, sizes and times right, but written as a Markdown table, and single-digit days ("Jun 1") were dropped
Hong Kong restaurant sign photo (stylized Chinese, at an angle)About 60% of the dishes read correctly; the phone number right; the large calligraphic sign and some characters wrong
Poem photographed upside down, 90° turned, tilted 5°, blurred, as WebP, with transparency (test of October 2026)Exact in every case; the upside-down one is read again turned upright (turnedUpright)
Same poem shrunk to 300 px wide (JPEG) / 150 px wideExact / 4-6 misread words
Swiss restaurant receipt (German), TIFF page of English text, Hindi page, Chinese poem (5 lines)All right (the receipt: names, items, totals, VAT)
Landscape photo, blank image, 1×1 pixel imageCorrectly reported as no text, not charged

In short: printed text, screenshots and receipts work well; stylized, handwritten, angled or very small text is less reliable, and the AI can misread or occasionally drop characters. Check important values (amounts, IDs) before you rely on them.

Use with AI agents (MCP)

Connect Apify's MCP server (https://mcp.apify.com?tools=tidytools/image-to-text-ocr) to Claude, Cursor or any MCP client, then ask e.g. "Read the text in these three receipt photos and give me the totals."

{ "urls": ["https://example.com/receipt.jpg"], "mode": "text" }

Failed images and images without text are not charged and carry an errorType.

Use it from code

curl -X POST "https://api.apify.com/v2/acts/tidytools~image-to-text-ocr/run-sync-get-dataset-items?token=YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"urls":["https://upload.wikimedia.org/wikipedia/commons/6/67/Japanese_Road_Sign_119_%28Street_name%29.jpg"],"mode":"text"}'

Base64 input: {"imagesBase64": ["data:image/png;base64,iVBORw0..."]}, or with your own ids: {"imagesBase64": [{"id": "receipt-17", "data": "iVBORw0..."}]}. The URL-list field imageUrls ([{"url": ...}]) is also accepted, but Apify rejects the whole run (HTTP 400) when it holds a blank line or a bare domain; urls (plain strings) never fails the run.

Schedules and integrations: run it daily or weekly with Apify Schedules, get a webhook when a run finishes, or send the results to Zapier, Make, n8n, Google Sheets, Slack and other apps with Apify integrations. Results can be exported as JSON, CSV, Excel or XML.

Advanced settings

  • Proxy: optional Apify Proxy for downloading images from sites that block data-center requests (billed to your Apify account). Without a proxy, a refused download (HTTP 401/403/429) is retried with an honest bot user agent, then with a referrer from the image's own site (hotlink protection).
  • Parallel images: how many images are processed at the same time (1-10).
  • Time limit per image: 180 seconds by default (30-300); a dense page of text can need 1-3 minutes. An image that takes longer (a stuck download or a very long text) fails with errorType: "timeout" and is not charged, so one slow image cannot hold the run.

Limitations

  • Formats: JPEG, PNG, WebP, GIF (first frame), AVIF, HEIC/HEIF (iPhone photos) and TIFF, up to 20 MB and 100 megapixels. SVG is not supported. PDF URLs up to 15 MB are read page by page.
  • Very large images are scaled down to 2,048 px on the long side, so tiny print in huge images can be lost; crop them first.
  • Very long text (more than about 2,000 tokens, e.g. a full restaurant menu) goes through the page route, which answers in Markdown and tends to turn column-aligned text into tables; in Plain text mode those tables become tab-separated lines. Dot leaders between a dish and its price ("Soup ........ 2,50") are written as " ... ". If that page read fails for a tall image, up to two extra upper/lower region reads may run within Time limit per image. This can take longer and can still return image-truncated with truncated: true when either region is incomplete. A text-bearing image still has one image-ocr charge and one Dataset row.
  • Photos turned 90° without an EXIF rotation tag are read as they are. Photos that are upside down are detected by a quick orientation check (it runs alongside the first read, so it adds no waiting time) and read again turned upright; such rows carry turnedUpright: true. This costs you nothing extra. Very small or blurry text (about 150 px wide for a whole page) still gets misread words; use a larger version when you can.
  • The language value is a guess from the script and common words; for Latin-script text it tells apart English, German, Dutch, French, Spanish, Italian, Portuguese, Polish, Swedish and Vietnamese (und when unclear).
  • AI OCR can misread characters, and on rare occasions skip a line; it does not return per-word positions or confidence scores.

FAQ

Why did I get no_text? The model found no readable text. Crop to the printed area, use a larger image, or add a language hint for small or stylized text. A no_text row is free.

Why did an image time out? A slow download or dense page can exceed Time limit per image (180 seconds by default). Try a smaller crop or raise the limit (up to 300 seconds). A timeout row is free.

Why was my input invalid? Use a full public HTTPS image URL in Image URLs (urls), one per line; remove blank lines and private addresses. Invalid base64 also gives invalid_input. PDF URLs are read page by page (up to 15 MB); SVG is unsupported. The imageUrls file/list editor can reject a whole run if it contains a blank line or bare domain; urls reports bad lines as free rows.

Why could the image not be downloaded? not_found means the link is missing; blocked means the host refused the request. Try a public direct image link or an image you have permission to use, and follow the source site's robots.txt. The Actor does not log in or bypass a login wall. Optional Apify Proxy can help with permitted public sources and is billed by Apify.

When am I charged? Only a row with returned image text is charged: $0.003 on Free and Bronze plans, including a partial transcription marked image-truncated. no_text, invalid inputs, failed downloads, unsupported images, model failures and timeouts are not charged; duplicates run once. Check each row's charged field.

Support

Open an issue in the Issues tab with the image URL. Issues are checked regularly.

Found this useful? A short review on the Store helps other users find it.

PDF pages / PDF 逐頁處理

Paste a public PDF URL in urls (or a dataset URL field). Text-layer pages are read directly; scanned pages use the existing OCR backend. Choose mode:"markdown" to retain scanned tables or mode:"table_rows" to extract only regular Markdown pipe tables (with or without outer pipes). Text-layer pages without such a table have tables:[] and tableReview:true. pageRange:"1-5,8" selects one-based pages, maxPdfPages defaults to 50, and mergePdfPages:true returns one row per file instead of one row per page. PDFs may be up to 15 MB. preserveCheckboxes does not apply to PDF pages: they are read as before, without checkbox symbols or checkboxReview.

在 urls 或資料集網址欄位貼上公開 PDF 網址。有文字層的頁面直接取字,掃描頁使用既有 OCR 後端;mode:"markdown" 保留掃描表格。pageRange:"1-5,8" 選擇頁碼;maxPdfPages 預設 50;mergePdfPages:true 合併成每檔一列。PDF 上限 15 MB。preserveCheckboxes 不套用到 PDF 頁面:照原方式讀取,不加方框符號,也沒有 checkboxReview。

A PDF page row includes imageUrl (original source), pdfUrl, pageNumber, pdfPageCount, text, route, charged, and the usual language/count/error fields. Combined rows include pages and chargedPages. Each page with text costs one unchanged image-ocr event ($0.003 on Free/Bronze); blank pages, skipped pages, failed downloads and failed OCR are free. The Free plan uses the existing free OCR route.

If a merged PDF reaches the maximum charge before its last page, the partial segment is saved. Resuming that run appends a second merged row for the remaining pages without charging the first segment again.

If no selected page succeeds, a merged row uses no_text when every page is blank and ocr_unavailable when any page's OCR backend fails, including a mix of blank and failed pages. The pages array keeps each page's errorType; neither kind is charged.

PDF 頁面列含 imageUrl(原始網址)、pdfUrl、pageNumber、pdfPageCount、text、route、charged 與原有語言/字數/錯誤欄位。合併列另有 pages 與 chargedPages。每個有文字的頁面沿用一次 image-ocr 事件(Free/Bronze 每次 $0.003);空白、超出上限、下載失敗或 OCR 失敗的頁面免費。免費方案沿用既有免費 OCR 路徑。

合併模式若在最後一頁前達到收費上限,會先儲存已完成的部分;續跑時會為剩餘頁面新增一列,不重複收取第一段費用。

FAQ — What if a PDF mixes text and scans? Text-layer pages use extracted text, and pages without text go through OCR. A scanned table in Markdown mode is returned as a Markdown table when OCR recognizes it.

**常見問題:PDF 同時有文字層和掃描頁?**有文字層的頁面直接取字,無文字的頁面才用 OCR;Markdown 模式下,辨識出的表格以 Markdown 表格回傳。

{"actor":"image-ocr","cases":[{"name":"nutrition_label_table_rows","input":{"urls":["https://upload.wikimedia.org/wikipedia/commons/e/e9/FDA_Nutrition_Facts_Label_2016.png"],"mode":"table_rows"},"expectedEvents":{"image-ocr":1},"expect":"One text-bearing image gives one image-ocr event and one row. Keep Markdown text; return a complete regular pipe table in tables or tables:[] with tableReview:true for manual review."},{"name":"japanese_road_sign_text","input":{"urls":["https://upload.wikimedia.org/wikipedia/commons/6/67/Japanese_Road_Sign_119_%28Street_name%29.jpg"],"mode":"text"},"expectedEvents":{"image-ocr":1},"expect":"One text-bearing image gives one image-ocr event and one plain-text row."},{"name":"sample_ocr_pdf_pages","input":{"urls":["https://upload.wikimedia.org/wikipedia/commons/c/c3/Sample_OCR.pdf"],"pageRange":"1-2","mode":"table_rows"},"expectedEvents":{"image-ocr":2},"expect":"Two text-bearing PDF pages give two image-ocr events and two rows. Each retains text and has either a regular table or tableReview:true; blank or failed pages are free."},{"name":"checkbox_sample_form","input":{"urls":["https://raw.githubusercontent.com/jetchegoyen/checkbox_scanner/main/tests/form-images/sample-form.png"],"preserveCheckboxes":true},"expectCharges":{"image-ocr":1},"expectRows":1,"expect":"Labelled vertical form (annotation sample-form.json: 3 selected, 2 empty): one charged row, image-ocr=1. Compare each written ☐/☑/☒ with the annotation; at least 4 of 5 boxes right; no invented state words."},{"name":"checkbox_grid_boxes_without_text","input":{"urls":["https://raw.githubusercontent.com/jetchegoyen/checkbox_scanner/main/tests/form-images/grid-form.png"],"preserveCheckboxes":true},"expectCharges":{},"expectRows":1,"expect":"Boxes-only grid (grid-form.json has 5 bboxes about 50 px apart and no label fields; no readable text): one no_text row, charged:false, no charge (image-ocr=0). Any invented word fails Q1."},{"name":"checkbox_nutrition_no_boxes","input":{"urls":["https://upload.wikimedia.org/wikipedia/commons/e/e9/FDA_Nutrition_Facts_Label_2016.png"],"preserveCheckboxes":true},"expectCharges":{"image-ocr":1},"expectRows":1,"expect":"Text without checkboxes: one charged row, image-ocr=1, and no ☐/☑/☒ in text (0 invented boxes)."},{"name":"checkbox_x_mark_labels","input":{"urls":["https://jncogs.github.io/JNC-Checkbox/images/example-windows.jpg"],"preserveCheckboxes":true},"expectCharges":{"image-ocr":1},"expectRows":1,"expect":"X-style checkboxes with readable labels: marked boxes are written ☒, never as void; one charged row, image-ocr=1."},{"name":"checkbox_gui_screenshot_labels","input":{"urls":["https://upload.wikimedia.org/wikipedia/commons/7/7f/Checkbox.PNG"],"preserveCheckboxes":true},"expectCharges":{"image-ocr":1},"expectRows":1,"expect":"GUI screenshot of the three checkbox states with labels (its Commons vector copy File:Checkbox_States.svg is a 'vector version of PNG screenshot' with embedded text): one charged row, image-ocr=1. Text must be the labels in the image; the partly filled state is not guessed; words not in the image fail Q1."},{"name":"sample_ocr_pdf_checkbox_option_ignored","input":{"urls":["https://upload.wikimedia.org/wikipedia/commons/c/c3/Sample_OCR.pdf"],"pageRange":"1-2","preserveCheckboxes":true},"expectCharges":{"image-ocr":2},"expectRows":2,"expect":"PDF regression: the option does not apply to PDFs; two text-bearing pages, two rows, image-ocr=2, no checkboxReview."},{"name":"cord_indonesian_receipt_fields","input":{"urls":["https://raw.githubusercontent.com/clovaai/cord/master/figure/sample.png"],"mode":"receipt_fields"},"expectedEvents":{"image-ocr":1},"expectRows":1,"expect":"Public CORD (CC-BY-4.0) Indonesian receipt sample: one charged row, image-ocr=1, full text kept. Record the model text: when it has exactly one TOTAL line with Rp/IDR (e.g. TOTAL Rp 38.000), receipt.total is that amount as a number (38000) and currency IDR; Subtotal/Cash/Change lines are never the total; several different totals give total:null, review:true. A misread amount in the text is a model error, not a parser error. For CORD v2 test 021 itself take the current signed image URL from https://datasets-server.huggingface.co/rows?dataset=naver-clova-ix%2Fcord-v2&config=default&split=test&offset=21&length=1 (ground truth total_price Rp 38.000)."},{"name":"japanese_road_sign_receipt_fields","input":{"urls":["https://upload.wikimedia.org/wikipedia/commons/6/67/Japanese_Road_Sign_119_%28Street_name%29.jpg"],"mode":"receipt_fields"},"expectedEvents":{"image-ocr":1},"expectRows":1,"expect":"Schema prefill image, not a receipt: one charged row, image-ocr=1; receipt has vendor/date/total/currency null and review:true (no labels, nothing guessed)."},{"name":"blank_image_receipt_fields","input":{"urls":["https://upload.wikimedia.org/wikipedia/commons/thumb/7/70/Solid_white.svg/250px-Solid_white.svg.png"],"mode":"receipt_fields"},"expectCharges":{},"expectRows":1,"expect":"Solid white PNG without text: one no_text row, charged:false, image-ocr=0."}]}