# PDF Table Extractor (`practical_ophthalmologist_iuq/pdf-table-extractor`) Actor

Extract tables from PDF files into clean JSON, CSV, Markdown and Excel-ready rows. Column structure and empty cells are preserved, so every value stays in the column it came from. Works on financial reports, research papers, government statistics and invoices.

- **URL**: https://apify.com/practical\_ophthalmologist\_iuq/pdf-table-extractor.md
- **Developed by:** [Scrappeer](https://apify.com/practical_ophthalmologist_iuq) (community)
- **Categories:** AI, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $30.00 / 1,000 table extracteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## PDF Table Extractor — get tables out of PDFs without losing the columns

Extract **tables** from PDF files into clean JSON, CSV, Markdown, and Excel-ready rows.
Column structure and empty cells are preserved, so every number stays in the column it
came from.

Built for financial reports, scientific papers, government statistics, invoices,
tariff schedules, and any other PDF where the value you need lives inside a table.

***

### The problem this solves

Every generic "PDF to text" tool flattens a table into one stream of words.
Take this table from a research paper:

| Model | BLEU EN-DE | BLEU EN-FR | Cost EN-DE | Cost EN-FR |
|---|---|---|---|---|
| ByteNet | 23.75 | | | |
| Deep-Att + PosUnk | | 39.2 | | 1.0e20 |
| GNMT + RL | 24.6 | 39.92 | 2.3e19 | 1.4e20 |

A text extractor gives you this:

```
Model BLEU Training Cost EN-DE EN-FR EN-DE EN-FR ByteNet 23.75
Deep-Att + PosUnk 39.2 1.0e20 GNMT + RL 24.6 39.92 2.3e19 1.4e20
```

Is `23.75` an EN-DE score or an EN-FR score? **There is no way to tell.**
The empty cells vanished, and with them the alignment between values and columns.
Every downstream use — a spreadsheet, a database, an LLM prompt — inherits that error.

This Actor reads the table geometrically instead, so the output is:

```json
{
  "header": ["Model", "BLEU EN-DE", "BLEU EN-FR", "Cost EN-DE", "Cost EN-FR"],
  "rows": [
    ["ByteNet",           "23.75", null,    null,     null],
    ["Deep-Att + PosUnk", null,    "39.2",  null,     "1.0e20"],
    ["GNMT + RL",         "24.6",  "39.92", "2.3e19", "1.4e20"]
  ]
}
```

`null` means the cell was genuinely empty. Nothing is silently dropped.

***

### What you get

- **Empty cells preserved as `null`** — the single most common failure of text extractors
- **Spanning headers flattened** — a two-row header like `BLEU / EN-DE EN-FR` becomes `BLEU EN-DE`, `BLEU EN-FR`
- **Three detectors, best result wins** — ruled borders, whitespace alignment, and a hybrid; overlapping detections are de-duplicated
- **Table captions detected** — `Table 3: Variations on the architecture` is attached to the right table
- **Split decimals repaired** — `1 . 0` becomes `1.0`, `· 10 20` becomes `·10^20`
- **Quality score per table** so you can filter out noisy detections
- **JSON + CSV + Markdown** in every result — export to Excel in one click, or paste straight into an LLM prompt
- **No proxy, no browser, no API key** — fast and cheap to run

***

### Input

```json
{
  "pdfUrls": [
    "https://example.com/annual-report-2025.pdf",
    "https://arxiv.org/pdf/1706.03762"
  ],
  "strategy": "auto",
  "minRows": 2,
  "minColumns": 2,
  "minQualityScore": 25,
  "includeMarkdown": true,
  "includeCsv": true
}
```

| Field | Default | Notes |
|---|---|---|
| `pdfUrls` | — | Required. Direct links to PDF files. |
| `strategy` | `auto` | `auto` runs all detectors. Use `lines` for bordered tables, `text` for whitespace-aligned ones. |
| `minRows` / `minColumns` | `2` / `2` | Filters out fragments. Keep `minColumns` at 2+ to avoid catching paragraphs. |
| `minQualityScore` | `25` | 0–100. Lower it if tables are missed, raise it if you get noise. |
| `maxPagesPerPdf` | `0` | `0` processes every page. |
| `maxFileSizeMb` | `50` | Larger PDFs are skipped. |

***

### Output

One dataset item per table:

| Field | Description |
|---|---|
| `sourceUrl`, `fileName` | Where the table came from |
| `pageNumber`, `pageCount` | Location in the document |
| `tableIndex` | 1-based index within the PDF |
| `caption` | e.g. `Table 2: BLEU scores on newstest2014` |
| `header` | Header row, or `null` if none detected |
| `rows` | Data rows. Empty cells are `null`. |
| `rowCount`, `columnCount`, `emptyCellCount` | Shape of the table |
| `qualityScore` | 0–1 confidence in the detection |
| `extractionStrategy` | Which detector produced it |
| `markdown` | Ready-to-paste Markdown table |
| `csv` | CSV string |

Export the dataset as **Excel, CSV, JSON, or XML** from the Apify Console, or pull it
through the API.

***

### Typical uses

- **Financial analysis** — pull balance sheets and income statements out of annual reports
- **Research** — collect result tables across dozens of papers into one spreadsheet
- **Government and regulatory data** — statistics releases, tariff schedules, procurement notices
- **RAG and LLM pipelines** — feed models a real table instead of a scrambled paragraph
- **Competitive intelligence** — spec sheets and pricing tables from vendor PDFs

***

### Limits

- **Digital text only.** Scanned PDFs are images; run OCR first, then use this Actor.
  When no tables are found you get a result explaining why rather than an empty run.
- **Rotated and vertically-written tables** are not supported yet.
- **Cells merged across rows** are reported in the first row they occupy.
- Password-protected PDFs are skipped with an error field.

***

### Pricing

Pay per event. You are charged for each table successfully extracted, plus a small
start fee per run. PDFs that yield no tables cost only the start fee.

***

### Support

Found a PDF that extracts badly? Open an issue on the Actor's **Issues** tab with the
URL and the page number. Detector tuning is the main way this Actor improves.

# Actor input Schema

## `pdfUrls` (type: `array`):

Direct links to the PDF files you want tables from.

## `strategy` (type: `string`):

auto runs all three detectors and keeps the best result per table.

## `minRows` (type: `integer`):

Ignore tables with fewer rows.

## `minColumns` (type: `integer`):

Ignore tables with fewer columns.

## `requireCaption` (type: `boolean`):

Return only tables that have a detected caption such as Table 3. Recommended for research papers, annual reports and government publications.

## `minQualityScore` (type: `integer`):

0-100. Filters out sparse detections.

## `maxPagesPerPdf` (type: `integer`):

0 means every page.

## `maxFileSizeMb` (type: `integer`):

Larger PDFs are skipped.

## `includeMarkdown` (type: `boolean`):

Adds a Markdown table to every result.

## `includeCsv` (type: `boolean`):

Adds a CSV string to every result.

## Actor input object example

```json
{
  "pdfUrls": [
    "https://arxiv.org/pdf/1706.03762"
  ],
  "strategy": "auto",
  "minRows": 2,
  "minColumns": 2,
  "requireCaption": false,
  "minQualityScore": 25,
  "maxPagesPerPdf": 0,
  "maxFileSizeMb": 50,
  "includeMarkdown": true,
  "includeCsv": true
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "pdfUrls": [
        "https://arxiv.org/pdf/1706.03762"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("practical_ophthalmologist_iuq/pdf-table-extractor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "pdfUrls": ["https://arxiv.org/pdf/1706.03762"] }

# Run the Actor and wait for it to finish
run = client.actor("practical_ophthalmologist_iuq/pdf-table-extractor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "pdfUrls": [
    "https://arxiv.org/pdf/1706.03762"
  ]
}' |
apify call practical_ophthalmologist_iuq/pdf-table-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=practical_ophthalmologist_iuq/pdf-table-extractor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/WLr5Usx5BkiTaChLv/builds/f2bZmwKspTNf3loJt/openapi.json
