Scan a receipt
A crumpled paper receipt is the last analog holdout in most expense workflows, and it's the one document nobody wants to type in by hand. This endpoint reads a photo of a receipt and returns merchant, date, line items, tax and total as structured JSON, so the paper never has to touch a spreadsheet.
Run it online
Run this on our servers with your account. Free tools run in your browser; this one bills your KIT balance per the price above.
The problem with a pocketful of receipts
Receipts are small, thermal-printed, fade within weeks, and arrive in every shape a cash register can produce: grocery tapes two inches wide, restaurant slips with a tip line scrawled in pen, gas station stubs printed sideways. Anyone who has tried to reconcile a stack of them at month-end knows the real cost isn't reading one receipt — it's reading fifty, consistently, without missing the one where the total was smudged. Multiply that by an entire sales team submitting expenses weekly, and manual entry stops being a chore and becomes a line item of its own in headcount time.
Who actually needs this
Expense-report tools that let an employee snap a photo instead of filling a form, accounting software reconciling petty cash, personal-finance apps building a spending history from paper trails, and reimbursement pipelines at companies that still see plenty of cash and card slips despite everything going digital elsewhere. Field teams — technicians, sales reps, delivery drivers — are frequent users too, since a phone camera is often the only tool they have on hand at the point of purchase.
What comes back
Send the image, get a task_id right away, and the finished job returns merchant name, purchase date, individual line items with quantities and prices where legible, subtotal, tax and grand total as clean JSON fields — the numbers a bookkeeping system needs, not a wall of raw text to re-parse.
Fitting it into a bigger flow
Because the call is asynchronous and priced per document rather than per API call bundled with anything else, it slots cleanly into an expense pipeline: a mobile app uploads the photo the moment it's taken, the webhook fires when parsing finishes, and the resulting JSON lands directly in the expense record without a human ever transcribing a number.
Why receipts resist plain OCR
Thermal paper degrades, receipt printers use tiny condensed fonts, and layouts vary wildly by point-of-sale system — a generic text extractor returns a jumble of numbers with no idea which one is the total. This endpoint is purpose-built to understand receipt structure specifically, not just to read characters off a page.
What you can do with it
Expense report automation
An employee photographs a lunch receipt in a mobile app, and the parsed total, date and merchant populate the expense line automatically.
Petty cash reconciliation
Finance batches a week's worth of office-supply receipts to reconcile the petty cash drawer without manual data entry.
Personal finance tracking
A budgeting app lets users snap grocery receipts and builds a category-level spending history from the extracted line items.
Reimbursement pipelines
A field-services company processes fuel and lodging receipts submitted by technicians, feeding parsed totals straight into payroll.
FAQ
How do I use the Receipt OCR API?
Send a photo to POST /ocr/receipt, receive a task_id immediately, and get the parsed JSON back by webhook or a signed link once the job finishes.
Is there a free tier?
There's no free tier — free tiers get abused and slow everyone down. Access runs on a prepaid ForHosting KIT balance: top up from $10.00 (it never expires) and each request is charged at its published price, so a call with no balance returns HTTP 402. No subscription, no tokens, no invented credits, and a failed task is never charged.
How is it priced?
$0.010 per request plus $0.0575 per document, published and flat — no hidden per-line or per-field charges.
What image formats work?
Standard photo formats from a phone camera or scanner work well; a clear, well-lit, in-focus shot of the full receipt gives the most complete result.
Can it handle a blurry or faded receipt?
It does its best on partial or faded thermal print, but very degraded receipts may return fewer fields; retry with a clearer photo when possible.
Does it work for receipts in different languages or currencies?
Yes, it reads receipts across common languages and currency formats and returns the amounts as found on the document.
Can I process a batch of receipts at once?
Each request handles one receipt and returns its own task_id, so processing a batch is simply firing parallel requests and collecting results by webhook.
Am I charged if parsing fails?
No. A failed task is retried automatically up to three times and is never charged; you receive a clear error instead.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, and soon from our app, email and Telegram.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/ocr/receipt \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"input":"…"}'const res = await fetch("https://api.kit.forhosting.com/ocr/receipt", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"input": "…"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/ocr/receipt",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"input": "…"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/ocr/receipt", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"input":"…"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"input":"…"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/ocr/receipt", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"input": "…"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "ocr.receipt",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_mb | 25 |
max_pages | 10 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
422 | task_failed | The task failed after 3 retries. You are never charged for it. |