New: PDF to E-Invoice for Factur-X/ZUGFeRD, and PDF to PDF/A for archive-ready PDF/A-3b.

Convert an Uploaded PDF to JSON in Python

This example uploads a local PDF, converts it with PDF.co’s PDF to JSON API, and saves the generated JSON file.

Python runs the requests. The input can be an ordinary document or a scanned PDF. The output describes extracted document content and structure; it is not executable code or an invoice schema with automatically named business fields.

Step 1: Prepare Python and Your PDF

Use Python 3 and install Requests:

python -m pip install requests

Get your API key from the PDF.co dashboard, then set the PDFCO_API_KEY environment variable in the terminal where you will run the program:

  • macOS/Linux: export PDFCO_API_KEY="YOUR_API_KEY"
  • Windows PowerShell: $env:PDFCO_API_KEY="YOUR_API_KEY"

Replace YOUR_API_KEY with your actual key. Start with a small PDF for this synchronous example.

Step 2: Upload, Convert, and Save

Save this complete program as pdf_to_json.py:

import argparse
import json
import os
from pathlib import Path

import requests


def api_json(response):
    response.raise_for_status()
    data = response.json()
    if data.get("error"):
        raise RuntimeError(data.get("message", "PDF.co reported an error"))
    return data


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("source", type=Path)
    parser.add_argument("destination", type=Path)
    args = parser.parse_args()

    if not args.source.is_file():
        raise ValueError("Source PDF does not exist")
    if args.destination.exists():
        raise FileExistsError("Choose a new destination filename")

    headers = {"x-api-key": os.environ["PDFCO_API_KEY"]}

    upload = api_json(requests.get(
        "https://api.pdf.co/v1/file/upload/get-presigned-url",
        headers=headers,
        params={
            "name": args.source.name,
            "contenttype": "application/octet-stream",
        },
        timeout=60,
    ))

    with args.source.open("rb") as source:
        response = requests.put(
            upload["presignedUrl"],
            data=source,
            headers={"Content-Type": "application/octet-stream"},
            timeout=120,
        )
        response.raise_for_status()

    result = api_json(requests.post(
        "https://api.pdf.co/v1/pdf/convert/to/json2",
        headers=headers,
        json={
            "url": upload["url"],
            "pages": "",
            "name": args.destination.name,
            "inline": False,
            "async": False,
        },
        timeout=120,
    ))

    download = requests.get(result["url"], timeout=120)
    download.raise_for_status()
    converted = download.json()

    with args.destination.open("x", encoding="utf-8") as output:
        json.dump(converted, output, ensure_ascii=False, indent=2)
        output.write("\n")

    print(f"Saved {args.destination}")


if __name__ == "__main__":
    main()

Run it with your local filenames:

python pdf_to_json.py input.pdf output.json

The program refuses to overwrite an existing destination file.

The presigned upload route returns two different URLs:

  • presignedUrl: Receives the PDF’s bytes through a PUT request.
  • url: Identifies the uploaded PDF for the conversion request.

The upload and result-download requests do not include the API-key header.

Step 3: Review the Generated JSON

The conversion response contains a temporary url pointing to the generated JSON file. The program downloads that file and saves it as output.json.

This document is separate from response metadata such as error, credits, and jobId.

With the default settings, inspect:

  • document: The converted document and its details.
  • document.pageCount: The number of pages.
  • document.page: Page details, represented as an object for one page or an array for multiple pages.

Compare the extracted text, reading order, and table groupings with the source PDF. Review scanned text carefully, especially numbers and punctuation. Do not execute extracted document text as code.

The empty pages value processes all pages. To limit the conversion, change it to "0" for the first page or "0-2" for the first three pages.

Longer Conversions

For longer documents, adapt the program to use async: true and Background Job Check. Retain the returned job ID and wait for success before downloading the generated JSON.

The example stops on HTTP, JSON-decoding, API-reported, and local-file errors. A local timeout does not prove that server processing failed; if you have a job ID, check its status before resubmitting.

Download successful results before their temporary links expire, and keep the source PDF for comparison.

Related Tutorials

See Related Tutorials