DEV Community

Cover image for Tables Inside PDFs, Extracted as JSON, Not a Screenshot
PDF4me
PDF4me

Posted on

Tables Inside PDFs, Extracted as JSON, Not a Screenshot

A PDF doesn't know it contains a table. Open one at the byte level and a table is just text runs positioned at specific coordinates, with maybe a few vector lines drawn underneath to make it look gridded. There's no <table> tag, no row object, no column index. The table you see is an illusion your eyes assemble from where the ink happens to sit on the page. Your code doesn't get that luxury.

This is why "just extract the table" turns into hours of manual cleanup on any team that deals with PDFs regularly. Finance teams copy-paste line items out of vendor statements into Excel by hand. Analysts screenshot a table from a research report and re-type it because pasting brings over three columns as one smeared line of text. Someone eventually reaches for generic OCR, which reads the text fine but has no idea that "42,500" belongs in the Q3 column of the third row rather than just being a number that happened to appear on the page.

None of this is a data problem. It's a structure problem. The numbers were always readable. What was missing was the row and column relationship between them.

What "extracting a table" actually has to solve

To turn a visual table back into structured data, something has to reconstruct three things a PDF never stored explicitly: which text belongs to which row, which text belongs to which column, and where one table ends and body text or a caption begins. Do that badly and you get exactly the mess described above, values with no relationship to each other, or worse, silently misaligned so a cell from row 4 ends up looking like it belongs to row 5.

This is the specific job of Extract Table from PDF, a REST endpoint built for pulling tabular data out of PDFs and returning it as structured JSON or CSV rather than a wall of positioned text.

Calling it directly

The endpoint is a single POST to /api/v2/ExtractTableFromPdf on https://api.pdf4me.com/. Authentication uses your API key from the PDF4me Dashboard, Base64-encoded and sent as a Basic auth header, same as every other PDF4me REST call. See Connect to the PDF4me API if you haven't wired that up yet.

The payload takes two required fields and one optional one:

{
  "docName": "output.pdf",
  "docContent": "JVBERi0xLjQK...",
  "async": true
}
Enter fullscreen mode Exit fullscreen mode

docName is the source filename (with a .pdf extension), docContent is the PDF's bytes Base64-encoded, and async is a boolean that, when true, has the API return 202 Accepted with a Location header you poll instead of blocking on a synchronous response. For anything beyond a trivial single-page table, async is the one you want.

Here's the request and polling logic, adapted from the official Python sample in PDF4me's sample repo:

import base64, requests, time

api_key = "your-api-key-here"
url = "https://api.pdf4me.com/api/v2/ExtractTableFromPdf"

with open("sample.pdf", "rb") as f:
    pdf_base64 = base64.b64encode(f.read()).decode("utf-8")

payload = {
    "docName": "output.pdf",
    "docContent": pdf_base64,
    "async": True
}
headers = {
    "Authorization": f"Basic {api_key}",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers, timeout=300)

if response.status_code == 202:
    location_url = response.headers["Location"]
    for _ in range(15):
        time.sleep(12)
        poll = requests.get(location_url, headers=headers, timeout=60)
        if poll.status_code == 200:
            table_data = poll.json()
            break
        elif poll.status_code != 202:
            raise RuntimeError(f"Extraction failed: {poll.status_code}")
elif response.status_code == 200:
    table_data = response.json()
Enter fullscreen mode Exit fullscreen mode

What comes back, and one honest caveat

A successful response returns extracted table data as JSON: an array of tables, each carrying its rows, columns, and cell data. Worth knowing before you build around it: the documented example response on the endpoint's own docs page isn't fully self-consistent (it shows a rows key twice on the same object, once as the row array and once as a row count), and the official Python sample code defensively handles more than one possible shape for the response, a dict with a tables key, a bare list, a table object with a rows array, or a table that's itself just a list of rows. That's not a knock on the endpoint, extraction responses for real-world documents are genuinely variable, but it does mean your integration code should check what actually came back rather than assuming one fixed schema, the way the sample script itself does with its isinstance checks.

It's also not documented, from the endpoint page alone, whether scanned (image-only) PDFs need to go through OCR first before table structure can be recognized, versus tables in natively-generated PDFs being read directly. If your source PDFs are scans rather than digitally created documents, that's worth confirming directly before you build a pipeline around it.

The same capability across the no-code platforms

For teams building in Power Automate, Extract Table from PDF exposes the same table recognition as a flow step, parsing tabular data with page numbers, rows, and columns so it can feed straight into Dataverse, Excel Online, or SharePoint without a developer in the loop.

Zapier has its own version, Extract Table From PDF in Zapier, which returns structured row, column, and JSON data for every table found in a document, built for financial report parsing, invoice line items, and research data extraction.

And in n8n, Extract Table From PDF puts the same node into a workflow canvas, so table data extracted from an incoming PDF can be routed straight into whatever node comes next, a database write, a Slack alert, another API call.

Worth saying plainly: there's no Extract Table module in Make (formerly Integromat) as of this writing. Make covers document classification, document parsing, form data extraction, text extraction, page extraction, and a few other extraction operations, but table extraction specifically isn't one of them yet. If your stack is Make-first, this capability currently means calling the REST endpoint directly from an HTTP module, or waiting on that gap to close.

If your end goal is actually a spreadsheet file rather than JSON in your own pipeline, PDF4me's PDF-to-Excel conversion is a different tool for a related job, a full XLSX file versus structured row/column data your code consumes directly.

Try it before you write a line of code

If you want to see the structured output before committing to a build, the API Tester for Extract Table from PDF lets you upload a real PDF and see the endpoint's response directly in the browser. It's the fastest way to check whether a specific document's tables come back clean before you wire up authentication and error handling around it.

The point of all this

Tables inside PDFs aren't broken data, they're just data stored in a format nothing downstream can query. The fix isn't a smarter screenshot or a more patient intern, it's an endpoint that already knows the difference between a row and a column and hands that structure back as something your code can actually use. Whether that call comes from your own backend, a Power Automate flow, a Zap, or an n8n workflow, the shape of the output is the same: JSON or CSV, organized by page, row, and column, ready for the next step instead of another round of manual cleanup.

Website: pdf4me.com
Documentation: docs.pdf4me.com
Developer portal: dev.pdf4me.com

Top comments (0)