SFOLN

Catalogue / Documents & media

Document Parser – AI PDF & Image to JSON

sfoln/document-to-json · AI, Developer tools, Automation On request

Extract the fields you name (invoice number, total, date, vendor...) from PDFs, scans and images into JSON with AI. PDF pages with a text layer cost $1 per 1,000; only scanned pages are OCR'd with Mistral ($7 per 1,000). $12 per 1,000 documents extracted.

About

What it does

Turn invoices, receipts, contracts, forms, CVs, scientific papers or any other document into the JSON fields you ask for. Give a list of document URLs (PDF, scanned PDF, image, DOCX, PPTX) and either a simple list of field names (invoice_number, total, date, vendor) or a full JSON schema; the Actor reads the document's text and fills the fields with an AI extraction step. PDF pages that already contain text are read straight from the PDF's text layer; only scanned pages, images and office files go to Mistral OCR (tables and layout preserved). One clean record per document, with notes on anything missing or ambiguous. Built for back-office automation, accounting and procurement pipelines, data entry replacement and AI agents.

Price

Price per delivered record

EventPricePer 1,000Charged for
OCR page
ocr-page
$0.007$7 / 1,000Charged once per page processed by Mistral OCR: scanned PDF pages, images, DOCX/PPTX pages, or every page with forceOcr.
Text-layer page
pdf-text-page
$0.001$1 / 1,000Charged once per PDF page read from the PDF's own text layer (born-digital or blank pages): no OCR needed.
Document extraction
document-extraction
$0.012$12 / 1,000Charged once per document whose requested fields were extracted into JSON.

Apify free plan: up to 5 items per run. Paid plans have no per-run cap; set maxTotalChargeUsd to cap spend.

Run

Call it from code, an agent or the Console

This API is private on Apify for now. Email us and we will publish it for you, usually within two working days. The calls below work once it is public. contact@sfoln.com

curl · synchronous run, rows in the response
curl -X POST "https://api.apify.com/v2/actors/sfoln~document-to-json/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"documentUrls":["https://arxiv.org/pdf/1706.03762v7"],"fields":["title","authors","publication_year","main_contribution: one sentence"],"maxPages":2}'
mcp.json · hosted Apify MCP server
{
  "mcpServers": {
    "apify": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=sfoln/document-to-json"
    }
  }
}
asynchronous run · for large batches
POST https://api.apify.com/v2/actors/sfoln~document-to-json/runs?maxTotalChargeUsd=1
GET  https://api.apify.com/v2/actor-runs/{runId}            # until status is SUCCEEDED
GET  https://api.apify.com/v2/datasets/{defaultDatasetId}/items?format=json

Authenticate with an Apify API token. MCP clients can instead sign in with OAuth when prompted.

Input

Input fields

List document URLs and the fields you want. Each document produces one dataset record with the extracted fields.

FieldTypeDefaultMeaning
documentUrls *arrayrequiredDocument URLs
Public URLs of PDFs, scans or images (PNG, JPG, WEBP, TIFF), DOCX or PPTX, one per line. Max 50 MB per file. PDF pages that have a text layer are read directly ($0.001 per page); scanned pages, images and office files are OCR'd ($0.007 per page). Accounts on the Apify free plan can process up to 5 documents per run.
fieldsarray—Fields to extract
Field names, one per line, optionally with a hint after a colon, e.g. "invoice_number", "total: grand total incl. tax", "line_items: list of {description, quantity, amount}". Ignored when a JSON schema is given.
jsonSchemaobject—JSON schema (optional)
Instead of field names, a JSON schema object for the output, e.g. {"type":"object","properties":{"total":{"type":"number"}}}.
instructionsstring—Extra instructions
Optional guidance for the extraction (max 2,000 characters), e.g. "Amounts are in EUR; dates are day-first".
maxPagesinteger20Max pages per document
Read at most this many pages per document (each page is charged: text-layer PDF pages $0.001, OCR'd pages $0.007). Accounts on the free plan are limited to 5. Range 1–100.
includeMarkdownbooleanfalseInclude document markdown
Also return the document's text as markdown, from the PDF's text layer and/or OCR (always included when extraction fails).
forceOcrbooleanfalseOCR every page
Send every PDF page to Mistral OCR, also pages that have a text layer (charged as ocr-page instead of pdf-text-page). Use it when a PDF's text layer is unreliable, e.g. garbled characters or text in the wrong order; scans and images are always OCR'd.
maxDocumentsinteger100Max documents
Safety cap on how many documents are processed in this run. Range 1–5000.
concurrencyinteger2Concurrency
Documents processed in parallel (1–5). Range 1–5.
Example input
{
  "documentUrls": [
    "https://arxiv.org/pdf/1706.03762v7"
  ],
  "fields": [
    "title",
    "authors",
    "publication_year",
    "main_contribution: one sentence"
  ],
  "maxPages": 2
}

Output

One record per input item

Records land in the run's dataset; a synchronous call returns them directly. Each has a status (ok is charged; everything else is free) and a charged flag. The run's OUTPUT record totals items and charges.

FieldTypeMeaning
inputstring | objectInput
The input item this record corresponds to.
statusstringStatus
ok | not_found | error | skipped_invalid | skipped_budget | skipped_free_plan
chargedbooleanCharged
Whether the record incurred a charge: document-extraction when the fields were extracted; when the extraction failed, the last page read (pdf-text-page or ocr-page), so billed pages always show. All page charges are in textPagesCharged and ocrPagesCharged.
chargedEventstring | nullCharged event
The record's last charged event: document-extraction, or the page event when the extraction failed.
sourcestringSource
Upstream data source.
fetchedAtstringFetched at
ISO timestamp.
warningsarray | nullWarnings
Non-fatal problems (pages not reachable, truncation, budget limits).
errorobject | nullError
{ code, message, provider?, status? } when status is not ok.
urlstring | nullURL
Document URL.
docSizeBytesinteger | nullDocument size (bytes)
Size the server reported for the document; on skipped_invalid records with error code document_too_large.
documentTypestring | nullDocument type
document (PDF, DOCX, PPTX ...) or image.
modestring | nullMode
hybrid: a PDF read from its own text layer, only its scanned pages OCR'd; ocr: every page OCR'd (images, office files, forceOcr, or a PDF whose text layer could not be read).
pageCountinteger | nullPages returned
Pages whose text was used (and charged).
pagesProcessedinteger | nullPages processed
Pages read: OCR'd by Mistral, or read from the PDF's text layer.
pageLimitinteger | nullPage limit
Page cap applied (maxPages, free-plan sample or budget).
possiblyTruncatedboolean | nullPossibly truncated
true when the page limit was reached, so later pages were not read.
extractedobject | nullExtracted fields
The requested fields as JSON (null values for fields not found).
confidenceNotesstring | nullConfidence notes
Model notes on missing, ambiguous, inferred or possibly misread fields.
extractionStatusstring | nullExtraction status
ok | failed.
extractionInputTruncatedboolean | nullExtraction input truncated
true when only the first ≈15,000 tokens of the document text were used for extraction.
ocrPagesChargedinteger | nullOCR pages charged
Number of ocr-page events charged for this document.
textPagesChargedinteger | nullText-layer pages charged
Number of pdf-text-page events charged for this document.
textLayerPagesarray | nullText-layer pages
Hybrid mode: page numbers read from the PDF's text layer.
ocrPagesarray | nullOCR pages
Hybrid mode: page numbers sent to OCR (scanned pages).
tokensUsedobject | nullTokens used
{ input, output } Mistral tokens for the extraction.
markdownstring | nullMarkdown
The document's text as markdown, from the text layer and/or OCR (includeMarkdown, or when extraction failed).
modelstring | nullOCR model
Mistral OCR model used; null when no page needed OCR.
Example record
{
  "input": "https://files.example.com/invoice-42.pdf",
  "url": "https://files.example.com/invoice-42.pdf",
  "documentType": "document",
  "mode": "hybrid",
  "pageCount": 2,
  "pagesProcessed": 2,
  "pageLimit": 2,
  "possiblyTruncated": false,
  "extracted": {
    "invoice_number": "INV-2026-042",
    "date": "2026-10-14",
    "vendor": "Acme GmbH",
    "total": 1190,
    "currency": "EUR"
  },
  "confidenceNotes": "Vendor VAT ID not present.",
  "extractionStatus": "ok",
  "extractionInputTruncated": false,
  "ocrPagesCharged": 0,
  "textPagesCharged": 2,
  "textLayerPages": [
    1,
    2
  ],
  "ocrPages": [],
  "tokensUsed": {
    "input": 1450,
    "output": 80
  },
  "model": null,
  "status": "ok",
  "charged": true,
  "chargedEvent": "document-extraction",
  "source": "mistral",
  "fetchedAt": "2026-10-14T10:00:00.000Z"
}

FAQ

Questions

Which pages are OCR'd, and what do pages cost?
PDF pages that already contain text (exported from accounting software, Word or a browser) are read straight from the PDF's text layer for $0.001 per page (pdf-text-page). Only pages without a usable text layer, such as scans and photos, and images or office files go to Mistral OCR for $0.007 per page (ocr-page). Set forceOcr to OCR every page.
What if a field is not in the document?
It is returned as null, never invented, and confidenceNotes says which fields were missing, ambiguous or inferred. When the reply contains none of your fields, the record has extractionStatus failed, carries the document's markdown and the document-extraction event is not charged.
Can I use my own JSON schema?
Yes: pass a JSON schema object in jsonSchema instead of a field list. Every top-level property of the schema is present in extracted (null when not found); the schema guides the model, and values are not validated against its types.

Machine-readable

This page for agents