Prepaid pages·No subscription

OCR API for PDFs and images.

Post a document to one endpoint and get its text back, page by page. No upload UI, no SDK, no monthly bill — just a Bearer key and a balance of pages.

From 1.0¢ a page · Pages never expire

PDFs, scans and photos

PDF, JPEG, PNG, TIFF, BMP and HEIF up to 50MB, multi-page or single shot.

One request, all the text

Get the whole document as a string and the same text split page by page.

Prepaid, never a subscription

Buy pages once. They never expire and nothing renews on your card.

Pricing

One page of a document costs one page of your balance. Buy a pack, spend it whenever, buy another when it runs out.

Starter

$50

2,500 pages

2.0¢ per page

Buy pages
Most popular

Growth

$150

10,000 pages

1.5¢ per page

Buy pages

Scale

$500

50,000 pages

1.0¢ per page

Buy pages

One-time purchase, no recurring charge. Payments are handled securely by Paddle.

API reference

The whole API is two endpoints. Here is all of it.

Overview

One endpoint takes a PDF or an image and returns its text, both as a single string and split page by page. There is no upload UI and no SDK to install: everything below is a plain HTTP request you can make from any language.

Base URLhttps://404tools.dev
Read a documentPOST /api/v1/ocr
Check your balanceGET /api/v1/account
Modelprebuilt-read

Authentication

Every request carries an API key as a Bearer token. Keys start with tk_live_ and are shown in full exactly once, when you create them. Create a key and store it the way you store any other secret.

Authorization: Bearer tk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keys are stored only as a SHA-256 digest, so a lost key cannot be recovered — revoke it and issue another. Cross-origin requests are allowed because the credential is a header you attach deliberately rather than a cookie the browser attaches for you, but that is not an invitation to ship a key to a browser: anything in front of a user should call your own backend, which then calls this API.

Sending a document

Three request shapes, all of them POST /api/v1/ocr. Pick whichever fits the language you are calling from; they produce identical responses. The content type you declare is only a hint — the bytes themselves decide how the document is treated, so a file that arrives as application/octet-stream still works.

Send multipart/form-data with the document in a field named file. This is the shape almost every HTTP client makes easiest.

curl -X POST https://404tools.dev/api/v1/ocr \
  -H "Authorization: Bearer $TOOLS_API_KEY" \
  -F "file=@invoice.pdf"

Documents it accepts

Content typeNotes
application/pdfPDF, including multi-page scans
image/jpegJPEG photos and scans
image/pngPNG screenshots and exports
image/tiffTIFF, including multi-page faxes
image/bmpWindows bitmap
image/heifHEIF/HEIC from modern phones

Common aliases are normalised for you, so image/jpg and image/heic are fine. Anything else is refused with a 415 before the bytes leave our network. The hard ceiling is 50MB per document, checked both against the declared length and against the bytes actually read.

The response

A successful call returns 200 and the JSON below. content is the whole document as one string with page breaks preserved as newlines; pages is the same text split into one entry per page, which is what you want if you are indexing or chunking.

{
  "content": "INVOICE\nAcme Corp\nInvoice #1042\n...",
  "pageCount": 3,
  "pages": [
    { "pageNumber": 1, "text": "INVOICE\nAcme Corp\n..." },
    { "pageNumber": 2, "text": "Line items\n..." },
    { "pageNumber": 3, "text": "Total due\n..." }
  ],
  "pagesCharged": 3,
  "pagesRemaining": 2497,
  "durationMs": 4120
}
contentFull extracted text, all pages.
pageCountPages the document actually had.
pages[]pageNumber (1-based) and text for each page.
pagesChargedPages debited from your balance for this call.
pagesRemainingBalance left after the debit.
durationMsTime the OCR itself took, not your upload.

How pages are counted and billed

Billing is prepaid pages, one page per page read. A ten-page PDF costs ten pages; a single photo costs one. Pages are bought in packs, never expire, and there is no subscription — when a balance runs out you buy another pack.

Only successful reads are charged. A 401, 413, 415, 429 or 502 costs you nothing, and neither does a 402. A document is billed whole or not at all: if it has more pages than your balance the call returns 402 with the pageCount it would have cost and your pagesRemaining, nothing is debited, and no text is returned. Buy a pack and send it again.

To check a balance without spending a page, call the account endpoint. Doing that before a batch job is the cheapest way to avoid discovering an empty balance halfway through one.

curl https://404tools.dev/api/v1/account \
  -H "Authorization: Bearer $TOOLS_API_KEY"

{ "pagesBalance": 2497, "pagesUsedTotal": 503 }

Rate limits

Each API key may make 20 requests per minute. Going over returns 429 with a Retry-After header in seconds; wait that long rather than retrying immediately. The limit is per key, so splitting a workload across several keys raises your effective throughput. Long documents are the other limit worth knowing about: a request may run for up to five minutes, so set your client timeout accordingly rather than leaving it at the usual 30 seconds.

Errors

Every failure returns JSON with a human-readable error and a stable code. Branch on the code, not on the message text.

{
  "error": "This document is 12 page(s) and your balance is 4. Nothing was charged.",
  "code": "insufficient_pages",
  "pageCount": 12,
  "pagesRemaining": 4
}
StatusCodeMeaning
400invalid_bodyThe body could not be parsed as the Content-Type promised.
400missing_fileA multipart request arrived without a "file" field.
400missing_urlA JSON request arrived without a "url" string.
400invalid_urlThe url was unparseable or used a scheme other than http/https.
400url_fetch_failedThe document could not be downloaded from the url you gave.
400empty_documentThe request carried no bytes, or the url returned an empty body.
401invalid_api_keyMissing, malformed, unknown or revoked key. All four look the same on purpose.
402insufficient_pagesYour balance is empty, or smaller than this document. Buy a page pack and retry; nothing was charged.
413document_too_largeThe document is over the 50MB limit.
415unsupported_media_typeThe bytes are not one of the supported document types.
429rate_limitedMore than 20 requests in a minute on this key. Honour the Retry-After header.
500accounting_failedPage accounting failed, so the text is withheld. Nothing was charged.
502ocr_failedThe upstream OCR engine rejected or failed on the document. Nothing was charged.

Copy-paste examples

All three read a local PDF and print its text. Keep the key in an environment variable — the examples assume TOOLS_API_KEY.

curl -X POST https://404tools.dev/api/v1/ocr \
  -H "Authorization: Bearer $TOOLS_API_KEY" \
  -F "file=@invoice.pdf"

Create an account, buy a pack, start reading documents.

You get your first key the moment your pack clears. Nothing renews and nothing expires.