# PDF Extractor: PDF to Text, Tables & Metadata (`ucchiii/pdf-extractor`) Actor

PDF to text and PDF table extractor for public PDF URLs (input: list of PDF URLs, optional page range). Per-page text, tables, title/author metadata. Scanned pages flagged, no OCR. Empty input = free demo. $0.004 per PDF incl. 20 pages, $0.0005 per extra page.

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

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Extractor — PDF to text, tables & metadata

**For developers, analysts and AI/RAG pipelines that need the contents of PDFs as clean data.** Give it public PDF URLs and
get **per-page text**, **tables as rows and cells** and document metadata (title, author, producer, page count). A
**pdf to text** and **pdf table extractor** in one Actor, with no OCR and no files to upload. **$0.004 per PDF (first 20 extracted pages included) + $0.0005 per extra page.**
Leave the input empty for a free demo.

### Use cases

- Feed reports, filings, manuals and papers into a RAG pipeline or LLM, page by page with page numbers
- Pull tables out of financial reports, price lists and government statistics into JSON/CSV
- Build a searchable text index of a PDF library, with title and author from the metadata
- Spot scanned documents (image-only pages) before sending them to an OCR service

### How it works

- Each PDF is downloaded once, then parsed with pdfplumber (MIT licence). Text and tables come from the PDF's own text layer.
- `pages` limits the range (`1-5,8`, `3-`), `maxPagesPerPdf` caps long documents (default 200, max 2000).
- Two output shapes: `perPage` (one row per page) or `perDocument` (one row per PDF with all text and tables).
- **Scanned pages**: a page with no text layer and only images is returned with `scanned: true` and empty text. There is **no OCR**.
- **Pay only for results**: a page is charged only when text or tables were extracted. Failed downloads, unreadable PDFs, blank pages and scanned pages are free. The free demo (empty input) is not charged.
- Safe by design: URLs that resolve to internal addresses (localhost, private ranges, link-local such as 169.254.169.254) are never contacted, including redirect targets (`blocked_private_address`). Max 5 redirects, 50 MB per file, 60 s per download, `%PDF` signature required.
- Per-document `SUMMARY` in the key-value store: title, author, producer, pageCount, pagesExtracted, pagesCharged, scannedPages, error.

### Input

```json
{ "urls": ["https://example.com/report.pdf"], "pages": "1-5,8", "extractTables": true, "outputMode": "perPage", "maxPagesPerPdf": 200 }
```

### Output (perPage)

```json
{
  "url": "https://example.com/report.pdf", "page": 1, "text": "Quarterly Report ...", "charCount": 1834, "scanned": false, "error": null,
  "tables": [{"header": ["Region", "Revenue", "Growth"], "rows": [["North", "1,200", "5%"], ["South", "900", "7%"]]}]
}
```

`header` is the first row of a table when it looks like a header (all cells filled, no numbers), otherwise `null` and the first row stays in `rows`.
`error` is e.g. `http_404`, `not_a_pdf`, `file_too_large`, `timeout`, `too_many_redirects`, `encrypted`, `unreadable_pdf`, `blocked_private_address`, `bad_pages:...`
(a failed PDF produces one row with `page: 0`). In `perDocument` mode each row has `text`, `tables` (each with its `page`), `title`, `author`, `producer`, `pageCount`, `pagesExtracted`, `scannedPages`, `error`.

### Pricing

$0.004 per PDF (pay per event, `pdf`; includes the first 20 extracted pages) + $0.0005 per extra page (`page`). Example: a 10-page PDF costs $0.004, a 100-page PDF $0.044. Failed, scanned-only and empty PDFs are free. For comparison, other PDF text Actors in the Apify Store charge about $0.003 to $0.02 per PDF (per document) or $0.003 to $0.005 per page.

### Limits and notes

- Text order and table detection follow the PDF's layout; complex multi-column pages and borderless tables may need checking.
- Password-protected PDFs are not opened (`encrypted`). Only the PDFs you list are downloaded; links inside them are not followed.
- Only process PDFs you are allowed to download. The text belongs to the document's rights holder.

### More tools from the same developer

- [Website Screenshot API](https://apify.com/ucchiii/website-screenshot) — screenshots and PDFs of web pages: full page, mobile, element, no cookie banners
- [Google Sheets Export](https://apify.com/ucchiii/google-sheets-export) — export an Apify dataset to Google Sheets

# Actor input Schema

## `urls` (type: `array`):

Public PDF URLs (http:// or https://), for example 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf'. Each PDF is downloaded once (max 50 MB, 60 s, up to 5 redirects). Up to 500 URLs per run. Leave empty to run a free demo on a small public PDF (not charged).

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

Optional page range, 1-based, for example '1-5,8' or '3-' (page 3 to the end). Empty = all pages (up to the limit below). Pages beyond the end of a PDF are ignored. Applied to every PDF.

## `extractTables` (type: `boolean`):

Detect ruled and aligned tables on each page. Each table is {header, rows}: 'header' is the first row when it looks like a header (all cells filled, no numbers), otherwise null. Turn off for plain text only (a little faster).

## `outputMode` (type: `string`):

'perPage' (default): one dataset row per page with url, page, text, tables, charCount, scanned, error. 'perDocument': one row per PDF with all text, all tables (with page numbers), title, author, producer, pageCount. Charging is the same in both modes ($0.004 per PDF incl. 20 pages, $0.0005 per extra page).

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

Stop after this many pages of each PDF (allowed 1 to 2000, default 200). Caps your cost on very long documents.

## Actor input object example

```json
{
  "urls": [
    "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
  ],
  "extractTables": true,
  "outputMode": "perPage",
  "maxPagesPerPdf": 200
}
```

# Actor output Schema

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

perPage: one item per PDF page (url, page, text, tables, charCount, scanned, error). perDocument: one item per PDF (text, tables, title, author, producer, pageCount, scannedPages, error).

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

Record SUMMARY: per PDF title, author, producer, pageCount, pagesExtracted, pagesCharged, scannedPages and error.

# 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 = {
    "urls": [
        "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("ucchiii/pdf-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 = { "urls": ["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("ucchiii/pdf-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 '{
  "urls": [
    "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
  ]
}' |
apify call ucchiii/pdf-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ucchiii/pdf-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/izgvTktt3ftpy6cdO/builds/GrEqNcLKuvxfg9PyD/openapi.json
