# PDF Table Extractor: PDF to CSV, JSON & Markdown (`withgrokbot/pdf-table-extractor`) Actor

Extract tables from PDFs to CSV, JSON and Markdown, plus per-page text. Digital and scanned (OCR) pages, auto-detected; ruled and borderless tables. Open-source engines, no third-party APIs. Pay per page processed; failed pages are free.

- **URL**: https://apify.com/withgrokbot/pdf-table-extractor.md
- **Developed by:** [402xAgent](https://apify.com/withgrokbot) (community)
- **Categories:** AI, Developer tools, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 page processeds

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## PDF Table Extractor: convert PDF tables to CSV, JSON & Markdown

Give it a PDF, get its tables back as **CSV, JSON and Markdown**, plus the plain text of every page. Works on **digital PDFs** (text layer) and **scanned PDFs** (OCR), and detects which is which per page automatically.

Built only on open-source engines (pdfplumber, pdfium, Tesseract OCR). Your file is not sent to any third-party AI or OCR API.

### What it does

- **Ruled tables** (cell borders) via line detection.
- **Borderless tables** (whitespace-aligned columns) via word-position clustering.
- **Scanned pages**: renders at 300 DPI, runs Tesseract, rebuilds rows and columns from word boxes.
- **Per-page text** for every processed page.
- **Combined files** per PDF in the key-value store: `<name>.csv`, `<name>.json`, `<name>.md`.

### Input

| Field | Description |
|---|---|
| `pdfUrls` | Direct links to PDFs you own or may process. Only these files are fetched (no crawling). |
| `pdfFile` | Upload a PDF in the console. |
| `kvStoreKey` | Key(s) of PDF records in the run's default key-value store (comma-separated). For a file in another store, pass its signed `recordPublicUrl` in `pdfUrls`. |
| `pageRange` | e.g. `1-3,7`. Empty = all pages. |
| `maxPages` | Cap per file (default 200). |
| `outputFormats` | Any of `csv`, `json`, `markdown`. |
| `ocr` | `auto` (default), `always`, `never`. |
| `borderless` | Detect tables without ruling lines (default on). |
| `includeText` | Return page text (default on). |

### Output (one dataset item per page)

```json
{ "source": "https://example.com/report.pdf", "page": 3, "status": "success", "mode": "digital",
  "tableCount": 1,
  "tables": [{ "index": 0, "method": "lattice", "rowCount": 5, "colCount": 3,
    "rows": [["State","2019","2020"],["Alabama","1,204","1,388"]],
    "csv": "State,2019,2020\n...", "json": [{"State":"Alabama","2019":"1,204","2020":"1,388"}],
    "markdown": "| State | 2019 | 2020 |\n|---|---|---|\n..." }],
  "text": "..." }
```

`method` is `lattice` (ruled), `stream` (borderless) or `ocr`. Failed pages have `status: "failed"` and a `reason`.

### Pricing

- **$0.005 per page** processed successfully (`page` event).
- A **scanned page (OCR) counts as 3 events** ($0.015), because OCR takes far more compute. Set `ocr` to `never` to avoid OCR.
- **Failed pages are free**, and so are files that can't be fetched or aren't PDFs.

### Accuracy (tested on 30 public PDFs)

- **Digital PDFs: ~97% cell accuracy** against benchmark answers (camelot test suite, ICDAR-2013). All 34 real tables were found.
- **Scanned PDFs: lower, ~80% cell accuracy.** OCR quality depends on the scan.

#### Known caveats

- **False tables:** author/affiliation blocks, figure grids and form boxes (e.g. on tax forms) can be returned as tables.
- **Split tables:** long borderless tables are sometimes returned as 2-3 consecutive fragments, and closely spaced columns can merge.
- **OCR errors:** OCR can drop decimal points (e.g. `3.30` read as `330`) or misread characters, and it can merge two tables that sit close together on one page. Check numbers on scanned pages.
- Rotated or vertical header text may come out reversed. Encrypted PDFs that need a password are not supported. Max 100 MB per file.

### Use cases

- **PDF to CSV / spreadsheet**: pull tables out of reports, statements and government PDFs for Excel or Google Sheets.
- **Data pipelines and ETL**: get structured JSON rows from PDF tables without manual copy-paste.
- **LLM and RAG input**: Markdown tables and page text that language models read well.

### FAQ

**How do I extract a table from a PDF?** Add the PDF link to `pdfUrls` (or upload with `pdfFile`) and run. Each page becomes one dataset item with its `tables` as CSV, JSON and Markdown.

**Does it work on scanned PDFs?** Yes, scanned pages are OCR'd with Tesseract automatically (`ocr: auto`). Accuracy is lower than on digital PDFs; see the caveats above.

**Can it read tables without borders?** Yes, borderless detection is on by default (`borderless`).

**Is my PDF sent to a third-party API?** No. Only open-source engines run inside the Actor.

**What does it cost?** $0.005 per successfully processed page; an OCR page counts as 3 events ($0.015). Failed pages are free.

Made by withgrokbot.

# Actor input Schema

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

Direct links to PDF files you own or are allowed to process. Only these files are fetched; no crawling.

## `pdfFile` (type: `string`):

Or upload a PDF file directly.

## `kvStoreKey` (type: `string`):

Or the key of a PDF record in this run's default key-value store (Comma-separate several keys. For a file in another store, pass its signed recordPublicUrl in PDF URLs instead.

## `pageRange` (type: `string`):

e.g. '1-3,7'. Empty = all pages (up to Max pages).

## `maxPages` (type: `integer`):

Safety cap on pages processed (and charged) per file.

## `outputFormats` (type: `array`):

Which table representations to include.

## `ocr` (type: `string`):

auto = OCR only pages detected as scanned (no text layer).

## `ocrLanguage` (type: `string`):

Tesseract language code (eng is installed).

## `borderless` (type: `boolean`):

Also find tables without ruling lines (whitespace-aligned columns).

## `includeText` (type: `boolean`):

Return the plain text of each page.

## Actor input object example

```json
{
  "pdfUrls": [
    "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
  ],
  "maxPages": 200,
  "outputFormats": [
    "csv",
    "json",
    "markdown"
  ],
  "ocr": "auto",
  "ocrLanguage": "eng",
  "borderless": true,
  "includeText": true
}
```

# Actor output Schema

## `pages` (type: `string`):

One item per processed page: tables (CSV/JSON/Markdown) and text.

## `files` (type: `string`):

Per-PDF combined tables as <name>.csv / .json / .md in the key-value store.

# 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://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("withgrokbot/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://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"] }

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

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).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://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
  ]
}' |
apify call withgrokbot/pdf-table-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,withgrokbot/pdf-table-extractor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

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