# Extract Tables from PDF to CSV & Excel: Camelot Extractor (`raional/pdf-table-extractor`) Actor

Extract tables from any PDF into clean structured rows ready for CSV or Excel. Auto-detects ruled and borderless tables, with page number, row/column counts, and an accuracy score per table. Powered by Camelot, the open-source PDF table extraction library.

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

## Pricing

$20.00 / 1,000 tables

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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use 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

## Extract Tables from PDF to CSV & Excel: Camelot Table Extractor

**Extract tables from PDF** files into clean, structured rows and columns, ready to drop straight into **CSV** or **Excel**. Point it at a PDF URL (or upload a file) and get back every table it finds: page number, row/column counts, an accuracy score, and the table's data as a row-and-column grid.

Powered by [Camelot](https://github.com/camelot-dev/camelot) (MIT licensed, 3,700+ GitHub stars), the open-source PDF table extraction library used across data-engineering and RAG pipelines.

Great for: **extract tables from PDF**, **PDF table extractor**, **PDF table to CSV**, **PDF table to Excel**, **PDF to CSV API**, financial statement and invoice line-item extraction, scientific/research paper tables, government and statistical report tables, and any pipeline that needs structured data out of PDF reports.

### What it does

Give it a list of PDF URLs, or upload a file directly. For every PDF, it scans every page, finds every table, and returns one dataset row per table: the source document, the page it was found on, its position in the document, its size, a quality/accuracy score, and its full contents as a row-by-row grid of cell values, ready to export as CSV, Excel, or JSON straight from the Apify dataset (use the **Export dataset** button on the run's Storage tab).

### Table type: lattice vs. stream

PDF tables come in two visual styles, and Camelot uses a different detection strategy for each:

| `flavor` | Best for | How it works |
|---|---|---|
| `auto` (default) | Mixed or unknown PDFs | Detects the style per page automatically |
| `lattice` | Tables with visible grid lines/borders (invoices, bank statements, financial reports) | Detects the ruled lines that box in each cell |
| `stream` | Tables with no visible lines, columns aligned by whitespace only | Detects columns from text alignment and spacing |

Auto-detection is convenient but not perfect: on some documents it can misjudge which style to use and return a degenerate result (e.g. one column instead of eight). If a table comes back looking wrong, re-run with `flavor` set explicitly to `lattice` or `stream` based on how the table actually looks in the PDF.

### Example input

```json
{
  "documents": [
    "https://raw.githubusercontent.com/camelot-dev/camelot/master/docs/_static/pdf/foo.pdf"
  ],
  "flavor": "auto"
}
````

### Output

One row per table found:

| Field | Description |
|---|---|
| `documentUrl` | The source PDF |
| `status` | `ok`, `no_tables_found`, or `error` |
| `pageNumber` | Page the table was found on |
| `tableIndex` | Position of this table within the document (1, 2, 3, …) |
| `rowCount` / `columnCount` | Size of the table |
| `accuracy` | Camelot's own parsing accuracy score (0–100) |
| `flavor` | Which detection method was actually used for this table |
| `data` | The table's contents as an array of rows, each row an array of cell values |
| `error` | Present only when `status` is `error` |

A document with no tables produces a single `no_tables_found` row rather than an error, so you can tell "processed, nothing there" apart from "failed to process."

### Pricing

Billed **per table successfully extracted**: fair for a one-table invoice vs. a ten-table financial report. Documents that fail to process, and documents where no tables are found, are **not charged**.

### FAQ

**How do I extract tables from a PDF?**
Paste the PDF's URL into `documents` (or upload the file) and run. Each table found comes back as its own row in the output, with the data in the `data` field.

**How do I convert a PDF table to CSV or Excel?**
Run the actor, then use Apify's own **Export dataset** button (Storage tab) to download the results as CSV, Excel (XLSX), or JSON. No extra conversion step needed.

**What's the difference between lattice and stream?**
Lattice looks for tables with visible grid lines (like a bank statement or invoice). Stream looks for tables with no visible lines, where columns just line up with consistent spacing. `auto` picks per page, but isn't foolproof; see above.

**Does this work on scanned PDFs (images with no real text)?**
Not this actor. Camelot's lattice/stream/auto parsers read the PDF's embedded text layer; a scanned page that's really just a photo of a document has no text layer to read. It works great on regular digital PDFs, including ones exported from Word/Excel/accounting software, or scanned PDFs that have already been OCR'd elsewhere so they have a text layer.

**Can it handle a PDF with multiple tables, or tables on different pages?**
Yes. Every table on every page is detected and returned as its own row, in document order.

**How is this different from just using Camelot myself?**
Camelot is a Python library: using it yourself means installing Python, OpenCV, and managing compute. This runs it as a hosted API: no setup, pay only for tables actually extracted.

### Please note

Only process documents you have the right to. PDFs are processed transiently for the run and not retained afterward.

Built with the [Apify Python SDK](https://docs.apify.com/sdk/python/) + [Camelot](https://github.com/camelot-dev/camelot).

# Actor input Schema

## `documents` (type: `array`):

List of PDF URLs to extract tables from. Leave empty if you're only using the file upload field below.

## `uploadedDocument` (type: `string`):

Have a PDF on your computer instead of a URL? Upload it here. Combines with any URLs listed above.

## `flavor` (type: `string`):

auto = detect automatically per page (recommended for mixed/unknown PDFs). lattice = tables with visible grid lines/borders (invoices, financial statements). stream = tables with no visible lines, columns separated by whitespace only. Auto-detection isn't perfect: if results look off, try setting lattice or stream explicitly based on how the table looks in the PDF.

## `maxDocuments` (type: `integer`):

Safety cap on how many documents to process in one run.

## Actor input object example

```json
{
  "documents": [
    "https://raw.githubusercontent.com/camelot-dev/camelot/master/docs/_static/pdf/foo.pdf"
  ],
  "flavor": "auto",
  "maxDocuments": 50
}
```

# Actor output Schema

## `tables` (type: `string`):

One dataset row per table found: documentUrl, pageNumber, tableIndex, rowCount, columnCount, accuracy, flavor, data, status.

## `summary` (type: `string`):

Counts of documents processed, tables extracted, and errors.

# 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 = {
    "documents": [
        "https://raw.githubusercontent.com/camelot-dev/camelot/master/docs/_static/pdf/foo.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("raional/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 = { "documents": ["https://raw.githubusercontent.com/camelot-dev/camelot/master/docs/_static/pdf/foo.pdf"] }

# Run the Actor and wait for it to finish
run = client.actor("raional/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 '{
  "documents": [
    "https://raw.githubusercontent.com/camelot-dev/camelot/master/docs/_static/pdf/foo.pdf"
  ]
}' |
apify call raional/pdf-table-extractor --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Extract Tables from PDF to CSV & Excel: Camelot Extractor",
        "description": "Extract tables from any PDF into clean structured rows ready for CSV or Excel. Auto-detects ruled and borderless tables, with page number, row/column counts, and an accuracy score per table. Powered by Camelot, the open-source PDF table extraction library.",
        "version": "0.1",
        "x-build-id": "YC9RMu9ZjoNIkP12J"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/raional~pdf-table-extractor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-raional-pdf-table-extractor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/raional~pdf-table-extractor/runs": {
            "post": {
                "operationId": "runs-sync-raional-pdf-table-extractor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/raional~pdf-table-extractor/run-sync": {
            "post": {
                "operationId": "run-sync-raional-pdf-table-extractor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "documents": {
                        "title": "Document URLs",
                        "type": "array",
                        "description": "List of PDF URLs to extract tables from. Leave empty if you're only using the file upload field below.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "uploadedDocument": {
                        "title": "Or upload a file directly",
                        "type": "string",
                        "description": "Have a PDF on your computer instead of a URL? Upload it here. Combines with any URLs listed above."
                    },
                    "flavor": {
                        "title": "Table type",
                        "enum": [
                            "auto",
                            "lattice",
                            "stream"
                        ],
                        "type": "string",
                        "description": "auto = detect automatically per page (recommended for mixed/unknown PDFs). lattice = tables with visible grid lines/borders (invoices, financial statements). stream = tables with no visible lines, columns separated by whitespace only. Auto-detection isn't perfect: if results look off, try setting lattice or stream explicitly based on how the table looks in the PDF.",
                        "default": "auto"
                    },
                    "maxDocuments": {
                        "title": "Max documents per run",
                        "minimum": 1,
                        "maximum": 500,
                        "type": "integer",
                        "description": "Safety cap on how many documents to process in one run.",
                        "default": 50
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
