POST /v1/parse
Parse an e-invoice XML or hybrid PDF into JSON, with a list of what the document holds beyond it.
Upload an invoice or credit note (XML or PDF) and get its main fields back as JSON.
The JSON is a subset of the document. What the document holds beyond it is listed in warnings, in every response.
Both XML documents and hybrid PDFs are supported.
Quick copy examples
Parse XML:
curl -X POST https://api.beliq.eu/v1/parse \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/xml" \
--data-binary @invoice.xmlimport { readFile } from 'node:fs/promises';
import { Beliq } from '@beliq/sdk';
const beliq = new Beliq({ apiKey: process.env.BELIQ_API_KEY! });
const parsed = await beliq.parse(await readFile('invoice.xml'));
console.log(parsed.format, parsed.profileDetected);
console.log(parsed.invoice);import os
from pathlib import Path
from beliq import Beliq
beliq = Beliq(api_key=os.environ["BELIQ_API_KEY"])
parsed = beliq.parse(Path("invoice.xml").read_bytes())
print(parsed.format, parsed.profile_detected)
print(parsed.invoice)Parse PDF:
curl -X POST https://api.beliq.eu/v1/parse \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/octet-stream" \
--data-binary @invoice.pdfimport { readFile } from 'node:fs/promises';
import { Beliq } from '@beliq/sdk';
const beliq = new Beliq({ apiKey: process.env.BELIQ_API_KEY! });
const parsed = await beliq.parse(await readFile('invoice.pdf'));
console.log(parsed.format, parsed.profileDetected);
console.log(parsed.invoice);import os
from pathlib import Path
from beliq import Beliq
beliq = Beliq(api_key=os.environ["BELIQ_API_KEY"])
parsed = beliq.parse(Path("invoice.pdf").read_bytes())
print(parsed.format, parsed.profile_detected)
print(parsed.invoice)Request
POST /v1/parse
Content-Type: application/xml | application/octet-stream
Authorization: Bearer <api-key>Headers
| Header | Required | Description |
|---|---|---|
Authorization |
required | Bearer <api-key> or use X-API-Key header |
Content-Type |
required | application/xml for XML input, application/octet-stream for PDF or binary input |
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
format |
string | optional | Has no effect on the result: Beliq always detects the format from the document itself, whatever you send here. The value is still checked. Anything other than auto, cii, ubl, fatturapa, sdi_messaggio, facturae, eslog, poland_ksef_fa3 or poland_ksef_fa2 (the values POST /v1/validate accepts) answers 400 VALIDATION_ERROR. Sending one of them does not widen what this endpoint parses: see the supported input formats above |
A query parameter this table does not list answers 400 VALIDATION_ERROR, and error.details.fields names it as a JSON Pointer, such as /formt.
Request body
The raw XML or PDF file content as the request body. Do not JSON-encode it.
curl -X POST https://api.beliq.eu/v1/parse \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/xml" \
--data-binary @invoice.xmlimport { readFile } from 'node:fs/promises';
import { Beliq } from '@beliq/sdk';
const beliq = new Beliq({ apiKey: process.env.BELIQ_API_KEY! });
const parsed = await beliq.parse(await readFile('invoice.xml'));
console.log(parsed.format, parsed.profileDetected);
console.log(parsed.invoice);import os
from pathlib import Path
from beliq import Beliq
beliq = Beliq(api_key=os.environ["BELIQ_API_KEY"])
parsed = beliq.parse(Path("invoice.xml").read_bytes())
print(parsed.format, parsed.profile_detected)
print(parsed.invoice)Response
Alongside the JSON body below, the response reports the detected syntax and profile as headers. See the Response headers reference.
Success (200)
{
"success": true,
"data": {
"format": "cii",
"profileDetected": "xrechnung",
"profileUrn": "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0",
"businessProcessId": "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0",
"invoice": {
"number": "INV-2026-001",
"issueDate": "2026-04-13",
"currencyCode": "EUR",
"documentType": "invoice",
"typeCode": "380",
"buyerReference": "04011000-12345-03",
"seller": {
"name": "Acme GmbH",
"address": {
"street": "Musterstraße 1",
"city": "Berlin",
"postalCode": "10115",
"countryCode": "DE"
},
"vatId": "DE123456789"
},
"buyer": {
"name": "Bundesministerium für Beispiele",
"address": {
"street": "Beispielweg 42",
"city": "Bonn",
"postalCode": "53113",
"countryCode": "DE"
}
},
"lines": [
{
"description": "IT consulting, April 2026",
"quantity": 20.0,
"unitCode": "HUR",
"unitPrice": 120.0,
"lineTotal": 2400.0,
"vatRate": 19.0,
"vatCategoryCode": "S"
}
],
"totalNetAmount": 2400.0,
"totalTaxAmount": 456.0,
"totalGrossAmount": 2856.0
},
"warnings": [
{
"code": "PARSE_NOT_RETURNED",
"message": "The document holds elements that this response does not return. elements lists each one by its path, with the number of times it occurs. terms names these EN 16931 business groups when one is among them: BG-3, BG-16, BG-20, BG-21, BG-23, BG-27, BG-28, BG-32. Attributes of an element that was read are not listed.",
"elements": [
{
"path": "/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:URIUniversalCommunication",
"count": 1
},
{
"path": "/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:URIUniversalCommunication",
"count": 1
},
{
"path": "/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax",
"count": 1
},
{
"path": "/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradePaymentTerms",
"count": 1
},
{
"path": "/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:DuePayableAmount",
"count": 1
},
{
"path": "/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:TaxBasisTotalAmount",
"count": 1
},
{
"path": "/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementPaymentMeans",
"count": 1
},
{
"path": "/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:AssociatedDocumentLineDocument",
"count": 1
},
{
"path": "/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:TypeCode",
"count": 1
}
],
"terms": [
"BG-16",
"BG-23"
]
}
]
}
}The document behind this example also carries a due date, payment terms, payment means and both parties’ electronic addresses. None of them is in invoice, and each is in warnings.
Parse response object
| Field | Type | Description |
|---|---|---|
format |
string | Detected syntax: cii or ubl |
profileDetected |
string? | A label for the specification identifier when Beliq knows it: xrechnung, peppol, en16931, facturx-<profile> or extended-ctc-fr |
profileUrn |
string? | BT-24. The specification identifier as the document carries it |
businessProcessId |
string? | BT-23. The business process type as the document carries it. This is not invoice.businessProcessId, a generate input that takes the three French codes only |
franceCtcDetected |
boolean? | true when BT-23 is one of the French cadre de facturation codes or BT-24 is the EXTENDED-CTC-FR identifier. Absent otherwise |
invoice |
Invoice | The fields read from the document |
warnings |
Warning[] | What this response says about itself. Always present, and empty only when the parser read every element that holds something |
Parsed invoice object
The parsed invoice uses the field names of the generate request invoice object and returns the fields in the tables below, whether the input was UBL or CII. It returns no other field of that object.
Every field but lines is returned only when the document states a value for it.
| Field | Type | Always present | Description |
|---|---|---|---|
number |
string | optional | BT-1. Invoice number |
issueDate |
string | optional | BT-2. Issue date, read as a date |
currencyCode |
string | optional | BT-5. Document currency code |
documentType |
string | optional | invoice or creditnote. On UBL the root element decides it and it is always present. On CII the type code decides it: a code that EN 16931 rule BR-CL-01 admits for an invoice only or for a credit note only. For a code it admits for both (81) or for neither, and for a document with no type code, the field is absent. Absent does not mean invoice |
typeCode |
string | optional | BT-3. The type code as the document carries it, such as 380 or 381 |
buyerReference |
string | optional | BT-10. Buyer reference |
seller |
Party | optional | Seller name, identifiers and postal address. Left out when the document states no field of the party object |
buyer |
Party | optional | Buyer name, VAT identifier and postal address. Left out when the document states no field of the party object |
lines |
InvoiceLine[] | always | BG-25. The invoice lines, or the credit note lines of a UBL credit note. Empty for a document that has none, such as Factur-X MINIMUM |
delivery |
Delivery | optional | BG-13. Delivery information. Present only when the document carries at least one of the values below |
totalNetAmount |
number | optional | BT-106. Sum of the line net amounts |
totalTaxAmount |
number | optional | BT-110. The total VAT amount in the document currency: the amount whose currencyID is the document currency code, which is how EN 16931 rule BR-CO-15 selects it. When the document holds no such amount the field is left out, and no PARSE_VALUE_NOT_FOUND warning is sent. When it holds more than one, none of them is returned and a PARSE_VALUE_NOT_FOUND warning says so. An amount with no currencyID, and the amount in the VAT accounting currency (BT-111), are not returned here: they come under PARSE_NOT_RETURNED |
totalGrossAmount |
number | optional | BT-112. Total with VAT |
Parsed party object
| Field | Type | Always present | Description |
|---|---|---|---|
name |
string | optional | BT-27 or BT-44. On UBL the registration name, cac:PartyLegalEntity/cbc:RegistrationName. The trading name in cac:PartyName is another term and is not returned as name: it comes under PARSE_NOT_RETURNED. On CII ram:Name |
vatId |
string | optional | BT-31 or BT-48. On UBL the cbc:CompanyID of the party tax scheme whose scheme is VAT; on CII the tax registration with scheme VA |
taxId |
string | optional | BT-32. CII only: the tax registration with scheme FC. On UBL it is not returned |
address.street |
string | optional | BT-35 or BT-50 |
address.additionalStreet |
string | optional | BT-36 or BT-51. CII only. On UBL it is not returned |
address.city |
string | optional | BT-37 or BT-52 |
address.postalCode |
string | optional | BT-38 or BT-53 |
address.countryCode |
string | optional | BT-40 or BT-55 |
address is left out when the postal address states none of its values.
Parsed invoice line object
| Field | Type | Always present | Description |
|---|---|---|---|
description |
string | optional | BT-153. Item name |
quantity |
number | optional | BT-129. Invoiced quantity, or the credited quantity on a UBL credit note |
unitCode |
string | optional | BT-130. Unit of measure code |
unitPrice |
number | optional | BT-146. Item net price |
lineTotal |
number | optional | BT-131. Line net amount |
vatRate |
number | optional | BT-152. Line VAT rate |
vatCategoryCode |
string | optional | BT-151. Line VAT category code |
A line that states none of these values is an empty object, so a line’s position in lines is its position in the document.
Numbers and dates
A number is returned when the element’s text is a decimal number as XML Schema writes one (xs:decimal): digits, with an optional sign and an optional fraction after a point, such as 1200, -5.50 or .5. Text in any other form, such as 1e3 or 1,200.00, is not read as a number. The response carries the number as a floating-point number. A decimal with more digits than that holds comes back as the nearest number it can hold.
A date is read by the form the document’s syntax prescribes:
- UBL: the text must be an
xs:date, a calendar date writtenYYYY-MM-DD. It is returned as the document writes it, with its time zone when it has one (2026-04-13+02:00). A day the month does not have, such as2026-02-30, is not a date. - CII: the
udt:DateTimeStringmust carryformat="102"and holdYYYYMMDD, and it is returned asYYYY-MM-DD. Text under anotherformat, or under none, is not read as a date. The day is not held to the month: CEN’s rule for this form,CII-DT-097, admits days01to31in every month, so20260230comes back as2026-02-30.
Text that is not read as a number or a date leaves the field out, and a PARSE_VALUE_NOT_FOUND warning names the field and the element. So does a decimal too large for a floating-point number.
Warnings
Each entry of warnings has a code and a message, and the fields its code calls for.
| Field | Type | Description |
|---|---|---|
code |
string | PARSE_NOT_RETURNED or PARSE_VALUE_NOT_FOUND |
message |
string | The warning in words |
field |
string? | PARSE_VALUE_NOT_FOUND: the invoice field the warning is about, as a path from invoice, such as totalGrossAmount or delivery.date. A field of an invoice line is written lines[].unitPrice, with no index |
terms |
string[]? | EN 16931 business term or group ids the warning is about |
elements |
object[]? | The elements of the document the warning is about, each as path and count. PARSE_NOT_RETURNED: those the response does not return. PARSE_VALUE_NOT_FOUND: those whose text was not read as a value |
PARSE_NOT_RETURNED
Lists every element of the document that holds text or an attribute and that the parser did not read. An element whose text was read and refused as a number or a date is not listed here: it comes under PARSE_VALUE_NOT_FOUND. An element is listed once, at the top of the part that was not read: payment means with five children are one entry. path is written with the usual prefixes (/Invoice/cac:PaymentMeans, /rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/...), and count is how often that path occurs. An element in a namespace outside UBL and CII is written with its namespace in braces. An attribute of an element that was read, such as a schemeID or a currencyID, is not listed.
terms names the business groups below when one is among the listed elements. Other terms are not named: find them by path.
| Group | Name | UBL element | CII element |
|---|---|---|---|
BG-3 |
Preceding invoice reference | cac:BillingReference |
ram:InvoiceReferencedDocument |
BG-16 |
Payment instructions | cac:PaymentMeans |
ram:SpecifiedTradeSettlementPaymentMeans |
BG-20 |
Document level allowances | cac:AllowanceCharge with cbc:ChargeIndicator false or 0 |
ram:SpecifiedTradeAllowanceCharge in the header settlement, indicator false |
BG-21 |
Document level charges | the same element, indicator true or 1 |
the same element, indicator true |
BG-23 |
VAT breakdown | cac:TaxTotal/cac:TaxSubtotal |
ram:ApplicableTradeTax in the header settlement |
BG-27 |
Invoice line allowances | cac:AllowanceCharge on a line, indicator false or 0 |
ram:SpecifiedTradeAllowanceCharge in the line settlement, indicator false |
BG-28 |
Invoice line charges | the same element, indicator true or 1 |
the same element, indicator true |
BG-32 |
Item attributes | cac:AdditionalItemProperty |
ram:ApplicableProductCharacteristic |
Each group is found the way the rule context of that name selects it in CEN’s EN 16931 validation artefacts, the ones POST /v1/validate runs. An allowance or charge whose indicator is none of the values above is listed by its path and named as neither.
PARSE_VALUE_NOT_FOUND
The document holds something where field is read from, and it is not one value the response can return. field is then left out of invoice. The warning has two shapes:
- With
elementsand noterms: an element holds text that was not read as a number or a date.elementslists each such element bypath, andcountis the number of elements at that path whose text was not read. The text itself is not repeated. There is one warning perfieldand reason, not one per occurrence. For a field of an invoice line,fieldis written without an index (lines[].unitPrice), so the warning covers every line whose value was not read and does not say which lines they are. - With
terms["BT-110"]and noelements: the document holds several total VAT amounts in its document currency, and the parser does not choose among them.fieldistotalTaxAmount. EN 16931 ruleBR-CO-15requires exactly one such amount on UBL, and on CII unless the total with VAT (BT-112) equals the total without VAT (BT-109).
A value the document does not state draws no PARSE_VALUE_NOT_FOUND: an element that is missing, is empty or holds only white space leaves its field out.
Parsed delivery object
The deliver-to party, place and date, read from cac:Delivery on UBL and from
ram:ApplicableHeaderTradeDelivery on CII. The field names are those of the
generate delivery object. A field is returned only
when the document has a value for it.
| Field | Type | Always present | Description |
|---|---|---|---|
name |
string | optional | BT-70. Deliver-to party name: cac:DeliveryParty/cac:PartyName/cbc:Name on UBL, ram:ShipToTradeParty/ram:Name on CII |
locationId |
object | optional | BT-71, as id, with schemeId (BT-71-1) when the document carries a scheme. Read from cac:DeliveryLocation/cbc:ID on UBL. On CII it is ram:ShipToTradeParty/ram:GlobalID with its scheme, or the unqualified ram:ID, which has none |
date |
string | optional | BT-72. Actual delivery date, read as a date: cbc:ActualDeliveryDate on UBL, ram:ActualDeliverySupplyChainEvent on CII |
address |
DeliveryAddress | optional | BG-15. Deliver-to address. Present only when the document’s address carries at least one value |
Parsed delivery address object
| Field | Type | Always present | Description |
|---|---|---|---|
street |
string | optional | BT-75. Deliver-to street and house number |
additionalStreet |
string | optional | BT-76. Additional deliver-to address line |
city |
string | optional | BT-77. Deliver-to city |
postalCode |
string | optional | BT-78. Deliver-to postal/ZIP code |
countrySubentity |
string | optional | BT-79. Deliver-to country subdivision: cbc:CountrySubentity on UBL, ram:CountrySubDivisionName on CII |
countryCode |
string | optional | BT-80. Deliver-to country code. Left out when the address states no country. EN 16931 rule BR-57 says each deliver-to address shall contain one |
One value sent to POST /v1/generate does not come back from a CII document: a locationId.schemeId outside the ISO 6523 ICD list. Generate writes such an identifier to the unqualified ram:ID, because BR-CL-26 rejects a non-ICD scheme on ram:GlobalID, so the document holds no scheme to read. UBL keeps it.
Error responses
| HTTP Status | Error Code | When |
|---|---|---|
| 400 | VALIDATION_ERROR |
Request body is empty or not a valid file, the format query parameter is not one of its allowed values, or a query parameter is undeclared |
| 400 | PARSE_FAILED |
The input could not be parsed as a valid e-invoice (malformed XML, unsupported format, no embedded XML in PDF) |
| 413 | VALIDATION_ERROR |
Request body is larger than this endpoint’s 1 MiB size limit. The cap is enforced before the body is read, so nothing was validated and no quota unit is spent. A body within the limit that the engine refuses as too large to process is also a 413, and its message says so: that body was read, and a live key’s quota unit is refunded |
| 415 | VALIDATION_ERROR |
The Content-Type is not one this route reads. Send application/xml, text/xml, application/octet-stream or application/pdf. Two other types get no 415: a JSON object or array sent as application/json gets a 400, and text/plain is read as UTF-8 text, which works for a UTF-8 XML document but not for a PDF. The free public demo is narrower: XML only, and PDF bytes are refused even when labelled as XML |
| 503 | ENGINE_UNAVAILABLE |
The engine is temporarily unavailable |
| 503 | DATABASE_UNAVAILABLE |
Beliq’s database could not serve the request. Retry after Retry-After. Quota refunds says whether a spent unit comes back |
400 PARSE_FAILED example
{
"success": false,
"error": {
"code": "PARSE_FAILED",
"message": "Could not parse XML: element 'Invoice' not found in expected namespace"
}
}Notes
- The parsed JSON uses the field names of the generate endpoint input. It is a subset of the document, so a document generated from it is not the document that was parsed: everything
warningslists is missing from it. - A field the document states no value for is left out of the response. It is not set to
null,0or an empty string: see what an absent field means. - For hybrid PDFs, Beliq reads the embedded XML attachment automatically.