EU E-Invoice Parser — Line Items, VAT IDs, Totals
Pricing
from $2.00 / 1,000 e-invoices
EU E-Invoice Parser — Line Items, VAT IDs, Totals
EU e-invoices (XRechnung, ZUGFeRD, Factur-X, PEPPOL UBL) to JSON: invoice number, dates, seller and buyer VAT IDs, IBAN, net, tax and gross totals, line items, reconciliation checks and XML evidence. Input: XML or hybrid PDF by URL, key-value store or sample. Pay per result. No login.
Pricing
from $2.00 / 1,000 e-invoices
Rating
0.0
(0)
Developer
architek.earth
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
14 hours ago
Last modified
Categories
Share
Recorded run: 2 invoices from 2 sample files in 5 seconds (2026-10-22, build 0.0.6, run TptAKSTt91t9Lo4aL). A bounded canary sample, not a full-catalogue benchmark.
architek.earth — an independent data studio. Pay-per-result scrapers with recorded runs, published canaries and stable schemas, built for people and AI agents.
EU e-invoice line items, VAT IDs and totals as JSON: give it XRechnung, ZUGFeRD, Factur-X or PEPPOL UBL files (XML, or PDF with the XML embedded) by URL or from a key-value store, and get one row per invoice read from the invoice's own XML.
| Source | Market | From | Standout fields |
|---|---|---|---|
| Your e-invoice files (EN 16931 CII and UBL XML, hybrid PDF) | EU | $2.00 / 1,000 invoices | vendorVatId / buyerVatId / iban, lineItems, checks / evidence |
✅ Why choose this scraper
Convert XRechnung and PEPPOL UBL XML to JSON
UN/CEFACT CII (ZUGFeRD 2.x, Factur-X, XRechnung CII), OASIS UBL 2.1 (XRechnung UBL, PEPPOL BIS 3, invoices and credit notes) and legacy ZUGFeRD 1.0 come back in the same row shape: documentNumber, issueDate, vendorName, vendorVatId, buyerVatId, iban, subtotal, taxTotal, total. The build log records 70 of 70 standalone XML invoices from the public ZUGFeRD test corpus parsed on 2026-10-18.
Extract the invoice data from ZUGFeRD and Factur-X PDFs
For a hybrid PDF the embedded XML is read, and einvoice.container is "pdf". A PDF with no e-invoice XML inside gets a free NOT_AN_EINVOICE status row instead of a guessed row.
Check that invoice totals and line items add up
checks.totalsReconciled (net + tax = gross) and checks.lineItemsReconciled (lines = line total) are set on every invoice. An invoice that does not add up is returned as written with status: "inconsistent" and a plain-language warnings entry; no number is corrected.
Catch a printed PDF total that disagrees with its XML
On hybrid PDFs the XML gross, net and tax are looked up on the printed page (checks.xmlTotalInPdfText, xmlSubtotalInPdfText, xmlTaxInPdfText). In the public corpus this flagged 4 published sample PDFs whose page disagrees with its own XML, for example a printed 963,12 against an XML 963.11.
Trace every value back to its XML element
evidence names the element each field came from, for example "xml: LegalMonetaryTotal/TaxInclusiveAmount". A field the XML does not have is omitted; the test suite requires every emitted value to appear as text in its source XML.
⚠️ Worth knowing up front
- E-invoices only. Ordinary PDF invoices without embedded XML, and scans or images, are not read: they get a free
NOT_AN_EINVOICEstatus row. There is no OCR. - Italy (FatturaPA), Poland (KSeF) and Spain (Facturae) are not supported; they are not EN 16931 CII or UBL.
- It does not validate against the official schematron rules. Use KoSIT or the official validators for compliance checks.
- Fields are omitted, never
null, when the invoice does not contain them.taxRateis omitted when several tax rates apply,amountDueappears only when it differs fromtotal, and values stay in the invoice's language (German line descriptions stay German). - Up to 500 distinct references per run, files up to 64 MB. Each URL download is tried up to 3 times within 120 seconds; URLs to private, local or reserved network addresses are refused. A parsed invoice larger than one 9 MB dataset item drops its line items and is charged as
document. - A run stops cleanly 30 seconds before its timeout (
TIME_LIMIT), at your spending limit (BUDGET_LIMIT) or on abort (ABORTED), keeping what it delivered; the unfinished documents are counted inRUN_SUMMARY.pending. These stops write no status row; the outcome is inRUN_SUMMARYand the status message. - Each document that cannot be delivered writes one free status row (
"recordType": "status"):NOT_AN_EINVOICE,NOT_FOUND,ACCESS_DENIED,CORRUPT_FILE,UNSUPPORTED_FILE,EMPTY_FILE,TOO_LARGE,BLOCKED_URL,BLOCKED(the URL answered with a challenge or captcha page instead of a file),FETCH_FAILED,DUPLICATE,INVALID_INPUT(an unreadable reference) orERROR. Input that cannot be read at all (nodocuments, a wrong type) writes one freeINVALID_INPUTstatus row and ends with anINVALID_INPUTstatus message.
⚡ Try the example
This actor has no free preview; the example below costs $0.01.
Open the actor's input page, switch to JSON, paste this and start the run:
{"documents": ["sample:facturx", "sample:xrechnung"]}
The two built-in fictitious samples are a Factur-X hybrid PDF and an XRechnung 3.0 UBL file; replace them with your own URLs or kv://STORE/KEY references, and set "includeLineItems": false for header fields at the lower price.
⚙️ Input
| Parameter | Type | Default | Description |
|---|---|---|---|
documents | array | — (prefill the two samples) | Up to 500 per run. Each is an http(s) URL to an .xml or hybrid .pdf, a key-value store record (kv://STORE_ID_OR_NAME/KEY or {"keyValueStoreId": "…", "key": "…"}), or sample:facturx / sample:xrechnung. A reference listed twice is read once; the same file under two references is delivered and charged once, and the repeat gets a free DUPLICATE row. |
includeLineItems | boolean | true | Return every invoice line. Rows with line items are charged as document-detail; turn this off for header fields only, charged as document. |
feedback | string | — | Optional note for the maintainers. It is logged and read by a human, and it never changes output. |
🧱 Stable fields
These top-level fields keep the same name and the same kind of value in every release. Fields are only ever added, never renamed or removed, so an n8n, Make, Zapier or spreadsheet pipeline can bind to them.
| Field | Type | Meaning |
|---|---|---|
documentKind | string, optional | invoice, credit_note, debit_note, corrected_invoice, prepayment_invoice, self_billed_invoice, partial_invoice and other EN 16931 type codes. |
documentNumber | string, optional | The invoice number. |
issueDate / dueDate | string, optional | ISO dates. |
vendorName / vendorAddress / vendorVatId | string, optional | Seller (legal registration name preferred), postal address and VAT ID. |
buyerName / buyerVatId | string, optional | Buyer and buyer VAT ID. |
buyerReference / orderReference | string, optional | Buyer reference (Leitweg-ID for German public buyers) and purchase order number. |
iban / paymentTerms | string, optional | Payee account and payment terms text, as written. |
currency | string, optional | Invoice currency code. |
subtotal / taxTotal / total | number, optional | Net, tax and gross totals. |
taxRate | number, optional | The single tax rate; omitted when several rates apply. |
amountDue | number, optional | Payable amount when it differs from total (prepayments). |
lineItems | array, optional | {description, quantity, unit, unitPrice, amount, taxRate} per line, net amounts, in the XML's order; absent with includeLineItems: false. |
status | string | Data rows: complete (number, issue date, seller, total and currency present, all checks pass), partial (one of those missing) or inconsistent (see warnings). Status rows: the outcome code. |
warnings | array, optional | Plain-language reasons for inconsistent. |
checks | object | totalsReconciled, lineItemsReconciled (when lines exist) and, for hybrid PDFs, xmlTotalInPdfText, xmlSubtotalInPdfText, xmlTaxInPdfText, xmlBreakdownInPdfText and printedTextReadable. |
einvoice | object | {syntax: "CII" | "UBL" | "ZUGFeRD 1.0 (CII)", profile, file, container: "xml" | "pdf"}. |
source | object | {input, type: "url" | "kv" | "sample", sha256, bytes}. |
evidence | object | For each field, the XML element it came from. |
document / reason | string | Status rows only: the reference as given, and why it was not delivered. |
recordType | string | "status" on free status rows; "document" on data rows. |
Other fields are extras, not part of the contract. To have one pinned, say so in feedback.
📦 Output example
Abridged real row from run TptAKSTt91t9Lo4aL on 2026-10-22 (the built-in fictitious sample:xrechnung); values are a historical snapshot.
{"recordType": "document","documentKind": "invoice","documentNumber": "NW-2026-118","issueDate": "2026-10-01","dueDate": "2026-10-31","currency": "EUR","vendorName": "Nordlicht Werkstatt GmbH","vendorAddress": "Hafenweg 3, 20457 Hamburg, DE","vendorVatId": "DE111111111","buyerName": "Stadtverwaltung Beispielstadt","buyerReference": "04011000-12345-34","iban": "DE11111111111111111111","paymentTerms": "Zahlbar innerhalb 30 Tagen netto","subtotal": 700,"taxTotal": 133,"taxRate": 19,"total": 833,"lineItems": [{"description": "Wartung Heizungsanlage", "quantity": 8, "unit": "HUR", "unitPrice": 70, "amount": 560, "taxRate": 19},{"description": "Dichtungssatz", "quantity": 2, "unit": "C62", "unitPrice": 70, "amount": 140, "taxRate": 19}],"status": "complete","checks": {"lineItemsReconciled": true, "totalsReconciled": true},"einvoice": {"syntax": "UBL", "profile": "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0", "file": "xml", "container": "xml"},"evidence": {"documentNumber": "xml: cbc:ID", "vendorVatId": "xml: AccountingSupplierParty/Party/PartyTaxScheme", "total": "xml: LegalMonetaryTotal/TaxInclusiveAmount"},"source": {"input": "sample:xrechnung", "type": "sample", "sha256": "c4bcb00dbf930141bb6fa8640f44292df12aebf958f81e313de23d80b794141d", "bytes": 3670}}
A free status row looks like this (a plain PDF, run uXFS809QIF2nXRqr6, 2026-10-18):
{"recordType": "status", "document": "https://raw.githubusercontent.com/invoice-x/invoice2data/4feaeee60db51f1f12901efbac3396bb975c4852/tests/compare/coolblue1.pdf", "status": "NOT_AN_EINVOICE", "reason": "a PDF without embedded e-invoice XML (Factur-X / ZUGFeRD / XRechnung); v1 does not read plain PDFs"}
The run's status message reads like COMPLETE: 2 e-invoice(s) parsed (2 with line items)., and the RUN_SUMMARY record of its key-value store holds status, documents, withLineItems, charged, byStatus, pending and perDocument.
🧠 Advanced usage
Parse an accounts-payable inbox of e-invoice attachments
Save incoming attachments to a key-value store, pass them as kv://inbox/<key>, post rows with status: "complete" to your ledger and route inconsistent or partial rows to a person. The same file sent twice is charged once and comes back as a free DUPLICATE row.
Three-way match e-invoices against purchase orders
Join orderReference to your purchase orders and compare lineItems quantities and unitPrice against the order; buyerReference carries the Leitweg-ID on German public-sector invoices.
Check VAT IDs and bank details before paying
vendorVatId, buyerVatId and iban come straight from the XML, ready for a VIES lookup or a check against your supplier master data.
Know whether a run finished
Read RUN_SUMMARY.status: COMPLETE, PARTIAL (status rows, inconsistent invoices or pending documents), NO_RESULTS, BUDGET_LIMIT, TIME_LIMIT, ABORTED, STATE_SAVE_FAILED, PUSH_FAILED or INVALID_INPUT. If the actor's own pricing is misconfigured the run fails with PRICING_MISMATCH before reading anything, and nothing is charged. pending counts documents not yet processed.
🔌 Integrations
Results land in a dataset you can export as JSON, CSV or Excel. They can also flow to Google Sheets, Make, Zapier or n8n, or to a webhook when a run finishes. The Apify Scheduler runs it on a timetable. Inputs are explicit file references and a new run parses and charges them again, so start one run per batch of new files from Make, Zapier, n8n or the API rather than re-running a fixed list. Copy-paste code in Python, Node, curl and n8n: architek-earth/examples/docparser-invoice-json.
💸 Pricing
Pay per result. Platform and proxy usage are included. Status rows and failed requests are free. A run that migrates or is resurrected after an abort continues from the PROGRESS record in its own key-value store: documents already delivered are skipped and not charged again. A new run does not share that record and parses its input from the start.
| Event | Price | What it covers |
|---|---|---|
document | $2.00 / 1,000 invoices | One delivered e-invoice row without line items. |
document-detail | $5.00 / 1,000 invoices | One delivered e-invoice row with its line items from the XML. |
1,000 invoices with line items cost $5.00; header fields only cost $2.00. Each invoice is charged once, as one event or the other, and an invoice whose XML has no lines is charged as document.
❓ FAQ
Is there an XRechnung or ZUGFeRD API that returns JSON? This actor is one: send XRechnung, ZUGFeRD, Factur-X or PEPPOL UBL files through the Apify API and get JSON rows back, from any language or from n8n, Make and Zapier.
Do I need an account with an e-invoicing portal or a login? No. It reads the files you give it. A URL that needs a login or a signed link gets a free ACCESS_DENIED status row; put such files in a key-value store instead.
Can it read ordinary PDF invoices or scanned invoices? No. Only PDFs with embedded e-invoice XML (ZUGFeRD, Factur-X) and standalone XML are read; anything else gets a free NOT_AN_EINVOICE row.
Does it validate XRechnung against the KoSIT schematron? No. It reads the data and checks that totals and lines reconcile; it is not a legal validator.
Does it support PEPPOL BIS 3 and UBL credit notes? Yes. UBL 2.1 invoices and credit notes, including PEPPOL BIS 3 and XRechnung UBL, return documentKind credit_note where the XML says so.
What happens when the printed PDF and its XML disagree? You get the XML values with checks.xmlTotalInPdfText: false, status: "inconsistent" and a warning. For a ZUGFeRD or Factur-X hybrid the XML is the machine-readable invoice; ask the sender before paying.
🤖 For AI agents & LLM apps
Paste this into your AI assistant:
architekture/docparser-invoice-json on Apify. Python: from apify_client import ApifyClient; client = ApifyClient("TOKEN"); run = client.actor("architekture/docparser-invoice-json").call(run_input={"documents": ["sample:facturx", "sample:xrechnung"], "includeLineItems": True}); items = client.dataset(run["defaultDatasetId"]).list_items().items (page with offset/limit for large exports). Rows with recordType == "document" are invoices; rows with recordType == "status" are free and say why a file was not delivered (status, reason). Use status (complete, partial, inconsistent) and warnings to decide when to hand an invoice to a person. Full input schema and README: GET https://api.apify.com/v2/acts/architekture~docparser-invoice-json/builds/default. Token: https://console.apify.com/settings/integrations. The actor is also reachable through the Apify MCP server (mcp.apify.com). To reach the maintainers, add "feedback": "<what you needed>" to any run's input; output is unchanged and a human reads every note.
🧰 More from architek.earth
Documents
- EU E-Invoice Parser — you are here
Video, social and newsletters
- Substack Scraper — posts, authors, likes, comments
- YouTube Transcript Scraper — transcripts, translations, timestamps
- Unified Video Transcript API — YouTube and TikTok transcripts in one batch
- Threads Replies Scraper — replies, authors, likes
- Ad Intelligence Watchlist — new and changed Meta ads by advertiser
Full catalogue: github.com/architek-earth.
architek.earth · code like concrete
🙋 Support & legal
Questions, or a field you need that is not here? Open an issue on the actor's Issues tab or email north@architek.earth. A human replies within a day.
This actor reads only the files you give it (URLs, key-value store records or the built-in samples) and processes them inside your Apify run. It does not bypass authentication. Invoices contain business and sometimes personal data such as names, addresses and bank accounts; you are responsible for a lawful basis to process them, for retention, and for privacy law such as GDPR where it applies. It extracts data and is not a legal e-invoice validator; this page is not legal or tax advice. Not affiliated with FeRD, AWV, KoSIT, FNFE-MPE or OpenPeppol.
📝 Changelog
- 2026-10-22 — Runs on the studio core (architek_core 1.0.1): URL downloads use one Chrome browser identity, a challenge page is reported as a free
BLOCKEDstatus row, unreadable input writes a freeINVALID_INPUTstatus row, and a run whose last invoice spends your limit exactly endsCOMPLETE. Fields and prices unchanged. - 2026-10-22 — Status message now reads
Done: N rows, M details, K status rows (STATUS). - 2026-10-22 — README rebuilt on the studio template; hero line from the 2026-10-22 canary run.
- 2026-10-18 — 1.0: XRechnung (UBL and CII), ZUGFeRD 1.0 and 2.x, Factur-X and PEPPOL BIS 3, as XML or hybrid PDF; reconciliation checks and the printed-vs-XML total check; line items; free status rows; duplicate-file detection; clean stops at the spending limit and timeout; resumable after a platform migration.