Step 1
Create an account
Sign up at /signup with Google, GitHub, Microsoft, Apple, or email. Your account is created instantly.
Extract structured data from a document in five steps.
Step 1
Sign up at /signup with Google, GitHub, Microsoft, Apple, or email. Your account is created instantly.
Step 2
Open the Dashboard and click Generate new key. Copy the key immediately — it is shown only once. Your key starts with bloh_.
Step 3
Replace bloh_your_api_key with your real key and invoice.pdf with a path to your document:
curl -X POST https://api.bloh.dev/extract \
-H "x-api-key: bloh_your_api_key" \
-F "file=@invoice.pdf" \
-F "document_type=invoice"
# pip install requests
import requests
with open("invoice.pdf", "rb") as f:
response = requests.post(
"https://api.bloh.dev/extract",
headers={"x-api-key": "bloh_your_api_key"},
files={"file": f},
data={"document_type": "invoice"},
)
print(response.json())
import fs from "node:fs";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("invoice.pdf")]));
form.append("document_type", "invoice");
const res = await fetch("https://api.bloh.dev/extract", {
method: "POST",
headers: { "x-api-key": "bloh_your_api_key" },
body: form,
});
console.log(await res.json());
Step 4
A successful extraction returns JSON with the extracted fields, document type, and confidence level:
{
"success": true,
"document_type": "invoice",
"data": {
"vendor_name": "Muller GmbH",
"invoice_number": "2026-0847",
"gross_amount": 1992.66,
"currency": "EUR",
"line_items": [
{ "description": "Consulting", "quantity": 40, "unit_price": 49.82, "amount": 1992.66 }
]
},
"confidence": "high",
"credits_used": 2,
"processing_time_ms": 1340
}
Step 5
Use POST /estimate to check how many credits a document will cost before committing to extraction.
All API requests require an API key passed in the x-api-key header. Generate keys from the Dashboard.
x-api-key: bloh_your_api_key
API keys start with bloh_ followed by a random hex string. Each account can have up to 5 active keys. You can revoke a key at any time from the Dashboard — revoked keys are rejected immediately.
The playground endpoints (/playground/extract and /playground/estimate) do not require an API key. They are rate limited to 3 extractions per day per IP address.
Extracts structured data from a document. Send the file as multipart form data.
https://api.bloh.dev/extract
| Field | Type | Required | Description |
|---|---|---|---|
file |
File | Yes | Document to extract. PDF, JPG, JPEG, PNG, WEBP, TIFF, DOCX, XLSX, PPTX, CSV, or TSV. Max 20 MB. |
document_type |
String | No | invoice, receipt, contract, id_document, bank_statement, or auto (default). |
schema |
String | No | JSON string defining custom fields. See Custom schemas. |
{
"success": true,
"document_type": "invoice",
"data": {
"vendor_name": "Muller GmbH",
"invoice_number": "2026-0847",
"date": "2026-01-15",
"due_date": "2026-02-15",
"net_amount": 1674.50,
"vat_rate": 19,
"vat_amount": 318.16,
"gross_amount": 1992.66,
"currency": "EUR",
"iban": "DE89370400440532013000",
"line_items": [
{
"description": "Consulting — January",
"quantity": 40,
"unit": "hours",
"unit_price": 41.86,
"amount": 1674.50
}
]
},
"confidence": "high",
"credits_used": 2,
"processing_time_ms": 1340
}
| Field | Type | Description |
|---|---|---|
success |
Boolean | Always true on success. |
document_type |
String | Detected or specified document type. |
data |
Object | Extracted fields. Structure varies by document type. Missing fields are null. |
confidence |
String | high, medium, or low. Reflects text quality and field coverage. |
credits_used |
Number | Credits consumed by this extraction. |
processing_time_ms |
Number | Server-side processing time in milliseconds. |
{
"success": false,
"error": {
"code": "UNSUPPORTED_FILE_TYPE",
"message": "File type .zip is not supported. Supported types: pdf, jpg, jpeg, png, webp, tiff, docx, xlsx, pptx, csv, tsv"
}
}
See Error codes for the full list.
Estimates how many credits an extraction will cost without running the LLM. Useful for cost control and pre-validation.
https://api.bloh.dev/estimate
Same as POST /extract: send a file field, with optional document_type.
{
"success": true,
"input_tokens": 1450,
"estimated_credits": 3,
"file_size_bytes": 245760,
"mime_type": "application/pdf"
}
| Field | Type | Description |
|---|---|---|
success |
Boolean | Always true on success. |
input_tokens |
Number | Approximate token count of the parsed document text. |
estimated_credits |
Number | Estimated credits the extraction would consume. |
file_size_bytes |
Number | Size of the uploaded file in bytes. |
mime_type |
String | Detected MIME type of the file. |
curl -X POST https://api.bloh.dev/estimate \ -H "x-api-key: bloh_your_api_key" \ -F "file=@contract.pdf"
By default, Bloh extracts a standard set of fields for each document type. To extract only specific fields, pass a JSON schema in the schema form field.
The schema must be a JSON object with a fields array of strings. Each string is a field name to extract.
{
"fields": ["vendor_name", "total_amount", "currency", "due_date"]
}
curl -X POST https://api.bloh.dev/extract \
-H "x-api-key: bloh_your_api_key" \
-F "file=@invoice.pdf" \
-F 'schema={"fields":["vendor_name","total_amount","currency"]}'
{
"success": true,
"document_type": "invoice",
"data": {
"vendor_name": "Muller GmbH",
"total_amount": 1992.66,
"currency": "EUR"
},
"confidence": "high",
"credits_used": 2,
"processing_time_ms": 980
}
When a custom schema is provided, the document_type parameter is ignored — the LLM extracts only the fields you specify.
All error responses share the same shape:
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable description"
}
}
| Code | HTTP | Description |
|---|---|---|
MISSING_FILE |
400 | No file field in the multipart request body. |
UNSUPPORTED_FILE_TYPE |
400 | File extension is not one of the supported types. |
INVALID_SCHEMA |
400 | The schema field is not valid JSON or does not match the expected format. |
UNAUTHORIZED |
401 | Missing, invalid, or revoked API key. |
FILE_TOO_LARGE |
413 | File exceeds the 20 MB size limit. |
PARSING_FAILED |
422 | The document could not be parsed. It may be corrupted, empty, or password-protected. |
EXTRACTION_FAILED |
422 | The extraction engine could not produce valid structured data from this document. |
RATE_LIMITED |
429 | Too many requests. See Rate limits. |
CREDIT_LIMIT_REACHED |
429 | Monthly credit allowance exhausted. Upgrade your plan or wait for the next billing cycle. |
INTERNAL_ERROR |
500 | Unexpected server error. If this persists, contact hello@bloh.dev. |
| Context | Limit | Window |
|---|---|---|
| API (per key) | 60 requests | 1 minute |
| Playground (per IP) | 3 extractions | 24 hours |
When you exceed a rate limit, the API returns a 429 status with the RATE_LIMITED error code. Wait for the window to reset before retrying.
| Plan | Credits/month |
|---|---|
| Free | 100 |
| Pro | 2,000 |
| Max | 20,000 |
Credit usage is visible on the Dashboard. Use POST /estimate to check costs before extracting.
Pass a document_type to optimize extraction, or use auto (default) to let Bloh detect the type.
| Type | Key fields extracted |
|---|---|
invoice |
vendor_name, invoice_number, date, due_date, line_items, net_amount, vat_rate, vat_amount, gross_amount, currency, iban, bic |
receipt |
merchant_name, receipt_number, date, time, line_items, subtotal, tax_rate, tax_amount, total_amount, currency, payment_method |
contract |
parties, contract_title, contract_number, effective_date, expiration_date, governing_law, jurisdiction, key_terms, signatures, total_value |
id_document |
document_type, document_number, full_name, date_of_birth, nationality, expiry_date, issue_date, issuing_authority, address |
bank_statement |
bank_name, account_holder, iban, bic, statement_period_start, statement_period_end, opening_balance, closing_balance, transactions |
auto |
Bloh detects the type and extracts the corresponding fields automatically. |
All dates are normalized to ISO 8601 (YYYY-MM-DD). All monetary amounts are returned as numbers without currency symbols. Currency is always a separate ISO 4217 field.