> ## Documentation Index
> Fetch the complete documentation index at: https://docs.spidra.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Turn a folder of PDFs into JSON

> Extract the same fields from every PDF in a folder (invoices, reports, contracts) into JSON lines, with the cost checked before you parse.

You have a folder of documents with the same kind of information in each: invoices, statements, reports. This guide uses [Parse](https://docs.spidra.io/parse.md) to pull the same fields out of every file into a JSON lines file, checking the cost first for free.

You need `pip install spidra` and your API key in `SPIDRA_API_KEY`. The example folder holds three invoices from different vendors, each laid out differently.

## 1. Describe the fields

A `description` on a field tells the AI how to write the value, which keeps dates and currencies consistent across vendors:

```python theme={null}
schema = {
    "type": "object",
    "properties": {
        "vendor": {"type": "string"},
        "invoice_number": {"type": "string"},
        "invoice_date": {"type": ["string", "null"], "description": "As YYYY-MM-DD"},
        "due_date": {"type": ["string", "null"], "description": "As YYYY-MM-DD"},
        "total": {"type": "number"},
        "currency": {"type": "string", "description": "Three-letter code, such as EUR"},
    },
}
```

## 2. Check the cost, then parse

`estimate_parse()` reads each file's page count without charging anything. `parse()` then reads the file and fills the schema:

```python theme={null}
# pdfs_to_json.py
import json
import os
from pathlib import Path

from spidra import Spidra

client = Spidra(api_key=os.environ["SPIDRA_API_KEY"])

schema = {
    "type": "object",
    "properties": {
        "vendor": {"type": "string"},
        "invoice_number": {"type": "string"},
        "invoice_date": {"type": ["string", "null"], "description": "As YYYY-MM-DD"},
        "due_date": {"type": ["string", "null"], "description": "As YYYY-MM-DD"},
        "total": {"type": "number"},
        "currency": {"type": "string", "description": "Three-letter code, such as EUR"},
    },
}

files = sorted(Path("invoices").glob("*.pdf"))

# 1. Check the cost first (free)
pages = sum(client.estimate_parse(str(f)).total_pages for f in files)
print(f"{len(files)} files, {pages} pages")

# 2. Parse each file and keep the fields
with open("invoices.jsonl", "w") as out:
    for f in files:
        doc = client.parse(str(f), prompt="The invoice details", schema=schema)
        out.write(json.dumps({"file": f.name, **doc.json}) + "\n")
        print(f.name, doc.json)
```

```text theme={null}
3 files, 3 pages
acme-1042.pdf {'vendor': 'ACME Supplies Ltd', 'invoice_number': 'INV-1042', 'invoice_date': '2026-10-01', 'due_date': '2026-10-31', 'total': 329.5, 'currency': 'EUR'}
globex-0093.pdf {'vendor': 'GLOBEX CORPORATION', 'invoice_number': '0093', 'invoice_date': '2026-05-10', 'due_date': '2026-05-24', 'total': 720, 'currency': 'GBP'}
northwind-7781.pdf {'vendor': 'Northwind Traders', 'invoice_number': '7781', 'invoice_date': '2026-10-03', 'due_date': '2026-11-02', 'total': 87, 'currency': 'USD'}
```

Three vendors, three layouts, one shape. Northwind wrote its date as "3 October 2026" and its total as "Amount due USD 87.00"; both came back in your format. Each file cost 2 credits: 1 for the page and 1 for the AI.

Look at Globex: its invoice said "Invoice date: 10/05/2026" and "Pay within 14 days". The AI read the date as 10 May and worked out the due date from the payment terms. If your vendors use US-style dates, say so in the prompt ("dates are month/day/year"), because a date like 10/05 can't be told apart from the page alone.

## Larger folders

* **Many files:** [`parse_batch()`](https://docs.spidra.io/api-reference/parse/parse-batch) takes up to 50 documents in one request, and one that fails doesn't stop the others.
* **Big files:** uploads go up to 50 MB; files up to 500 MB use `upload_for_parse()` first.
* **Only some pages:** pass `pages="1-2"` to parse just the first two pages of each file, and pay for those.
* **Scanned documents** are read with AI automatically. They take longer, and quality depends on the scan.

## If something goes wrong

* **A file failed.** It wasn't charged. Password-protected and damaged files fail with a plain error such as "This PDF is password protected".
* **A field is `null`.** The document doesn't say it, or says it in a way the prompt didn't cover. Add a hint to the field's `description`.
* **Totals come back as text.** Make sure the field's type is `number` in the schema.

Files you upload are kept for 24 hours so you can parse them again with a different schema, then deleted. Delete one straight away with `client.delete_parse_file(file_id)`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.