<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: OCR-AI</title>
    <description>The latest articles on DEV Community by OCR-AI (@ocrai).</description>
    <link>https://dev.to/ocrai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4232008%2F52e1c7fa-a497-43fa-ab28-043540906f34.png</url>
      <title>DEV Community: OCR-AI</title>
      <link>https://dev.to/ocrai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ocrai"/>
    <language>en</language>
    <item>
      <title>Giving Claude and Cursor a Hebrew OCR tool over remote MCP</title>
      <dc:creator>OCR-AI</dc:creator>
      <pubDate>Sat, 07 Nov 2026 11:47:50 +0000</pubDate>
      <link>https://dev.to/ocrai/giving-claude-and-cursor-a-hebrew-ocr-tool-over-remote-mcp-36ho</link>
      <guid>https://dev.to/ocrai/giving-claude-and-cursor-a-hebrew-ocr-tool-over-remote-mcp-36ho</guid>
      <description>&lt;p&gt;&lt;em&gt;By the OCR-AI team&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Language models are decent at reading images now, but ask one to pull the allocation number, VAT and line items out of a scanned Hebrew invoice and check them against a spreadsheet, and you'll spend more time correcting it than you saved. A better split is to give the agent a tool that reads the document into structured data, and let the model do the reasoning on top.&lt;/p&gt;

&lt;p&gt;This post shows how to connect our remote MCP server to an MCP client like Claude or Cursor, what the tools do, and a few things we learned about running OCR behind MCP.&lt;/p&gt;

&lt;h2&gt;
  
  
  The server
&lt;/h2&gt;

&lt;p&gt;The endpoint is &lt;code&gt;https://ocr-ai.com/mcp&lt;/code&gt;. It uses the Streamable HTTP transport and is stateless: every POST gets one JSON response, with no session and no event stream. Supported protocol versions are 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;initialize&lt;/code&gt; and &lt;code&gt;tools/list&lt;/code&gt; work without a key, so a client can discover the tools before you've set anything up. Actually calling a tool needs an API key, sent as a Bearer token.&lt;/p&gt;

&lt;p&gt;There are three tools:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Arguments&lt;/th&gt;
&lt;th&gt;Returns&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;read_document&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;file_url&lt;/code&gt; or &lt;code&gt;file_base64&lt;/code&gt;; optional &lt;code&gt;pages&lt;/code&gt; (e.g. &lt;code&gt;"1-5,8"&lt;/code&gt;), &lt;code&gt;hint&lt;/code&gt; (&lt;code&gt;auto&lt;/code&gt;, &lt;code&gt;invoice&lt;/code&gt;, &lt;code&gt;receipt&lt;/code&gt;, &lt;code&gt;card&lt;/code&gt;, &lt;code&gt;table&lt;/code&gt;, &lt;code&gt;text&lt;/code&gt;), &lt;code&gt;language&lt;/code&gt; (&lt;code&gt;en&lt;/code&gt; or &lt;code&gt;he&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Markdown text, plus &lt;code&gt;structuredContent&lt;/code&gt; with the full document object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;check_balance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;balance, whether pages expired, a &lt;code&gt;buy_url&lt;/code&gt;, the packs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;buy_pages&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pack&lt;/code&gt; (&lt;code&gt;p100&lt;/code&gt;, &lt;code&gt;p500&lt;/code&gt;, &lt;code&gt;p2000&lt;/code&gt;), &lt;code&gt;currency&lt;/code&gt; (&lt;code&gt;USD&lt;/code&gt; or &lt;code&gt;ILS&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;a &lt;code&gt;buy_url&lt;/code&gt; for that pack&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Getting a key
&lt;/h2&gt;

&lt;p&gt;Keys come from the agents page. You leave an email, get a sign-in link (no password, no card), and create a key on your account page. The key is shown once, so copy it then. An account's first key starts with 100 free pages.&lt;/p&gt;

&lt;p&gt;Keep the key on the server side or in your local client config. Don't put it in a web page or a mobile app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;Most MCP clients that support remote servers take a URL and headers. This is the config from our docs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ocr-ai"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://ocr-ai.com/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer ocrai_sk_…"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;ocrai_sk_…&lt;/code&gt; with your key. In Cursor that block goes in your MCP config file (&lt;code&gt;mcp.json&lt;/code&gt;). Clients differ a little in where the file lives and whether they want &lt;code&gt;"type": "http"&lt;/code&gt; or another name for the transport, so check your client's own docs if it doesn't pick the server up. The fields that matter are the URL and the &lt;code&gt;Authorization&lt;/code&gt; header.&lt;/p&gt;

&lt;p&gt;If you want to check the server before wiring up a client, you can call a tool with plain curl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://ocr-ai.com/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer ocrai_sk_…"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept: application/json, text/event-stream"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc": "2.0", "id": 1, "method": "tools/call",
       "params": {"name": "read_document",
                  "arguments": {"file_url": "https://example.com/invoice.pdf"}}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What a session looks like
&lt;/h2&gt;

&lt;p&gt;Once connected, you can just ask. Something like "read this invoice and tell me the vendor, invoice number and total" with a link to a PDF. The agent calls &lt;code&gt;read_document&lt;/code&gt; with &lt;code&gt;file_url&lt;/code&gt; and &lt;code&gt;hint: "invoice"&lt;/code&gt;, and gets back fields like &lt;code&gt;vendor_name&lt;/code&gt;, &lt;code&gt;document_number&lt;/code&gt;, &lt;code&gt;issue_date&lt;/code&gt; and &lt;code&gt;total&lt;/code&gt;, each with a normalized value (dates as &lt;code&gt;YYYY-MM-DD&lt;/code&gt;, amounts as plain decimals), plus the line-item table and the full text in reading order.&lt;/p&gt;

&lt;p&gt;Because the field keys are fixed, the model doesn't have to guess what "סה״כ לתשלום" means. It's &lt;code&gt;total&lt;/code&gt;. That makes follow-up work much more reliable: comparing three supplier quotes, checking that a delivery note matches a purchase order, or filling a CSV with one row per invoice.&lt;/p&gt;

&lt;p&gt;Fields that were hard to read come back with &lt;code&gt;"uncertain": true&lt;/code&gt;. It's worth telling the agent, in your prompt or rules file, to point those out rather than silently using them.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;file_url&lt;/code&gt; has to be a public https link with no credentials in it, and the download must finish within 20 seconds. For a local file, the client can send &lt;code&gt;file_base64&lt;/code&gt; instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Errors are written for the model
&lt;/h2&gt;

&lt;p&gt;This is the design choice we'd recommend to anyone building an MCP server. Problems a model can act on don't come back as protocol errors. They come back as tool results with &lt;code&gt;isError: true&lt;/code&gt;, a plain sentence, and a code in &lt;code&gt;structuredContent.error&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The main example is running out of pages. A call is checked before anything is read, and if the account has fewer pages than the call needs, nothing is read and nothing is charged. The tool result says how many pages were needed, how many are left, and includes a &lt;code&gt;buy_url&lt;/code&gt;. The agent can hand that link to you, you pay on Stripe's page, and the agent calls again. The link only adds pages to the key's account; it doesn't sign anyone in, so it's safe to pass along.&lt;/p&gt;

&lt;p&gt;Other codes the model can handle the same way: &lt;code&gt;missing_key&lt;/code&gt; and &lt;code&gt;invalid_key&lt;/code&gt; (with a link to get a key), &lt;code&gt;rate_limited&lt;/code&gt;, &lt;code&gt;bad_pages&lt;/code&gt;, &lt;code&gt;fetch_failed&lt;/code&gt;, &lt;code&gt;unsupported_type&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Timeouts
&lt;/h2&gt;

&lt;p&gt;One practical gotcha. A &lt;code&gt;read_document&lt;/code&gt; call can cover up to 20 pages, and many pages can take more than a minute. MCP clients stop waiting after a set time; the MCP TypeScript SDK defaults to 60 seconds. If you see timeouts on long PDFs, raise the timeout for that tool if your client allows it, or ask the agent to read in smaller ranges with &lt;code&gt;pages&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Billing on timeouts is spelled out in the docs: if the connection closes before the answer is sent, the call is refunded in full. If the client stops waiting but keeps the connection open, those pages are charged. Failed pages are always given back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why remote and not local
&lt;/h2&gt;

&lt;p&gt;A local MCP server that wraps Tesseract is easy to build, and for clean English text it's fine. For Hebrew invoices you'd still own the bidi handling, the layout logic and the field extraction. A remote server means one URL and one header in the config, and the same tool works from any client.&lt;/p&gt;

&lt;p&gt;The trade-off is that the document leaves your machine. Our docs and security page say files are read in memory and not stored on the server, but you should make that call for your own documents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;p&gt;Docs, including every error code: &lt;a href="https://ocr-ai.com/en/docs" rel="noopener noreferrer"&gt;ocr-ai.com/en/docs&lt;/a&gt;. There's a Markdown copy at &lt;code&gt;/docs.md&lt;/code&gt; you can drop into an agent's context. And if you want to see the output on your own invoice before setting anything up, the browser version reads 5 pages a day free with no signup.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>ocr</category>
      <category>hebrew</category>
    </item>
    <item>
      <title>Pulling fields out of Hebrew invoices with a REST API</title>
      <dc:creator>OCR-AI</dc:creator>
      <pubDate>Sat, 07 Nov 2026 11:46:11 +0000</pubDate>
      <link>https://dev.to/ocrai/pulling-fields-out-of-hebrew-invoices-with-a-rest-api-4l57</link>
      <guid>https://dev.to/ocrai/pulling-fields-out-of-hebrew-invoices-with-a-rest-api-4l57</guid>
      <description>&lt;p&gt;&lt;em&gt;By the OCR-AI team&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you've ever run a scanned Israeli tax invoice through a general-purpose OCR library, you know the output. The words come back in the wrong order, the phone number is split across two lines, and the total ends up somewhere next to the vendor's address. You can fix some of it with post-processing. Most people give up and retype.&lt;/p&gt;

&lt;p&gt;This post walks through reading Hebrew invoices and receipts with our REST API, and then spends some time on why this is harder than it looks, because that part applies no matter which tool you use.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one endpoint you need
&lt;/h2&gt;

&lt;p&gt;Reading a document is a single call: &lt;code&gt;POST https://ocr-ai.com/api/v1/read&lt;/code&gt;. It accepts PDF, JPEG, PNG and WebP, and you authenticate with an API key as a Bearer token (&lt;code&gt;Authorization: Bearer ocrai_sk_...&lt;/code&gt;; an &lt;code&gt;X-API-Key&lt;/code&gt; header also works).&lt;/p&gt;

&lt;p&gt;You can send the file three ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;multipart/form-data&lt;/code&gt;, with the file in a field called &lt;code&gt;file&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;JSON, with &lt;code&gt;file_base64&lt;/code&gt; or &lt;code&gt;file_url&lt;/code&gt; (a public https link)&lt;/li&gt;
&lt;li&gt;the raw bytes, with the file's own &lt;code&gt;Content-Type&lt;/code&gt;, and the options as query parameters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The options are the same in each case: &lt;code&gt;pages&lt;/code&gt; (like &lt;code&gt;"1-3"&lt;/code&gt; or &lt;code&gt;"1-5,8"&lt;/code&gt;), &lt;code&gt;hint&lt;/code&gt; (&lt;code&gt;auto&lt;/code&gt;, &lt;code&gt;invoice&lt;/code&gt;, &lt;code&gt;receipt&lt;/code&gt;, &lt;code&gt;card&lt;/code&gt;, &lt;code&gt;table&lt;/code&gt; or &lt;code&gt;text&lt;/code&gt;) and &lt;code&gt;language&lt;/code&gt; for labels and the summary (&lt;code&gt;en&lt;/code&gt; or &lt;code&gt;he&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Upload from disk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://ocr-ai.com/api/v1/read &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$OCRAI_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"file=@invoice.pdf"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"pages=1-3"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or point it at a URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://ocr-ai.com/api/v1/read &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$OCRAI_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"file_url": "https://example.com/invoice.pdf", "hint": "invoice"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing worth knowing: the server decides the file type from the bytes, not from the name or the declared type. And unknown fields are rejected with a &lt;code&gt;400 bad_request&lt;/code&gt; that names the offending field, which is nice when you typo &lt;code&gt;hnit&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes back
&lt;/h2&gt;

&lt;p&gt;The response is a document object. At the top level you get page accounting (&lt;code&gt;total_pages&lt;/code&gt;, &lt;code&gt;pages_read&lt;/code&gt;, &lt;code&gt;pages_charged&lt;/code&gt;, &lt;code&gt;balance&lt;/code&gt;, &lt;code&gt;next_pages&lt;/code&gt;), and then &lt;code&gt;pages[]&lt;/code&gt;, one entry per page. Each page that was read has a &lt;code&gt;document_type&lt;/code&gt;, a &lt;code&gt;language&lt;/code&gt;, a short &lt;code&gt;summary&lt;/code&gt;, plus &lt;code&gt;fields&lt;/code&gt;, &lt;code&gt;tables&lt;/code&gt; and the full &lt;code&gt;text&lt;/code&gt; in reading order.&lt;/p&gt;

&lt;p&gt;Fields are the interesting part. Each one has a &lt;code&gt;key&lt;/code&gt;, a human &lt;code&gt;label&lt;/code&gt;, the &lt;code&gt;value&lt;/code&gt; as printed, a &lt;code&gt;type&lt;/code&gt;, and where it applies a &lt;code&gt;normalized&lt;/code&gt; value and a &lt;code&gt;box&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"issue_date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"תאריך הפקה"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"21/10/2026"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"normalized"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-21"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"box"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;144&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;706&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;163&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;798&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;normalized&lt;/code&gt; is what you actually want to store: dates as &lt;code&gt;YYYY-MM-DD&lt;/code&gt;, money as a plain decimal (&lt;code&gt;"6,018.00 ₪"&lt;/code&gt; becomes &lt;code&gt;"6018"&lt;/code&gt;). The &lt;code&gt;box&lt;/code&gt; is &lt;code&gt;[ymin, xmin, ymax, xmax]&lt;/code&gt; on a 0-1000 scale relative to the page, so you can draw a highlight over the scan in a review UI. If a value was hard to read, the field carries &lt;code&gt;"uncertain": true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The keys are fixed. For invoices you'll see things like &lt;code&gt;vendor_name&lt;/code&gt;, &lt;code&gt;vendor_tax_id&lt;/code&gt;, &lt;code&gt;document_number&lt;/code&gt;, &lt;code&gt;allocation_number&lt;/code&gt;, &lt;code&gt;issue_date&lt;/code&gt;, &lt;code&gt;due_date&lt;/code&gt;, &lt;code&gt;subtotal&lt;/code&gt;, &lt;code&gt;vat_rate&lt;/code&gt;, &lt;code&gt;vat_amount&lt;/code&gt;, &lt;code&gt;total&lt;/code&gt; and &lt;code&gt;currency&lt;/code&gt;. Line items arrive as a table with &lt;code&gt;columns&lt;/code&gt; and &lt;code&gt;rows&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small Python script
&lt;/h2&gt;

&lt;p&gt;Here's a minimal script that reads an invoice and prints the header fields plus the line items. It uses only the documented endpoint and response shape.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OCRAI_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://ocr-ai.com/api/v1/read&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;KEY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;invoice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;language&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# multi-page calls can take over a minute
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;402&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;SystemExit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Out of pages: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;buy_url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;read_invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;invoice.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;done&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;page &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;page&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fields&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vendor_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vendor_tax_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;document_number&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;allocation_number&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;issue_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vat_amount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;flag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  &amp;lt;- check&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uncertain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;normalized&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tables&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;columns&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few details in there are deliberate. The long timeout is because a call can read up to 20 pages and that can take more than a minute. The per-page &lt;code&gt;status&lt;/code&gt; check matters because a call can succeed while one page fails; a failed page is given back (&lt;code&gt;refunded: true&lt;/code&gt;) and has an &lt;code&gt;error&lt;/code&gt; such as &lt;code&gt;engine_busy&lt;/code&gt;, which you can simply retry. And the 402 branch exists because when the account has fewer pages than the call needs, nothing is read or charged. You get a &lt;code&gt;buy_url&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;Limits to plan around: 20 pages per call, 20 MB per file, 28 MB per request body, roughly 30 calls a minute per key. If a PDF has more pages than one call reads, &lt;code&gt;next_pages&lt;/code&gt; tells you the range to ask for next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Hebrew OCR is genuinely hard
&lt;/h2&gt;

&lt;p&gt;None of this is specific to us. If you're evaluating any tool, these are the places to test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bidirectional text.&lt;/strong&gt; Hebrew runs right to left, but numbers, dates, email addresses and English product names inside it run left to right. A PDF stores characters in some order, and a viewer applies the Unicode bidi algorithm to display them. Copy text out of a lot of Hebrew PDFs and you get the stored order, which is why "reversed Hebrew" is such a common complaint. Reversing the string fixes the Hebrew and breaks every number.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mixed numbers and currency.&lt;/strong&gt; An invoice line like &lt;code&gt;2 × 680.00 = 1,360.00 ₪&lt;/code&gt; has to survive with the digits in order and the shekel sign attached to the right number. Thousands separators and decimal points look like noise to a naive tokenizer. Getting this wrong is worse than getting a word wrong, because nobody notices until the books don't balance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layout.&lt;/strong&gt; Israeli invoices tend to put the vendor block top right, the customer block top left, and a table whose first column is on the right. Reading order for a page like that isn't "top to bottom, left to right" or even its mirror image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nikud and look-alike letters.&lt;/strong&gt; Most business documents don't use vowel points, but some printed material does, and stray dots from a bad scan can be mistaken for them. Several letters differ by a tiny stroke (ד and ר, ה and ח, ו and ן), which is exactly the detail a faded thermal receipt loses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fonts.&lt;/strong&gt; Hebrew typography is a smaller world, and a lot of invoices are produced by older accounting software with idiosyncratic fonts, or printed on thermal paper that fades within months.&lt;/p&gt;

&lt;p&gt;So treat the output as something to check, not something to trust blindly. That's why every field has a box and an uncertainty flag: in a pipeline, route anything marked &lt;code&gt;uncertain&lt;/code&gt; to a person, and let the rest through.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying it without code
&lt;/h2&gt;

&lt;p&gt;If you just want to see what a given invoice produces, the browser version at &lt;a href="https://ocr-ai.com/en" rel="noopener noreferrer"&gt;ocr-ai.com&lt;/a&gt; reads 5 pages a day free, with no signup. For the API, an account's first key starts with 100 free pages. The full reference, including every error code, is at &lt;code&gt;/docs&lt;/code&gt;, and there's a Markdown copy at &lt;code&gt;/docs.md&lt;/code&gt; if you want to paste it into a model's context.&lt;/p&gt;

</description>
      <category>ocr</category>
      <category>hebrew</category>
      <category>api</category>
      <category>python</category>
    </item>
  </channel>
</rss>
