# SiteVerdict — PDF & Invoice Data Extractor (tables, line items) (`siteverdict/siteverdict-pdf-invoice-extractor`) Actor

Extract text, tables and invoice data from PDFs without AI: supplier, VAT number (checked), invoice no., dates, net/VAT/gross totals and line items, each with a confidence score and arithmetic checks. UK/EU formats, optional OCR.

- **URL**: https://apify.com/siteverdict/siteverdict-pdf-invoice-extractor.md
- **Developed by:** [Aman Thakran](https://apify.com/siteverdict) (community)
- **Categories:** AI, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Invoice & Receipt PDF Extractor → Xero, QuickBooks, Excel (SiteVerdict)

**Drop in supplier invoices and receipts. Get back checked bills ready to import into Xero or QuickBooks: supplier, VAT/GST number (check digits verified), invoice number, dates, net / VAT / gross and every line item — with the maths checked, duplicates caught and anything doubtful flagged for review.**

- **Accurate where it matters:** 100% field precision and recall on 90 benchmark invoices across UK, EU, US and India; **100% precision / 89% recall on a blind stress set** of awkward layouts. When it is unsure it leaves a field empty and says why — it does not guess. [Benchmark details](#benchmark-honest-numbers)
- **Catches what costs money:** 90 out of 90 planted errors caught (wrong totals, bad VAT/GSTIN check digits, wrong VAT amount, lines that do not multiply out), 0 false alarms on clean documents. Duplicate invoices are flagged within a batch and, optionally, across months.
- **Accountant exports included:** Xero bill import CSV, QuickBooks Online bill CSV, an Excel workbook, UBL 2.1 / Peppol and Factur-X / ZUGFeRD XML drafts and an HTML batch report — no extra charge.
- **Cheap and private by default:** about **$12 per 1,000 one-page invoices**. No AI reads your documents unless you switch it on with your own key.

### Three ways people use it

1. **Accounts-payable inbox → Xero/QuickBooks.** Your e-mail parser, Zapier or Make sends the PDF attachments as an *Inbox batch*. You get `XERO_BILLS.csv` / `QUICKBOOKS_BILLS.csv` plus a *Needs review* list. Each bill keeps the e-mail's sender, subject and message ID, so you can trace it back.
2. **Monthly supplier-portal sweep.** Point *Folder pages* at the public page or bucket listing where a supplier posts its invoices, and turn on **Monitor mode** with a schedule. Each run processes only new or changed PDFs (unchanged files are skipped and not charged) and posts a Slack message when something needs attention.
3. **Bookkeeping practices and India GST.** Validate a client's purchase invoices before filing: GSTIN check characters, CGST = SGST, intra-state vs inter-state (IGST) consistency, lakh/crore amounts (`Rs. 1,18,000/-`), and VAT number check digits for 29 European countries.

### Before → after

A two-column Indian tax invoice PDF (`TAX INVOICE … GSTIN: 33HGNFA3673J3ZP … CGST @ 9% Rs. 4,006.08 … SGST @ 9% Rs. 4,006.07 … Grand Total Rs. 52,524.08`) becomes:

```json
{
  "documentType": "invoice",
  "supplierName": "Deccan Cloud Solutions Pvt. Ltd.",
  "supplierTaxIdType": "GSTIN",
  "supplierVatNumber": "33HGNFA3673J3ZP",
  "supplierVatChecksumValid": true,
  "invoiceNumber": "GST/2026-27/0118",
  "purchaseOrder": "PO-26643",
  "issueDate": "2026-03-27",
  "dueDate": "2026-04-26",
  "currency": "INR",
  "netTotal": 44511.93,
  "vatTotal": 8012.15,
  "grossTotal": 52524.08,
  "vatRates": [18],
  "lineItems": [{ "description": "Cotton fabric (metres)", "quantity": 11, "unitPrice": 219.2, "amount": 2411.2, "arithmeticOk": true }],
  "checks": { "lineSumEqualsNet": true, "netPlusVatEqualsGross": true, "lineArithmeticFailed": 0, "vatMathOk": true },
  "totalsConsistent": true,
  "fieldConfidence": { "invoiceNumber": 0.92, "issueDate": 0.89, "currency": 0.93, "grossTotal": 0.98 },
  "needsReview": false,
  "duplicate": null
}
```

…and a row per line in `XERO_BILLS.csv`:

```
*ContactName,…,*InvoiceNumber,*InvoiceDate,*DueDate,Total,…,Description,*Quantity,*UnitAmount,*AccountCode,*TaxType,TaxAmount,…,Currency
Harbourside Print Co. Ltd,…,INV-2026-6872,15/01/2026,14/02/2026,8079.68,…,"A4 copier paper, 80gsm (box of 5 reams)",4,21.63,429,20% (VAT on Expenses),17.3,…,GBP
```

When something is wrong you see it in plain words, e.g. `"needsReview": true, "reviewReasons": ["TOTALS_MISMATCH"]` with the issue *"Net 417.48 + VAT 83.5 = 500.98, but gross total is 510.98 (difference 10)."*, or `"duplicate": { "of": "https://…/inv-0042.pdf", "reason": "same-invoice", "earlierRun": true }`.

### Benchmark (honest numbers)

Full method, per-field tables and caveats: [BENCHMARK.md](BENCHMARK.md). All test documents are synthetic (no real personal data); competitor Actors were compared on their documentation, not run.

| Test | Docs | Precision | Recall | Notes |
| --- | --- | --- | --- | --- |
| Previous version (v1.0) | 60 | ≈97.5% | ≈87.3% | India GST only 62–72% right |
| This version, UK/EU/US/India corpora (incl. a held-out seed) | 90 | 100% | 100% | 9 layout families, 9 regions, multi-rate VAT, GST |
| **This version, blind stress set** (8 unusual layouts written after the code was frozen) | 8 | **100%** | **89%** | misses are left empty and flagged, never guessed |
| Error detection (planted defects) | 90 | 90 caught | 0 false alarms | wrong totals, bad tax-ID check digits, wrong VAT, bad line maths |

| | This Actor | Typical alternatives on the Store |
| --- | --- | --- |
| Invoice fields + line items | yes, with confidence per field | yes (OCR/LLM based) or text only |
| VAT (29 countries) + GSTIN check digits | yes | not documented |
| Per-rate VAT, CGST = SGST, line maths | yes | "verify calculations" at best |
| Duplicate invoices (batch + across runs) | yes | not documented |
| Xero / QuickBooks / Excel / UBL / Factur-X | yes, included | not documented |
| Folder monitoring + Slack alerts | yes | not documented |
| Price per 1,000 one-page invoices | **≈ $12** | ≈ $10 (receipt OCR, your own cloud keys for better engines) to $30–120 |

### Pricing (pay per event)

| Event | Price | When |
| --- | --- | --- |
| `page-processed` | $0.002 | per page read from the PDF's text layer |
| `invoice-extracted` | $0.010 | per document whose invoice/receipt fields are returned |
| `ocr-page` | $0.010 | per scanned page read with OCR (only with *OCR* on; replaces page-processed) |
| `ai-refined-document` | $0.005 | only when the optional AI step (your own key) filled or corrected a verified field |

Examples: **100 one-page invoices ≈ $1.20. 1,000 ≈ $12. 1,000 scanned one-page receipts with OCR ≈ $20.** Exports, duplicate checks, monitoring and alerts are included. You are **not** charged for failed, password-protected, corrupt, too-large or non-PDF files, exact duplicate files, or unchanged documents in Monitor mode. The run's maximum total charge is respected.

### How to use it

1. Add documents in any mix: **PDF URLs** (public or pre-signed S3/GCS/SharePoint/Drive links), **files in Apify key-value stores** (upload them in Console), an **Inbox batch** (e-mails with attachment links), a **JSON batch record**, **folder pages** (HTML index, public bucket listing or JSON list of PDF links) or a **dataset of URLs** from another Actor.
2. Pick exports (Excel, Xero, QuickBooks by default; add CSV, UBL, Factur-X). For Xero set the account code (default 429 General Expenses) and the date format of your organisation.
3. Optional: *Monitor mode* + a schedule, *Remember invoices across runs*, an HTTPS Slack/webhook alert, OCR for scans, AI refinement with your own Anthropic or OpenAI key.
4. Run. Read the **Invoices**, **Needs review and duplicates**, **Line items** and **Validation issues** views, and download the files listed in `OUTPUT.files`.

Inbox batch example:

```json
{
  "inboxMessages": [
    { "messageId": "<a1@supplier.example>", "from": "accounts@supplier.example", "subject": "Invoice 4471",
      "attachments": [{ "url": "https://files.example.com/inv-4471.pdf", "filename": "inv-4471.pdf" }] }
  ],
  "exportFormats": ["xlsx", "xero", "quickbooks", "html"],
  "rememberAcrossRuns": true
}
```

### What it reads

- **Fields:** supplier name, VAT number or GSTIN (format + check digit), company number, IBAN (mod-97), document type (invoice / receipt / credit note), invoice number, PO, issue and due date (or issue date + payment terms), currency, net, VAT/GST, gross, amount due, VAT rates, line items (description, quantity, unit price, VAT rate, amount).
- **Languages and formats:** labels in English, German, French, Dutch, Spanish and Italian; amounts `1,234.56`, `1.234,56`, `1 234,56`, Indian `1,23,456.78` and `Rs. 1,18,000/-`, negatives in parentheses; dates numeric, ISO, `3rd October 2026`, `14. September 2026`, `12-Sep-2026`. Day/month order is decided per document (unambiguous dates settle it; `$` + US address or sales tax means month first; £/€/₹ documents are day first).
- **Layouts:** item tables with or without headers, multi-page tables, VAT summary tables on receipts, horizontal totals tables, labels above values, DIN 5008 sender lines, HSN/SAC and serial-number columns, discount columns.

### Validation checks

`TOTALS_MISMATCH`, `LINE_SUM_MISMATCH`, `LINE_ARITHMETIC`, `VAT_RATE_MISMATCH` (per rate when lines carry rates), `GST_CGST_SGST_MISMATCH`, `GST_TYPE_MISMATCH` (IGST vs CGST+SGST against the two GSTIN state codes), `VAT_NUMBER_CHECKSUM_INVALID` / `GSTIN_CHECKSUM_INVALID`, `VAT_RATE_UNUSUAL` (not a standard/reduced rate of the supplier's country), `DUE_BEFORE_ISSUE`, `MISSING_SUPPLIER_VAT_NUMBER` / `MISSING_SUPPLIER_GSTIN`, `DUPLICATE_INVOICE`, and more. `needsReview` is set when a key field is missing or below your confidence threshold (default 80%), an error-level check fails, a money-affecting warning is raised, or the document is a duplicate.

### Optional AI refinement (your key, off by default)

Only documents that need review are sent, and only their weak fields are asked for. Every answer is **verified against the PDF text** before it is accepted — names and numbers must be printed in the document, dates must be printed dates, tax IDs must pass their check digits, and totals must still add up — so the model cannot add invented data. Accepted values are marked `ai-refined` in `fields` and listed in `aiRefinedFields`. Default models: `claude-sonnet-5-5` (Anthropic, with automatic server-side fallback if a request is declined) or `gpt-4.1-mini` (OpenAI); for large volumes a cheaper model such as `claude-haiku-5-5` is usually enough because every answer is checked. Your provider bills the LLM usage; we charge `ai-refined-document` only when a verified field was filled or corrected.

### FAQ

**Is this OCR?** Digital PDFs are read from their text layer (fast, exact). Scans need *OCR* on (tesseract.js; slower and less accurate, charged per OCR page).
**Do you check VAT numbers with VIES/HMRC?** No — this Actor checks format and check digits locally (a failing check digit cannot be a real number). For live registry checks use the SiteVerdict VAT Validator Actor.
**Will Xero accept the CSV as is?** Columns follow Xero's *Import bills* template (amounts tax-exclusive). Choose your account code and date format in the input; if your Xero organisation uses different tax-rate names, set *Xero tax rate name*. QuickBooks Online lets you map the columns once on its import screen.
**Are the UBL / Factur-X files valid e-invoices?** They are drafts built from the PDF and are not schematron-validated; `E_INVOICE_GAPS.json` lists what each one is missing (often the buyer, which is off by default for privacy). Factur-X here is the CII XML, not a PDF/A-3 hybrid.
**What does a duplicate mean?** Same file (hash), same supplier + invoice number, or same supplier + amount + date. Identical files are not processed or charged twice. With *Remember invoices across runs* or Monitor mode the keys are kept in a named key-value store in your account.
**Does monitoring re-download everything?** It sends `If-None-Match` / `If-Modified-Since`, so unchanged files usually are not downloaded at all; anything unchanged is skipped and not charged.
**Can I keep my invoices private?** Use pre-signed URLs or upload to a key-value store. PDFs are processed in memory and not kept unless you turn on *Keep copies of the PDFs*.

### Limits

- Accuracy figures come from synthetic test documents; unusual layouts can still miss fields (they are then empty and flagged, not guessed). Poor scans, handwriting and photos of crumpled receipts are hard for any OCR.
- Images (JPG/PNG) are not accepted — PDF only. Max 100 MB / 2,000 pages per file (defaults 25 MB / 50 pages), 10,000 documents per run.
- Folder discovery reads one public page per folder URL (no login, no JavaScript rendering, no bucket pagination) and respects robots.txt.
- Buyer name and VAT number are off by default; turn them on only with a lawful basis.

### Compliance, privacy and sources

- **No AI by default**; with AI on, document text goes only to the provider whose key you supply, under your agreement with them.
- **Personal data:** invoices can contain names of sole traders and buyers. Buyer details are off by default; PDFs are held in memory only; secrets (Authorization header, webhook URL, AI key) are never logged or stored in the output. You are the controller of the documents you process (UK GDPR / EU GDPR); this Actor acts on your instructions.
- **Network etiquette:** honest User-Agent `SiteVerdict/pdf-invoice-extractor (+aman.thakran@bharatfare.com)`, private/internal addresses blocked, robots.txt respected for folder pages, conditional requests in Monitor mode.
- **Rules used:** VAT check-digit algorithms as published by national tax authorities (re-implemented from the public python-stdnum rules), GSTIN check character (GST Network Luhn mod-36), IBAN ISO 13616 mod-97, standard VAT/GST rates in force in October 2026.
- Extraction results are not accounting, tax or legal advice. Check flagged documents against the originals before posting or paying.

Built by SiteVerdict (BharatFare Travel and Technologies Ltd, UK). Questions or a layout we miss: aman.thakran@bharatfare.com.

# Changelog

This Actor's version history is a separate document: https://apify.com/siteverdict/siteverdict-pdf-invoice-extractor/changelog.md

# Actor input Schema

## `pdfUrls` (type: `array`):

Public or pre-signed (S3, GCS, SharePoint/Drive direct-download) PDF links. Query strings of signed URLs are redacted in the output. Apify key-value store record URLs (including files you upload in Apify Console) also work. Anything you put in a request's userData is carried to the output as "context".

## `keyValueStoreRecords` (type: `array`):

PDFs already stored in Apify key-value stores, as "storeIdOrName/key" (or just "key" for this run's default store). Use this for files you upload yourself, so they never need a public URL.

## `inboxMessages` (type: `array`):

E-mail-style batch from your inbox parser, Zapier, Make or mail server: \[{"messageId", "from", "subject", "receivedAt", "attachments": \["https://...pdf" | {"url", "filename"} | {"kvRecord": "store/key", "filename"}]}]. Every PDF attachment becomes one document; messageId/from/subject/receivedAt are copied to the output "context" so you can trace each bill to its e-mail. Non-PDF attachments (by filename) are skipped and not charged. Message bodies are never read.

## `manifestRecord` (type: `string`):

Optional: "storeIdOrName/key" of a JSON record holding the same array as "Inbox batch" (or {"messages": \[...]}) - handy when an automation drops a batch file into a key-value store.

## `folderUrls` (type: `array`):

Public pages that list PDFs: an HTML index / download page, a public S3-style bucket listing (XML), or a JSON list of URLs. Every PDF link found is processed. robots.txt is respected; pages behind a login are not supported. Combine with Monitor mode to process only new or changed files on each scheduled run.

## `folderUrlPattern` (type: `string`):

Optional regular expression for links to pick up from folder pages when they do not end in .pdf (for example "/download?id="). Default: links ending in .pdf.

## `folderMaxPdfs` (type: `integer`):

Safety cap on links taken from each folder page.

## `datasetId` (type: `string`):

Optional: ID of an Apify dataset (e.g. from a crawler) whose items contain PDF URLs.

## `datasetUrlField` (type: `string`):

Name of the field holding the PDF URL in each dataset item.

## `authorizationHeader` (type: `string`):

Sent as the HTTP Authorization header (e.g. "Bearer ...") only to the host of each PDF/folder URL, never to redirect targets on other hosts. Never logged or stored in the output.

## `invoiceMode` (type: `string`):

auto = extract invoice/receipt fields only when the document looks like an invoice, receipt or credit note; always = try on every PDF; off = text and tables only (no invoice-extracted charge).

## `extractTables` (type: `boolean`):

Detect tables from text positions and return them as rows, Markdown and CSV.

## `includeText` (type: `boolean`):

Return the document text with the page layout preserved (columns line up).

## `includePageText` (type: `boolean`):

Also return the text of each page separately.

## `maxTextChars` (type: `integer`):

Longer text is truncated (textTruncated = true). Tables and invoice fields are not affected.

## `numberFormat` (type: `string`):

auto detects per document. uk = 1,234.56 (also Indian 1,23,456.78); eu = 1.234,56 or 1 234,56.

## `dateOrder` (type: `string`):

auto = decided per document: unambiguous dates such as 25/12 or 12/25 settle it, otherwise US signals ($, US address, sales tax) mean month first, else day first. Set DMY or MDY to force one.

## `defaultCurrency` (type: `string`):

ISO code used when a document shows no currency (e.g. GBP, EUR). Leave empty to report null.

## `defaultVatCountry` (type: `string`):

Two-letter country used to check VAT numbers printed without a country prefix (e.g. GB). Auto: GB when the document uses £.

## `includeBuyerDetails` (type: `boolean`):

Off by default because the buyer may be a private individual. Turn on only if you have a lawful basis to process it (needed for complete UBL/Factur-X drafts).

## `reviewConfidencePercent` (type: `integer`):

A document is flagged needsReview when a key field (supplier, invoice number, date, currency, total) is missing or below this confidence, when an error-level check fails, or when it is a possible duplicate. Fields below the threshold are also what the optional AI step looks at.

## `detectDuplicates` (type: `boolean`):

Flag the same file, the same supplier + invoice number, or the same supplier + amount + date seen earlier in the batch. Identical files are not processed or charged twice.

## `rememberAcrossRuns` (type: `boolean`):

Keep duplicate-detection keys (file hash, supplier tax ID/name, invoice number, total, date) in a named key-value store in YOUR Apify account, so an invoice paid last month is flagged when it arrives again. Always on in Monitor mode.

## `monitorMode` (type: `boolean`):

For scheduled runs: remembers every document (content hash, ETag, Last-Modified) in the named state store and outputs only new or changed ones. Unchanged documents are skipped and not charged. Uses polite conditional requests (If-None-Match / If-Modified-Since).

## `stateStoreName` (type: `string`):

Named key-value store that holds monitor and duplicate state. Use one name per supplier folder / client to keep their histories apart.

## `alertWebhookUrl` (type: `string`):

Optional HTTPS webhook called once at the end of the run, e.g. a Slack incoming-webhook URL. Treated as a secret; never logged.

## `alertFormat` (type: `string`):

slack = {"text": ...} message; json = structured payload with the run summary and flagged documents.

## `alertOn` (type: `string`):

problems = something needs review, is a duplicate or failed; new = at least one new/changed invoice; always = every run.

## `exportFormats` (type: `array`):

Files written to the run's key-value store (links in OUTPUT): Excel workbook, Xero bills CSV, QuickBooks Online bills CSV, flat invoices CSV, UBL 2.1 (Peppol BIS 3) XML drafts, Factur-X/ZUGFeRD (CII) XML drafts and an HTML batch report. Exports are included in the price.

## `exportDateFormat` (type: `string`):

Match your accounting organisation's region.

## `xeroAccountCode` (type: `string`):

Expense account for imported bill lines (429 = General Expenses in Xero's default chart).

## `xeroTaxType` (type: `string`):

Leave empty to map automatically (UK: 20% (VAT on Expenses), 5% (VAT on Expenses), Zero Rated Expenses, No VAT; elsewhere: Tax on Purchases). Set to force one tax rate name from your Xero organisation.

## `quickbooksAccount` (type: `string`):

Account name for imported bill lines.

## `writeLineItemsCsv` (type: `boolean`):

Save all line items of the run as one CSV file in the run's key-value store.

## `aiProvider` (type: `string`):

Off by default. When on, only documents flagged needsReview are sent - and only their weak fields are asked for - to YOUR Anthropic or OpenAI account. Every AI answer is verified against the PDF text (values must be printed, tax IDs must pass check digits, totals must add up) before it is accepted. Charged as ai-refined-document only when a verified field was filled or corrected; the LLM usage is billed by your provider.

## `aiApiKey` (type: `string`):

Your own Anthropic or OpenAI API key. Stored encrypted by Apify, never logged or written to the output.

## `aiModel` (type: `string`):

Default: claude-sonnet-5-5 (Anthropic) or gpt-4.1-mini (OpenAI). For high volumes a cheaper model such as claude-haiku-5-5 usually suffices because every answer is verified.

## `aiMaxDocuments` (type: `integer`):

Cost guard for your LLM account.

## `enableOcr` (type: `boolean`):

Read pages without a text layer with tesseract.js OCR. About 2-10 s per page; charged as ocr-page instead of page-processed. Accuracy depends on scan quality.

## `ocrLanguages` (type: `string`):

Tesseract language codes joined by + (max 5), e.g. eng, eng+deu, fra+nld.

## `ocrMinCharsPerPage` (type: `integer`):

A page is OCR'd only when its text layer has fewer characters than this.

## `maxFileSizeMb` (type: `integer`):

Larger files are skipped (status too_large) and not charged.

## `maxPagesPerDocument` (type: `integer`):

Only the first N pages are processed and charged (status partial).

## `maxDocuments` (type: `integer`):

Safety cap on documents per run.

## `maxConcurrency` (type: `integer`):

Documents processed at the same time. Raise with more memory.

## `storeSourceCopies` (type: `boolean`):

Off by default: PDFs are processed in memory and discarded. On = save each successfully processed PDF to this run's key-value store (kept for your storage retention period).

## Actor input object example

```json
{
  "pdfUrls": [
    {
      "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
    }
  ],
  "folderMaxPdfs": 200,
  "datasetUrlField": "url",
  "invoiceMode": "auto",
  "extractTables": true,
  "includeText": true,
  "includePageText": false,
  "maxTextChars": 100000,
  "numberFormat": "auto",
  "dateOrder": "auto",
  "includeBuyerDetails": false,
  "reviewConfidencePercent": 80,
  "detectDuplicates": true,
  "rememberAcrossRuns": false,
  "monitorMode": false,
  "stateStoreName": "siteverdict-invoice-state",
  "alertFormat": "slack",
  "alertOn": "problems",
  "exportFormats": [
    "xlsx",
    "xero",
    "quickbooks",
    "html"
  ],
  "exportDateFormat": "DMY",
  "xeroAccountCode": "429",
  "quickbooksAccount": "Uncategorized Expense",
  "writeLineItemsCsv": true,
  "aiProvider": "none",
  "aiMaxDocuments": 100,
  "enableOcr": false,
  "ocrLanguages": "eng",
  "ocrMinCharsPerPage": 20,
  "maxFileSizeMb": 25,
  "maxPagesPerDocument": 50,
  "maxDocuments": 1000,
  "maxConcurrency": 2,
  "storeSourceCopies": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `output` (type: `string`):

No description

## `files` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "pdfUrls": [
        {
            "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("siteverdict/siteverdict-pdf-invoice-extractor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "pdfUrls": [{ "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf" }] }

# Run the Actor and wait for it to finish
run = client.actor("siteverdict/siteverdict-pdf-invoice-extractor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "pdfUrls": [
    {
      "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
    }
  ]
}' |
apify call siteverdict/siteverdict-pdf-invoice-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,siteverdict/siteverdict-pdf-invoice-extractor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/yuL9OdjwYohfSypaz/builds/DRs35gdzHDeJtqizB/openapi.json
