Purchase Order Parser API

v1.0

Extract every field from any purchase order document — with server-side arithmetic validation built in.

Overview

The Purchase Order Parser API turns any PO document into clean, structured JSON with a single API call. It extracts vendor, buyer, ship-to, full line items, and all financial totals — then validates the arithmetic server-side and flags any discrepancies before returning the response.

🤖 AI-Powered

Works with any PO layout, template, or vendor format without configuration

✅ Math Validated

Line-item arithmetic and grand total reconciliation checked server-side on every request

📸 OCR Included

Handles scanned PDFs and image-based POs automatically on the MEGA plan

💡 Perfect For

  • • Procurement automation and ERP integration (SAP, Oracle, NetSuite)
  • • Accounts payable platforms cross-referencing POs against supplier invoices
  • • Supply chain and order management systems
  • • Supplier portals accepting buyer PO uploads
  • • Finance teams flagging pricing discrepancies before payment

Quick Start

Base URL

https://api.cparse.com/po/v1

Authentication

http
X-API-Key: YOUR_API_KEY

Get your API key from the dashboard. New accounts include free credit — no credit card required.

Quick Example

cURL

bash
curl --request POST \
  --url https://api.cparse.com/po/v1/parse \
  --header 'X-API-Key: YOUR_API_KEY' \
  --form 'file=@purchase_order.pdf'

Endpoints

GET/fields

List all fields extracted by the PO parser. No authentication required.

Response
json
{
  "fields": [
    { "name": "po_number", "description": "Purchase order number / reference" },
    { "name": "issue_date", "description": "PO issue date (YYYY-MM-DD)" },
    { "name": "delivery_date", "description": "Requested delivery date (YYYY-MM-DD)" },
    { "name": "currency", "description": "ISO 4217 currency code (e.g. USD, EUR)" },
    { "name": "vendor.name", "description": "Vendor (supplier) name" },
    ...
  ],
  "line_item_fields": [
    { "name": "line_number", "description": "Sequence number on the document" },
    { "name": "sku", "description": "Item / product code or SKU" },
    { "name": "description", "description": "Text description of the item" },
    ...
  ]
}
GET/health

Health check endpoint. Returns service status.

Response
json
{ "status": "ok", "service": "po-parser", "version": "0.1.0" }
POST/parse

Extract structured data from a purchase order document.

Request
  • Content-Type: multipart/form-data
  • file (required): PDF, DOCX, JPEG, PNG, ZIP, or 7z containing the PO document
Response
json
[
  {
    "success": true,
    "file_name": "purchase_order.pdf",
    "pages_processed": 1,
    "ocr_used": false,
    "data": {
      "po_number": "PO-2026-00187",
      "issue_date": "2026-03-28",
      "delivery_date": "2026-04-18",
      "currency": "USD",
      "vendor": {
        "name": "Global Parts Supply Co.",
        "address": "1200 Industrial Pkwy, Houston, TX 77001",
        "email": "orders@globalparts.com",
        "phone": "+1-713-555-0182",
        "tax_id": "EIN 47-1234567"
      },
      "buyer": {
        "name": "Acme Manufacturing Inc.",
        "address": "500 Factory Rd, Detroit, MI 48201",
        "email": "procurement@acme.com",
        "phone": "+1-313-555-0093"
      },
      "ship_to": {
        "name": "Acme Detroit Warehouse",
        "address": "700 Warehouse Blvd, Detroit, MI 48202"
      },
      "line_items": [
        {
          "line_number": 1,
          "sku": "BLT-M8-100",
          "description": "M8 Hex Bolts, Grade 8.8, Box of 100",
          "quantity": 50,
          "unit": "box",
          "unit_price": 12.50,
          "line_total": 625.00,
          "tax_rate": null
        }
      ],
      "subtotal": 1655.00,
      "discount_amount": null,
      "tax_amount": 132.40,
      "shipping_cost": 45.00,
      "grand_total": 1832.40,
      "payment_terms": "Net 30",
      "incoterms": null,
      "notes": "Please include packing list with shipment.",
      "validation": {
        "total_mismatch": false,
        "line_arithmetic_errors": []
      }
    }
  }
]

Response Fields

Document Fields

FieldTypeDescription
po_numberstring | nullPurchase order number or reference
issue_datestring | nullPO issue date (YYYY-MM-DD)
delivery_datestring | nullRequested delivery date (YYYY-MM-DD)
currencystring | nullISO 4217 currency code (e.g. USD, EUR)
vendorobjectSupplier receiving the order — see Vendor fields below
buyerobjectCompany issuing the order — see Buyer fields below
ship_toobjectDelivery destination when different from buyer
line_itemsarrayOrdered line items — see Line Item fields below
subtotalnumber | nullPre-tax, pre-shipping subtotal
discount_amountnumber | nullDocument-level discount as a flat amount
tax_amountnumber | nullTotal tax amount
shipping_costnumber | nullFreight or delivery charge if itemised
grand_totalnumber | nullFinal amount due
payment_termsstring | nullPayment terms as written (e.g. "Net 30")
incotermsstring | nullIncoterms if stated (e.g. "FOB", "CIF")
notesstring | nullFree-text notes or special instructions
validationobjectServer-side computed validation results

Vendor / Buyer / Ship-To Fields

FieldDescription
nameCompany or individual name
addressFull address as a single string
emailEmail address (vendor and buyer only)
phonePhone number (vendor and buyer only)
tax_idVAT, EIN, ABN, or similar tax identifier (vendor only)

Line Item Fields

FieldTypeDescription
line_numbernumber | nullSequence number on the document
skustring | nullItem or product code / SKU
descriptionstring | nullText description of the item
quantitynumber | nullQuantity ordered
unitstring | nullUnit of measure — null if not stated on the document
unit_pricenumber | nullPrice per single unit
line_totalnumber | nullLine total as stated on the document
tax_ratenumber | nullPer-line tax rate as a percentage (e.g. 10 for 10%)

Validation Fields

The validation object is computed server-side after extraction — never by the LLM. Use it to decide whether to accept the data automatically or flag it for manual review.

FieldTypeDescription
total_mismatchbooleantrue when grand_total does not equal subtotal + tax + shipping - discount
line_arithmetic_errorsarrayLines where quantity * unit_price does not match the stated line_total
line_arithmetic_errors[].line_numbernumber | nullLine number from the document
line_arithmetic_errors[].expected_totalnumberComputed: quantity * unit_price
line_arithmetic_errors[].stated_totalnumberline_total as extracted from the document
line_arithmetic_errors[].deltanumberstated_total minus expected_total (negative = overstated on document)

Plans

FeatureBASICPROULTRAMEGA
Max file size5 MB10 MB20 MB20 MB
Max pages10255050
OCR (images / scanned PDFs)
Max OCR pages---3

API-key users authenticated via X-API-Key always receive MEGA plan limits. Balance is the only constraint.

Error Handling

StatusMeaningFix
400No file / empty file / corrupted archiveCheck the uploaded file
401Missing or invalid API keyPass a valid X-API-Key header
402Insufficient balanceTop up at cparse.com/dashboard
413File too large or too many pages for your planUpgrade plan or reduce file
415Unsupported file type or OCR not enabled on planUse PDF/DOCX or upgrade to MEGA
500Internal server errorRetry or contact support

Code Examples

Python

python
import requests

url = "https://api.cparse.com/po/v1/parse"
headers = {"X-API-Key": "YOUR_API_KEY"}

with open("purchase_order.pdf", "rb") as f:
    response = requests.post(url, files={"file": f}, headers=headers)

result = response.json()[0]
data = result["data"]

print(data["po_number"])           # "PO-2026-00187"
print(data["vendor"]["name"])      # "Global Parts Supply Co."
print(data["grand_total"])         # 1832.40

# Check validation
if data["validation"]["total_mismatch"]:
    print("Warning: grand total does not reconcile")

for err in data["validation"]["line_arithmetic_errors"]:
    print(
        f"Line {err['line_number']}: "
        f"expected {err['expected_total']}, "
        f"stated {err['stated_total']} "
        f"(delta {err['delta']})"
    )

# Print line items
for item in data.get("line_items", []):
    print(f"  {item['sku']}  {item['description']}  qty={item['quantity']}  total={item['line_total']}")

JavaScript (Node.js)

javascript
import FormData from 'form-data';
import fs from 'fs';
import axios from 'axios';

const form = new FormData();
form.append('file', fs.createReadStream('purchase_order.pdf'));

const response = await axios.post('https://api.cparse.com/po/v1/parse', form, {
  headers: {
    ...form.getHeaders(),
    'X-API-Key': 'YOUR_API_KEY',
  },
});

const [result] = response.data;
const { data } = result;

console.log(data.po_number);        // "PO-2026-00187"
console.log(data.vendor.name);      // "Global Parts Supply Co."
console.log(data.grand_total);      // 1832.40

if (data.validation.total_mismatch) {
  console.warn('Grand total does not reconcile');
}

data.validation.line_arithmetic_errors.forEach((err) => {
  console.warn(
    `Line ${err.line_number}: expected ${err.expected_total}, stated ${err.stated_total} (delta ${err.delta})`
  );
});