# PDF to Excel – PDF Table Extractor & Bank Statement Converter (`rowfeed/pdf-table-extractor`) Actor

Convert PDF tables to Excel, CSV and JSON: bank statements, invoices, price lists and reports. Upload PDFs or paste links; get every table as rows plus one Excel file per PDF. Finds ruled and unruled tables. $3 per 1,000 pages with tables + $0.10 per 1,000 pages read.

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

## Pricing

from $3.00 / 1,000 page with 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/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 to Excel – PDF Table Extractor & Bank Statement Converter

Convert the tables in PDFs to Excel, CSV and JSON: bank statements, invoices, price lists and report tables.
Upload PDFs or paste links; every table comes back as clean rows, plus **one Excel file per PDF** with each table on its own sheet. Works best on digital PDFs with clear tables: statements, invoices, price lists and ruled report tables.
Built for bookkeepers, analysts, finance teams, automation workflows (n8n, Make, Zapier) and AI agents that need PDF tables as data. No OCR service, no API key, no copy-paste.

### What you get

- **One row per table row** with `data` (column name → cell), the same values as an ordered `cells` list, the `columns`, and where it came from: `file_name`, `page`, `table` and `row`.
- **An Excel file per PDF** (`excel_url` on every row): one sheet per table, named `p3_t1` for page 3, table 1. Clear amounts like `1,250.00`, `-84.20`, `(12.00)` and European `1.250,00` become real numbers, so the sheet can sum them; account and card numbers, codes with leading zeros, dates and percentages stay text exactly as printed.
- **Ruled and unruled tables.** Tables drawn with lines are read cell by cell. Pages without lines (most bank statements, price lists) are read by how the words line up in columns. Running text, two-column page layouts and chart gridlines are not reported as tables.
- **Every transaction checked against the balance.** On statements with a balance column, each row gets `balance_check`: `ok` when the previous balance plus its deposit or minus its withdrawal gives the balance printed on that row, `mismatch` when it does not, so a misread row can never slip through unnoticed. Statements listed newest first are handled too. The run's log and `STATS` say how many rows were checked and whether any did not add up.
- **CSV and JSON** come from the dataset export as usual.

### Example output

A row from the pre-filled example (a sample bank statement):

```json
{
  "source": "https://raw.githubusercontent.com/kristerkb-pixel/rowfeed-seo-mcp/main/examples/bank-statement-example.pdf",
  "file_name": "bank-statement-example.pdf",
  "page": 1,
  "page_count": 2,
  "table": 1,
  "row": 2,
  "method": "text",
  "columns": ["Date", "Description", "Amount", "Balance"],
  "cells": ["03.10.2026", "Grocery store Oslo", "-84.20", "1,165.80"],
  "data": { "Date": "03.10.2026", "Description": "Grocery store Oslo", "Amount": "-84.20", "Balance": "1,165.80" },
  "excel_url": "https://api.apify.com/v2/key-value-stores/.../records/tables-1-bank-statement-example.xlsx",
  "balance_check": "ok"
}
```

### Input

| Field | What it does |
|---|---|
| `pdfs` | Upload PDF files or paste links to them. Up to 200 per run, 50 MB each. |
| `mode` | `auto` (default): ruled tables, and aligned-text tables on pages without ruled ones. `lines` or `text` force one method. |
| `firstRowIsHeader` | Use each table's first row as column names (default). Off: `col_1`, `col_2`, ... |
| `firstPage`, `lastPage` | Read only these pages (0 = from the start / to the end). |
| `maxPagesPerPdf` | Read at most this many pages per PDF. Default 100. |

### Pricing

Pay per event, no subscription:

- **$0.003 per page with tables** ($3 per 1,000 pages), however many tables or rows the page has.
- $0.0001 per page read ($0.10 per 1,000 pages), with or without tables.
- $0.002 per run start.
- Links that fail, files that are not PDFs and password-protected PDFs are free. A PDF that breaks partway through has paid only for the pages read before it broke.

Examples: a 3-page bank statement with a table on every page costs $0.0113. A 100-page report with tables on 10 pages costs $0.042.
Cap the cost of a run with the maximum charge setting; the run stops before a page it could not pay for.

### Good to know

- **Tested on** statements laid out like those of banks in Norway, the Netherlands, Hong Kong (bilingual Chinese/English), India, Singapore and the US, in English, Norwegian, Dutch, French and Chinese column names, with US (1,234.56), European (1.234,56) and French (1 234,56) amounts. The balance check reads column names in English, Norwegian, German, French, Dutch, Spanish, Italian, Chinese, Japanese and Korean. Layouts vary by bank: run one statement first and look at `balance_check`. Where it says `mismatch`, look at that row in the PDF.
- **No OCR.** The PDF needs a text layer (you can select the text in a PDF viewer). Scanned or photographed pages have none, so no tables are found; they only cost the $0.0001 page read.
- Table detection is automatic and PDFs vary a lot: check the first file of a new kind. Complex report layouts (tables with only an outer frame, numbers spread with spaces) can come back split into several tables or with merged columns. Headers spread over several lines may become two rows, and a table broken by a heading or a page break comes back as two tables.
- **Why it asks for full permissions:** Apify only lets an Actor read a file you upload if it has full permissions. This Actor reads only the PDFs you give it and writes only to the run's own storage. You approve it once in Apify Console, on the first run. Runs started through the API or by an AI agent also need that one approval first.
- Links must be public.
- Your PDFs are read inside your own Apify run and not stored anywhere else.

### More tools from Rowfeed

- [PDF to Text](https://apify.com/rowfeed/pdf-page-text-extractor) – the full text of every PDF page, one row per page.
- [SEO Audit Tool](https://apify.com/rowfeed/seo-site-audit) – page-by-page SEO checks for any website.
- [Website Traffic Checker](https://apify.com/rowfeed/website-traffic-checker) – estimated traffic of any website.

# Actor input Schema

## `pdfs` (type: `array`):

Upload PDFs or paste links to them (one per line): bank statements, invoices, price lists, reports. Up to 200 per run, 50 MB each.

## `mode` (type: `string`):

Auto reads tables drawn with lines, and on pages without them finds tables by how the words line up (bank statements, price lists). Lines only or Aligned text only force one method.

## `firstRowIsHeader` (type: `boolean`):

Use each table's first row as column names. Off: columns are col_1, col_2, ... and the first row is a data row.

## `firstPage` (type: `integer`):

First page to read (1 = the first page). 0 = from the start.

## `lastPage` (type: `integer`):

Last page to read. 0 = to the end.

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

Read at most this many pages of each PDF. Each page read costs $0.0001; pages with tables $0.003 more.

## Actor input object example

```json
{
  "pdfs": [
    "https://raw.githubusercontent.com/kristerkb-pixel/rowfeed-seo-mcp/main/examples/bank-statement-example.pdf",
    "https://www.census.gov/construction/nrc/pdf/newresconst.pdf"
  ],
  "mode": "auto",
  "firstRowIsHeader": true,
  "firstPage": 0,
  "lastPage": 0,
  "maxPagesPerPdf": 100
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("rowfeed/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 = {}

# Run the Actor and wait for it to finish
run = client.actor("rowfeed/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 '{}' |
apify call rowfeed/pdf-table-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,rowfeed/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/8X6YnxLlhmRrOuwzX/builds/55xXwfc1W0pzcppJr/openapi.json
