# ZUGFeRD & Factur-X PDF to JSON - Invoice Parser & Extractor (`kamerozkan/zugferd-facturx-pdf-to-json`) Actor

Extract embedded invoice XML from hybrid PDF/A-3 files, normalize EN 16931 data, and return a versioned technical conformance report. E-invoice parser and data extraction for ZUGFeRD and Factur-X: every parsed field ships with validation findings, never silent guesses.

- **URL**: https://apify.com/kamerozkan/zugferd-facturx-pdf-to-json.md
- **Developed by:** [Kamer Ozkan](https://apify.com/kamerozkan) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 document processeds

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## ZUGFeRD & Factur-X PDF to JSON - Invoice Parser & Extractor

Extract the embedded CII invoice from a hybrid PDF and return one stable,
EN 16931-oriented JSON result with technical evidence.

### What it does

- Accepts up to 20 hybrid PDFs per run and 20 MiB per PDF.
- Reads uploads, inline base64, public or signed HTTPS URLs, and explicit
  key-value store records.
- Extracts exactly one recognized invoice attachment in an isolated worker.
- Checks PDF/A-3 identification, XMP metadata, attachment name,
  `AFRelationship`, MIME type, stream digest, and profile consistency.
- Validates MINIMUM, BASIC WL, BASIC, EN16931, and EXTENDED profile XML with
  release-pinned ZUGFeRD 2.5 / Factur-X 1.09 assets.
- Validates ZUGFeRD 1.0 embedded XML with pinned Mustangproject `2.24.0`,
  then makes a digest-verified, fail-closed D16B upgrade attempt before JSON
  normalization.
- Normalizes available CII business terms into the shared JSON contract with
  EN 16931 trace entries and decimal strings.
- Can store extracted XML, validator XML, and veraPDF reports in the run
  key-value store.

`normalizationStatus` is independent from technical conformance. It is
`COMPLETE` only when the full declared binding was transferred without
unmapped evidence or converter warnings. Reduced profiles and unsupported
extension semantics can return honest `PARTIAL` output.

For a successful ZUGFeRD 1.0 path, normalization provenance is
`LEGACY_ZF1_VIA_PINNED_UPGRADE`. The original embedded XML remains the
extractable artifact; its digest and the upgraded D16B digest are recorded
separately. The legacy XSLT is not claimed to preserve every source semantic,
so its integrity boundary is reported as `NOT_PROVEN_BY_UPGRADER`.

### Product boundary

A regular PDF without embedded invoice XML is rejected. OCR is not used to
guess invoice values.

The Actor does not compare visible PDF text with the embedded XML and does not
verify electronic signatures. Its report is a technical assessment, not a
legal-validity, tax-recognition, authenticity, or recipient-acceptance
decision.

Raw XML and technical reports are opt-in KVS artifacts because they can
contain invoice business data. Dataset rows contain metadata, hashes, report
summaries, and normalized JSON.

### Billing contract

The processing engine and Actor contract are feature-complete. The source
contains a fail-closed pay-per-event charge path for `document-processed` at
exactly `$0.005` per evaluated document. Local non-PPE results remain
`billable: false`; a hosted run stops before processing unless the event and
price contract are active. The synthetic `apify-actor-start` event is allowed,
while `apify-default-dataset-item` must be absent or cost USD 0.

The live Apify pricing configuration uses `document-processed` as the primary
event. A pinned hybrid ZUGFeRD PDF hosted smoke produced one billable Dataset
result and exactly one custom document charge. Source failures,
`NOT_EVALUATED` results, runtime failures, and budget-limited documents are not
eligible for the custom document charge.

### Real output from a live run

Unedited record from a live run on 2026-07-30 against an official ZUGFeRD corpus PDF; long sub-objects are shortened here, values are exactly as returned:

```json
{
  "contractVersion": "einvoice-suite-result/1.0.0",
  "actorName": "zugferd-facturx-pdf-to-json",
  "itemId": "corpus-zugferd-en16931",
  "inputIndex": 0,
  "fileName": "zugferd_2p0_EN16931_Einfach.pdf",
  "producedAt": "2026-07-30T08:40:44.866151Z",
  "processingStatus": "SUCCEEDED",
  "conformanceStatus": "ACCEPTED",
  "billable": true,
  "scope": "DOCUMENT_EXTRACTION_NORMALIZATION_AND_TECHNICAL_CHECKS",
  "source": {
    "sourceKind": "URL",
    "container": "PDF_A_3",
    "syntax": "CII_INVOICE",
    "sha256": "29842457f57143aba5da78fdf390aba773c965e60b1caab93dfff3e9d4c802f4",
    "embeddedXmlSha256": "5600027528d5ffaf89fb0be911c73ec84125a98411525e7299c7ca3a0ab073f7",
    "standard": {
      "family": "ZUGFERD",
      "profile": "ZUGFERD_EN16931",
      "scenario": "ZUGFeRD 2.5 / Factur-X 1.09 EN16931",
      "version": "2.x"
    }
  },
  "normalizationStatus": "PARTIAL",
  "report": "(report, shortened here)",
  "container": "(container, shortened here)",
  "engineDurationMs": 6533,
  "artifacts": {
    "embeddedXml": {
      "mediaType": "application/xml",
      "byteLength": 10702,
      "sha256": "5600027528d5ffaf89fb0be911c73ec84125a98411525e7299c7ca3a0ab073f7",
      "storageStatus": "NOT_REQUESTED"
    }
  },
  "invoice": "(invoice, shortened here)"
}
```

#### When the PDF is not an e-invoice

A plain, non-hybrid PDF is refused with a named error instead of a guessed result, and `billable: false` means it is not charged. Unedited record from a live run on 2026-07-30:

```json
{
  "contractVersion": "einvoice-suite-result/1.0.0",
  "actorName": "zugferd-facturx-pdf-to-json",
  "itemId": "plain-pdf",
  "inputIndex": 0,
  "fileName": "plain.pdf",
  "producedAt": "2026-07-30T08:52:37.059056Z",
  "processingStatus": "FAILED",
  "conformanceStatus": "NOT_EVALUATED",
  "billable": false,
  "scope": "DOCUMENT_EXTRACTION_NORMALIZATION_AND_TECHNICAL_CHECKS",
  "error": {
    "code": "PDF_PARSE_FAILED",
    "message": "The input PDF could not be parsed safely"
  }
}
```

This Actor is also exposed to AI agents through Apify's MCP server
(mcp.apify.com): an agent can discover it by search and run it with the same
pay-per-event billing, with no separate integration.

### E-invoice tool family

Validators, one per market, same output discipline:
[XRechnung & ZUGFeRD](https://apify.com/kamerozkan/xrechnung-xml-batch-validator-api) | [Peppol BIS](https://apify.com/kamerozkan/peppol-bis-preflight-validator) | [France](https://apify.com/kamerozkan/france-einvoice-validator) | [Poland KSeF](https://apify.com/kamerozkan/poland-ksef-preflight-validator) | [Italy FatturaPA](https://apify.com/kamerozkan/italy-fatturapa-validator)

# Actor input Schema

## `invoiceFiles` (type: `array`):

Upload hybrid ZUGFeRD or Factur-X PDF files.

## `documents` (type: `array`):

Each item must contain exactly one source: url, base64, or keyValueStoreKey.

## `sourceKeyValueStoreId` (type: `string`):

Key-value store ID containing records referenced by keyValueStoreKey.

## `allowPartialNormalization` (type: `boolean`):

Return safely parsed fields even when the document is technically non-conformant. The report remains non-conformant or indeterminate.

## `storeEmbeddedXml` (type: `boolean`):

Store the complete embedded invoice XML in the run key-value store and return only its key and digest in Dataset.

## `storeTechnicalReports` (type: `boolean`):

Store the sanitized ZUGFeRD rules report and veraPDF report in the run key-value store.

## `maxFindingsPerDocument` (type: `integer`):

Maximum number of PDF, XML, and business-rule findings returned for each invoice.

## Actor input object example

```json
{
  "documents": [
    {
      "documentId": "official-zugferd-sample",
      "fileName": "zugferd_2p0_EN16931_Einfach.pdf",
      "url": "https://raw.githubusercontent.com/ZUGFeRD/corpus/master/ZUGFeRDv2/correct/intarsys/EN16931/zugferd_2p0_EN16931_Einfach.pdf"
    }
  ],
  "allowPartialNormalization": true,
  "storeEmbeddedXml": false,
  "storeTechnicalReports": false,
  "maxFindingsPerDocument": 100
}
```

# Actor output Schema

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

Default Dataset rows validated against the versioned invoice, conversion, conformance, and semantic-difference contract.

## `summary` (type: `string`):

The OUTPUT key-value-store record with aggregate processing and billing counts.

## `artifacts` (type: `string`):

ARTIFACT-prefixed key-value-store records written only when artifact storage was requested.

# 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 = {
    "documents": [
        {
            "documentId": "official-zugferd-sample",
            "fileName": "zugferd_2p0_EN16931_Einfach.pdf",
            "url": "https://raw.githubusercontent.com/ZUGFeRD/corpus/master/ZUGFeRDv2/correct/intarsys/EN16931/zugferd_2p0_EN16931_Einfach.pdf"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("kamerozkan/zugferd-facturx-pdf-to-json").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 = { "documents": [{
            "documentId": "official-zugferd-sample",
            "fileName": "zugferd_2p0_EN16931_Einfach.pdf",
            "url": "https://raw.githubusercontent.com/ZUGFeRD/corpus/master/ZUGFeRDv2/correct/intarsys/EN16931/zugferd_2p0_EN16931_Einfach.pdf",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("kamerozkan/zugferd-facturx-pdf-to-json").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "documents": [
    {
      "documentId": "official-zugferd-sample",
      "fileName": "zugferd_2p0_EN16931_Einfach.pdf",
      "url": "https://raw.githubusercontent.com/ZUGFeRD/corpus/master/ZUGFeRDv2/correct/intarsys/EN16931/zugferd_2p0_EN16931_Einfach.pdf"
    }
  ]
}' |
apify call kamerozkan/zugferd-facturx-pdf-to-json --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=kamerozkan/zugferd-facturx-pdf-to-json",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/E5MjN9GowX6maWUAz/builds/wwRlBIS9n9VgVGjok/openapi.json
