All posts
·7 min read

How to Automatically Extract Invoice Data to JSON or CSV with a Single API Call

A hands-on guide to extracting PDF data to JSON with an AI invoice parser API — one curl request to upload a document and get clean, schema-shaped data back, including scanned PDFs. With Python and Node.js examples.

If your backend receives invoices as PDFs — or worse, as scanned images — you've probably lived some version of this pipeline: run the file through a text extractor, get back a soup of unstructured strings, and then write an ever-growing pile of regexes to fish out the invoice number, the date, and the total. It works for one vendor. Then vendor number two arrives with a different layout, and the regexes multiply.

This guide walks through the alternative: sending the document to an AI invoice parser API and getting clean, schema-shaped JSON back from a single API call — upload and extraction in one request, scanned PDFs and photos included.

The problem with parsing PDF text yourself

A PDF is a drawing format, not a data format. When you extract raw text from an invoice, you typically get something like this:

ACME INDUSTRIAL SUPPLY CO.   INVOICE
INV-2481  Date: 06/30/2026     Terms Net 30
M8 hex bolts (box of 500) 4 62.50 250.00
Angle grinder disc 115mm 20 3.10 62.00
                    SUBTOTAL 312.00
      TAX 8.25%  25.74     TOTAL   337.74

Column boundaries are gone, label/value pairs run together, and the visual structure a human relies on has been flattened away. Every heuristic you write against this text is coupled to one vendor's layout — and if the "PDF" is actually a scan, there is no text layer at all until you bolt on an OCR step. The fix is to stop parsing text and ask for structured data directly.

Step 1: Define your fields once

Create a free ParseDirect account (50 pages/month, no card required) and create a template — the list of fields you want back, described in plain language. For invoices, something like: invoice_number (string), vendor_name (string), invoice_date (date), total_amount (number), and line_items — a table field with description, quantity, and unit_price columns.

This template is nota zone map tied to one layout. It works for every invoice from every vendor, because the AI reads the document semantically rather than looking at coordinates. Then grab an API key from Settings — you'll get a pd_... token, shown once.

If you ever need your template's ID programmatically, list your templates:

curl https://parsedirect.com/api/v1/templates \
  -H "Authorization: Bearer pd_your_api_key"

Step 2: One API call — upload and extract together

POST the file as multipart/form-data and pass your template_id in the same request. ParseDirect uploads the document, runs the extraction, and returns the structured result in one response:

curl https://parsedirect.com/api/v1/documents \
  -H "Authorization: Bearer pd_your_api_key" \
  -F "file=@invoice-2481.pdf" \
  -F "template_id=your_template_id"

The response is JSON that's ready to use as-is:

{
  "document": {
    "id": "9b2e6c1a-...",
    "filename": "invoice-2481.pdf",
    "status": "completed"
  },
  "extraction": {
    "status": "success",
    "page_count": 2,
    "extracted_data": {
      "invoice_number": "INV-2481",
      "vendor_name": "Acme Industrial Supply Co.",
      "invoice_date": "2026-06-30",
      "total_amount": 337.74,
      "line_items": [
        {
          "description": "M8 hex bolts (box of 500)",
          "quantity": 4,
          "unit_price": 62.50
        },
        {
          "description": "Angle grinder disc 115mm",
          "quantity": 20,
          "unit_price": 3.10
        }
      ]
    }
  }
}

Notice what you didn'thave to do: no OCR configuration, no regexes, no layout templates, no cleanup pass. The output is constrained to your template's schema — the fields you defined, with the types you defined. total_amountcomes back as an actual number, not the string "$337.74", and line_items is a real array you can iterate over.

The same call in Python and Node.js

Python

import requests

resp = requests.post(
    "https://parsedirect.com/api/v1/documents",
    headers={"Authorization": "Bearer pd_your_api_key"},
    files={"file": open("invoice-2481.pdf", "rb")},
    data={"template_id": "your_template_id"},
)
resp.raise_for_status()

data = resp.json()["extraction"]["extracted_data"]
print(data["invoice_number"], data["total_amount"])
for item in data["line_items"]:
    print(f'- {item["description"]}: {item["quantity"]} x {item["unit_price"]}')

Node.js

import { readFile } from "node:fs/promises";

const form = new FormData();
form.append(
  "file",
  new Blob([await readFile("invoice-2481.pdf")], { type: "application/pdf" }),
  "invoice-2481.pdf",
);
form.append("template_id", "your_template_id");

const resp = await fetch("https://parsedirect.com/api/v1/documents", {
  method: "POST",
  headers: { Authorization: "Bearer pd_your_api_key" },
  body: form,
});

const { extraction } = await resp.json();
console.log(extraction.extracted_data);

Scanned PDFs and photos: the exact same call

This is where most pipelines fall over. A scanned invoice has no text layer, so text extractors return nothing, and legacy tools route you through a separate OCR engine. With ParseDirect there is no separate path: the model reads documents visually, so a scanned PDF, a JPEG, a PNG, or a phone photo of a receipt goes through the same endpoint with the same template — just change the filename in the -F "file=@..." line. A scan costs the same as a born-digital PDF: one page is one page.

Getting CSV instead of JSON

The API speaks JSON, and JSON is the right format for a backend. If a teammate needs a spreadsheet, every extraction can be exported as CSV from the dashboard with one click — or convert in a few lines of code, since the structure is already flat and predictable:

import csv, sys

writer = csv.DictWriter(sys.stdout, fieldnames=["description", "quantity", "unit_price"])
writer.writeheader()
writer.writerows(data["line_items"])

Production notes

The full endpoint reference lives in the API docs.

Try it with your own invoices

The whole setup — account, template, API key, first successful extraction — takes about five minutes. Sign up free, define your invoice fields, and run the curl command above against a real document. The first 50 pages every month are free, so you can evaluate it on your actual documents rather than a demo.