# PDF Text & Table Extractor – Pages, Markdown, RAG (`glidepath/pdf-text-extractor`) Actor

PDF to text, Markdown and tables, page by page. Input: PDF URLs (+ page ranges). Output: per-page text, Markdown with tables, table cells, RAG chunks and document metadata. No OCR. $1.50/1k pages; image-only pages free.

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

## Pricing

from $0.90 / 1,000 pdf pages

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 Text & Table Extractor – Pages, Markdown, RAG

**Turn PDF files into clean text, Markdown and tables, one row per page.** Give it PDF links; for every page you get the text in reading order, a Markdown version with the page's tables as Markdown tables, the table cells as rows and columns, optional RAG chunks, and the document's metadata. You pay per page, and pages without a text layer (scans, images, blank pages) are free.

Plain HTTP and a fast PDF parser, no browser: a 50-page report takes seconds.

### Who uses it

- **AI and RAG builders:** feed reports, manuals and papers into a vector database with page numbers kept, already split into chunks of the size you choose.
- **Analysts and researchers:** pull the tables out of statistical releases, annual reports and price lists, page by page, into JSON, CSV or Excel.
- **Developers and automations:** convert PDFs that arrive as links (invoices from a portal, public documents, filings) into text for a workflow in Make, Zapier or n8n.
- **Monitoring:** run it on a schedule for documents that are republished under the same URL.

### How to use it

1. Put one or more PDF links in **PDF URLs** (up to 500).
2. Optionally choose **Pages** (e.g. `1-5, 8`), turn tables or Markdown on or off, and set a **RAG chunk size**.
3. Run it and download the pages as JSON, CSV or Excel, or pass them to another Actor, an AI agent or your vector database.

### Input example

```json
{
  "urls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
    "https://www.irs.gov/pub/irs-pdf/p15.pdf"
  ],
  "pages": "1-10",
  "maxPages": 1000,
  "maxPagesPerFile": 0,
  "extractTables": true,
  "includeMarkdown": true,
  "preserveLayout": false,
  "chunkSize": 1000,
  "chunkOverlap": 100
}
```

### Output example

```json
{
  "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
  "fileName": "fw9.pdf",
  "pageNumber": 1,
  "pageCount": 6,
  "text": "W-9\nRequest for Taxpayer\nForm Give form to the\n(Rev. March 2024) Identification Number and Certification ...",
  "markdown": "W-9\nRequest for Taxpayer ...\n\n| Employer identification number |  |  | ... |\n|---|---|---|...",
  "tables": [
    {"index": 0, "rowCount": 2, "columnCount": 10, "rows": [["Employer identification number", "", "..."], ["", "", "–", "..."]]}
  ],
  "tableCount": 1,
  "charCount": 5650,
  "wordCount": 1010,
  "chunks": [{"index": 0, "text": "W-9\nRequest for Taxpayer ..."}],
  "title": "Form W-9 (Rev. March 2024)",
  "subject": "Request for Taxpayer Identification Number and Certification",
  "keywords": "Fillable",
  "creator": "Designer 6.5",
  "producer": "Designer 6.5",
  "createdAt": "2024-03-06T13:18:13Z",
  "modifiedAt": "2024-03-06T13:18:13Z",
  "pdfVersion": "1.7",
  "fileSizeBytes": 140815,
  "extractedAt": "2026-10-11T05:00:00Z"
}
```

| Field | Type | Description |
|---|---|---|
| `url` | string | The PDF URL you entered |
| `fileName` | string | File name from the URL |
| `pageNumber` | integer | Page number (1-based) |
| `pageCount` | integer | Pages in the whole file |
| `text` | string | Page text in reading order (e-mail addresses and phone numbers removed) |
| `markdown` | string / null | Page text with tables as Markdown tables (null when **Include Markdown** is off) |
| `tables` | array | Tables on the page: `index`, `rowCount`, `columnCount`, `rows` (rows of cell strings) |
| `tableCount` | integer | Number of tables on the page |
| `charCount` | integer | Characters in `text` |
| `wordCount` | integer | Words in `text` |
| `chunks` | array | RAG chunks of the page text: `index`, `text` (empty unless a chunk size is set) |
| `title` | string / null | Document title from the PDF's metadata |
| `subject` | string / null | Document subject from the PDF's metadata |
| `keywords` | string / null | Document keywords from the PDF's metadata |
| `creator` | string / null | Application that created the document |
| `producer` | string / null | Software that produced the PDF |
| `createdAt` | string / null | Creation date from the metadata, ISO 8601 UTC |
| `modifiedAt` | string / null | Modification date from the metadata, ISO 8601 UTC |
| `pdfVersion` | string / null | PDF version from the file header, e.g. 1.7 |
| `fileSizeBytes` | integer | Size of the downloaded file |
| `extractedAt` | string | ISO 8601 UTC |

The run's key-value store also has `SUMMARY` (per file: page count, pages saved, pages without a text layer) and, when something could not be read, `ERRORS` with the reason: `NOT_A_PDF`, `NOT_FOUND`, `NO_TEXT_LAYER`, `ENCRYPTED`, `DAMAGED`, `TOO_LARGE`, `TOO_MANY_PAGES`, `NO_PAGES_SELECTED`, `ROBOTS_DISALLOWED`, `BLOCKED`, `FETCH_FAILED`, `REFUSED`, `SKIPPED`.

### Use it with

- **AI agents and MCP:** the Apify MCP server exposes this Actor as a tool; the input is a list of PDF links.
- **Vector databases and LangChain/LlamaIndex:** use `chunks` (or `text` per page) with `url` and `pageNumber` as the source reference.
- **Make, Zapier, n8n, Google Sheets, Slack:** through the Integrations tab.
- **API and code:** the Apify API, the JavaScript and Python clients, or CSV/JSON/Excel downloads.

### Pricing

Pay per page: **$1.50 per 1,000 pages** on the Apify Free plan; on paid plans **$1.35** (Starter), **$1.10** (Scale) and **$0.90** (Business and higher) (the price for your plan is shown on the Pricing tab). The examples below use the Free-plan price. Tables, Markdown and RAG chunks are included. Only pages saved with text are charged; pages without a text layer, pages outside your page range and files that fail are free.

- 100 reports of 20 pages (2,000 pages) → about **$3.00**.
- 1,000 pages of manuals for a RAG knowledge base → about **$1.50**.
- 10,000 pages of filings a month → about **$15.00/month**.

A small start fee of $0.00005 per run applies. Set a **maximum cost per run** in the run options and the Actor stops cleanly when it is reached; **Max pages per run** does the same by count.

### Limits and notes

- **Text PDFs only, no OCR.** Scanned or image-only pages have no text layer; they are listed in `SUMMARY` and not charged. Text that is part of an image (a chart, a pasted table screenshot) is not extracted.
- **Tables are detected from their ruling lines** (cell borders). Tables drawn without lines still appear in `text` and `markdown` as plain lines, but not in `tables`.
- Rotated text (e.g. a vertical label in a page margin) is left out; it would otherwise come out letter-reversed.
- Up to 500 files per run, 50 MB and 2,000 pages per file, 100,000 pages per run. Password-protected files are reported as `ENCRYPTED`.
- **PDF links only.** A link that returns a web page or another file type is reported as `NOT_A_PDF` and not charged; to read web pages use a web scraper instead.
- **robots.txt is respected** (RFC 9309) for every file; if robots.txt can't be read (server error), the file is skipped to be safe. No logins, no proxies, no bot-check bypass; private or internal addresses are refused.
- E-mail addresses and phone numbers are replaced with `[email removed]` / `[phone removed]`; the document's author field is not output.

### FAQ

**Is it legal to extract text from PDFs?** This Actor downloads only files that are publicly reachable at the links you give, where the site's robots.txt allows it, without logging in or getting around any protection, and it removes e-mail addresses and phone numbers. You are responsible for having the right to use the documents you process (copyright and each site's terms still apply); if in doubt, ask a lawyer.

**How is this different from a web page to Markdown tool?** Those tools return a whole PDF as one block of text. This Actor keeps pages separate (with page numbers for citations), extracts tables as cells and as Markdown tables, lets you pick page ranges, splits text into RAG chunks and does not charge for pages without text.

**Why is a page missing?** It had no text layer (see `pagesWithoutText` in `SUMMARY`), was outside your **Pages** range, or a limit was reached.

**Can it read scanned PDFs?** Not in this version: there is no OCR. Scanned pages are skipped for free, so you can find them in `SUMMARY` and send only those to an OCR tool.

**Can I upload a file instead of a link?** Put the file somewhere it has a public link (e.g. a key-value store record or cloud storage) and pass that link.

### Changelog

See [CHANGELOG.md](CHANGELOG.md).

### Support

Found a problem or need a feature? Open an issue on the Actor's Issues tab. We read every one.

# Changelog

This Actor's version history is a separate document: https://apify.com/glidepath/pdf-text-extractor/changelog.md

# Actor input Schema

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

Direct links to PDF files, one per line (up to 500 per run, 50 MB and 2,000 pages per file). Links that return an HTML page or another file type are reported as NOT_A_PDF and not charged.

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

Which pages to extract from each file, e.g. '1-5, 8, 10-' (1-based). Empty = all pages.

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

Stop after saving this many pages in total (each saved page is charged).

## `maxPagesPerFile` (type: `integer`):

Cap for each PDF, so one long report doesn't use up the whole run.

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

Add each page's tables as rows of cells ('tables'). Tables are detected from their ruling lines (cell borders).

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

Add a 'markdown' field: the page text with its tables rendered as Markdown tables, in reading order.

## `preserveLayout` (type: `boolean`):

Pad the text with spaces so columns line up as on the page (useful for forms and statements). Off = plain reading-order text.

## `chunkSize` (type: `integer`):

Also split each page's text into chunks of at most this many characters ('chunks'), cut at paragraph, line or word boundaries, ready for embeddings. Use at least 200. Included in the page price.

## `chunkOverlap` (type: `integer`):

Characters repeated from the end of the previous chunk (less than half the chunk size).

## Actor input object example

```json
{
  "urls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ],
  "maxPages": 1000,
  "maxPagesPerFile": 0,
  "extractTables": true,
  "includeMarkdown": true,
  "preserveLayout": false,
  "chunkSize": 0,
  "chunkOverlap": 0
}
```

# Actor output Schema

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

No description

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

No description

## `errors` (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 = {
    "urls": [
        "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("glidepath/pdf-text-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.irs.gov/pub/irs-pdf/fw9.pdf"] }

# Run the Actor and wait for it to finish
run = client.actor("glidepath/pdf-text-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.irs.gov/pub/irs-pdf/fw9.pdf"
  ]
}' |
apify call glidepath/pdf-text-extractor --silent --output-dataset

```

## MCP server setup

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