FastOCR API
Extract text from documents and structured data from bank statements. Upload a file, poll for completion, download the result.
Getting access
The API is in private beta. Access is allowlisted per account — you cannot generate a key until your account is approved.
- 1Contact us to request API access for your account.
- 2Once approved, create an API key in the developer console.
- 3Pass the key as
Authorization: Bearer <key>on every request.
Document OCR
Upload a PDF, run OCR, and get extracted text or a searchable PDF back. Five requests from file to result.
FASTOCR_KEY="your-api-key"
BASE="https://api.fastocr.org"
# 1. Create a document job
curl -s -X POST "$BASE/v1/documents" \
-H "Authorization: Bearer $FASTOCR_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"filename":"scan.pdf","size_bytes":'$(wc -c < scan.pdf | tr -d ' ')'}' \
-o doc.json
# 2. Upload the PDF to the presigned URL
curl -s -X PUT "$(jq -r '.upload_url' doc.json)" \
-H "Content-Type: application/pdf" \
-T scan.pdf
# 3. Start processing
DOC=$(jq -r '.id' doc.json)
curl -s -X POST "$BASE/v1/documents/$DOC/start" \
-H "Authorization: Bearer $FASTOCR_KEY"
# 4. Poll until ready
while true; do
STATUS=$(curl -s "$BASE/v1/documents/$DOC" \
-H "Authorization: Bearer $FASTOCR_KEY" | jq -r '.status')
case "$STATUS" in completed|partial|failed) break ;; esac
sleep 3
done
# 5. Download the extracted text
URL=$(curl -s "$BASE/v1/documents/$DOC/output?format=text" \
-H "Authorization: Bearer $FASTOCR_KEY" | jq -r '.url')
curl -o result.txt "$URL"Output formats: ?format=text for plain text, ?format=pdf for a searchable PDF.
Idempotency: The Idempotency-Key header prevents duplicate jobs if a request is retried. Use a unique value per job.
Statuses: awaiting_upload → processing → completed or failed. Large documents may finish as partial with is_truncated: true.
Bank statement extraction
Upload a bank statement and extract transactions as structured data. Same five-step flow as documents. Accepts PDF, PNG, JPG, and WebP.
FASTOCR_KEY="your-api-key"
BASE="https://api.fastocr.org"
# 1. Create a bank statement job
curl -s -X POST "$BASE/v1/bank-statements" \
-H "Authorization: Bearer $FASTOCR_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"filename":"statement.pdf","size_bytes":'$(wc -c < statement.pdf | tr -d ' ')'}' \
-o stmt.json
# 2. Upload the file
curl -s -X PUT "$(jq -r '.upload_url' stmt.json)" \
-H "Content-Type: application/pdf" \
-T statement.pdf
# 3. Start extraction
STMT=$(jq -r '.id' stmt.json)
curl -s -X POST "$BASE/v1/bank-statements/$STMT/start" \
-H "Authorization: Bearer $FASTOCR_KEY"
# 4. Poll until ready
while true; do
STATUS=$(curl -s "$BASE/v1/bank-statements/$STMT" \
-H "Authorization: Bearer $FASTOCR_KEY" | jq -r '.status')
case "$STATUS" in completed|failed) break ;; esac
sleep 3
done
# 5. Download as Excel
URL=$(curl -s "$BASE/v1/bank-statements/$STMT/output?format=xlsx" \
-H "Authorization: Bearer $FASTOCR_KEY" | jq -r '.url')
curl -o transactions.xlsx "$URL"Output formats: ?format=xlsx (Excel), ?format=csv, or ?format=json.
File limits: PDFs up to 100 MB. Images up to 20 MB.
Endpoints
Base URL: https://api.fastocr.org
| Method | Path | Description |
|---|---|---|
| POST | /v1/documents | Create a document job |
| GET | /v1/documents | List documents |
| GET | /v1/documents/{id} | Check status |
| POST | /v1/documents/{id}/start | Start processing |
| GET | /v1/documents/{id}/output | Download result |
| DELETE | /v1/documents/{id} | Delete a document |
| POST | /v1/bank-statements | Create a statement job |
| GET | /v1/bank-statements | List statements |
| GET | /v1/bank-statements/{id} | Check status |
| POST | /v1/bank-statements/{id}/start | Start extraction |
| GET | /v1/bank-statements/{id}/output | Download result |
| DELETE | /v1/bank-statements/{id} | Delete a statement |
List parameters: ?limit= (1–100, default 20), ?status=, ?cursor=, ?updated_since=.
List response: the array is named after the resource — { documents: [...], next_cursor } and { bank_statements: [...], next_cursor }. A next_cursor of null means there are no more pages.
Create body: filename (required), size_bytes (required), external_id (optional), sha256 (optional).
Errors
A failed job returns HTTP 200 with a terminal status and an error object. HTTP status describes the request, not the work.
{
"error": {
"type": "processing_error",
"code": "timeout",
"message": "This file took too long to process. Please try again.",
"retryable": true
}
}- Match on
code, never onmessage— messages change without notice. - New codes will be added. Treat any unrecognised code as
processing_failed. - Only
buy_pagesreopens a failed job — buy credits, then call/startagain on the same job.
| Code | Type | When | Retryable |
|---|---|---|---|
| buy_pages | insufficient_credits | Out of page credits. | No — buy a pack, then call /start again on the same job. |
| unreadable_statement | invalid_request | Bank statement could not be read. | No |
| unreadable_document | invalid_request | PDF could not be read. | No |
| too_many_pages | invalid_request | File exceeds the page cap. | No |
| empty_file | invalid_request | File has no pages. | No |
| timeout | processing_error | Processing ran out of time. | Yes |
| processing_failed | processing_error | Anything else. | Yes |
Ready to get started?
Request access, create a key, and run the quickstart above.