# PDF Text Extractor (`deriverge/pdf-text-extractor`) Actor

Text from PDF files as paragraphs or Markdown, one row per file or per page, with title, author, dates and page count. Give PDF links or a web page that links to PDFs; there is no OCR, so scanned pages without a text layer come back empty.

- **URL**: https://apify.com/deriverge/pdf-text-extractor.md
- **Developed by:** [deriverge s.r.o.](https://apify.com/deriverge) (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.10 / 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 Extractor

PDF Text Extractor downloads PDF files and returns their text as paragraphs or as Markdown with headings and lists, one row per file or one row per page. File rows also carry the metadata (title, author, dates, PDF version), the page count, a SHA-256 checksum and the web links found in the file. Paste PDF links, or a web page that links to PDFs, and click **Start**. There is no OCR, so pages that are only scanned images come back without text. It costs $0.20 per 1,000 PDF pages on the Free plan ($0.10 on Business), and the $5 of monthly credit in Apify's Free plan covers about 25,000 PDF pages.

### What data does it return?

| Field | Description |
|---|---|
| `url`, `fileName` | Address of the PDF and the file name in it, e.g. `p5027.pdf`. |
| `sourcePage` | The web page that linked to the PDF when you gave a page; `null` for direct links. |
| `status`, `error` | `ok`, `no_text` (no page had text), `not_pdf`, `robots_disallowed` (the website's robots.txt disallows the file) or `error`, with the reason in `error`, e.g. `The server answered HTTP 404.` |
| `pageCount`, `pagesExtracted`, `pagesWithoutText` | Pages in the file, pages that gave text, and pages without text such as scans or blank pages. |
| `title` | Title from the file's metadata, or the largest line on the first page when the metadata has none. |
| `author`, `subject`, `keywords` | Metadata as the file carries it. IRS forms and publications, for example, give an office code such as `SE:W:CAS:AM:IPSO` as the author. |
| `creator`, `producer`, `pdfVersion` | The programs that made the document and the PDF, e.g. `Adobe InDesign CS5.5 (7.5.2)`, and the PDF version (`1.7`). |
| `createdAt`, `modifiedAt` | Creation and modification dates from the metadata, ISO 8601 in UTC. |
| `language`, `hasForm` | Language from the metadata (`en-US`) and whether the PDF has fillable form fields. |
| `sizeBytes`, `sha256` | File size in bytes and the SHA-256 checksum; a new checksum means the file has changed. |
| `text`, `format` | The text of the whole file, in `text` or `markdown` format. |
| `wordCount`, `charCount` | Words and characters in `text`. |
| `links` | Web links in the file, up to 500, e.g. `http://www.identitytheft.gov/`. |
| `checkedAt` | When the file was read, ISO 8601 in UTC. |

### How to extract text from PDFs

1. Put the addresses in **PDF links or web pages**, one per line. A link that returns a PDF is read directly. A web page, such as a list of reports, is searched for links that end in `.pdf`, and up to **Maximum PDFs per web page** of them (50 unless you change it) are read.
2. Pick the **Output format**. Plain text joins the lines of each paragraph and rejoins words hyphenated at a line end. Markdown adds `#` headings for lines set larger than the body text, and `-` and `1.` list items.
3. Turn on **One row per page** to get every page as its own row, for example to index a long document page by page.
4. To read part of each file, fill **Pages** with a range such as `1-5, 8, 10-` (`10-` means page 10 to the end), or lower **Maximum pages per PDF** from 1,000.
5. Click **Start**. Three files are read at a time, and the rows appear in the **Output** tab, where you can export them as JSON, CSV or Excel.

Input for the API:

```json
{
  "urls": [
    "https://www.irs.gov/pub/irs-pdf/p1281.pdf",
    "https://www.irs.gov/forms-pubs/about-form-w-9"
  ],
  "format": "markdown",
  "rowPerPage": true,
  "pages": "1-10"
}
```

### Example output

A file row from a run on 27 September 2026. The PDF was found on an IRS page about Form W-9 that was given as a web page, so `sourcePage` is filled. The Markdown text is cut after its first two sections; `wordCount` and `charCount` describe the whole text.

```json
{
  "url": "https://www.irs.gov/pub/irs-pdf/p5027.pdf",
  "sourcePage": "https://www.irs.gov/forms-pubs/about-form-w-9",
  "fileName": "p5027.pdf",
  "status": "ok",
  "error": null,
  "pageCount": 1,
  "pagesExtracted": 1,
  "pagesWithoutText": 0,
  "title": "Publication 5027 (Rev. 5-2018)",
  "author": "SE:W:CAS:AM:IPSO",
  "subject": "Identity Theft Information for Taxpayers",
  "keywords": null,
  "creator": "Adobe InDesign CS5.5 (7.5.2)",
  "producer": "Adobe PDF Library 9.9",
  "createdAt": "2018-06-05T22:34:59.000Z",
  "modifiedAt": "2018-06-06T13:10:48.000Z",
  "language": null,
  "pdfVersion": "1.7",
  "hasForm": false,
  "sizeBytes": 93363,
  "sha256": "fdffcfc09ff43520c2f51e11c8e2fda12b80eb8d0b86193303592a1eda620fad",
  "wordCount": 690,
  "charCount": 4203,
  "format": "markdown",
  "text": "# Identity Theft Information for Taxpayers\n\nIdentity theft places a burden on its victims and presents a challenge to many businesses, organizations and governments, including the IRS. The IRS combats this crime with an aggressive strategy of prevention, detection and victim assistance.\n\nPublication 5027 (Rev. 5-2018) Catalog Number 67495R Department of the Treasury Internal Revenue Service www.irs.gov\n\n## What is tax-related identity theft?\n\nTax-related identity theft occurs when someone uses your stolen Social Security number (SSN) to file a tax return claiming a fraudulent refund. If you become a victim, we are committed to resolving your case as quickly as possible.\n\nYou may be unaware that this has happened until you e-file your return and discover that a return already has been filed using your SSN. Or, the IRS may send you a letter saying it has identified a suspicious return using your SSN.\n\n## Know the warning signs\n\nBe alert to possible tax-related identity theft if you are contacted by the IRS about:\n\n- More than one tax return was filed for you,\n\n- You owe additional tax, have a refund offset or have had collection actions taken against you for a year you did not file a tax return, or\n\n- IRS records indicate you received wages or other income from an employer for whom you did not work.\n\n...",
  "links": [
    "https://www.irs.gov/",
    "http://www.identitytheft.gov/",
    "http://www.equifax.com/",
    "http://www.experian.com/",
    "http://www.transunion.com/",
    "https://www.irs.gov/pub/irs-pdf/f14039.pdf",
    "https://www.irs.gov/identitytheft",
    "https://www.irs.gov/Individuals/Data-Breach-Information-for-Taxpayers",
    "https://www.irs.gov/taxessecuritytogether",
    "https://www.irs.gov/pub/irs-pdf/p4524.pdf"
  ],
  "checkedAt": "2026-09-27T07:18:50.225Z"
}
```

### How much does it cost to extract text from PDFs?

| | Free plan | Starter | Scale | Business |
|---|---|---|---|---|
| 1,000 PDF pages | $0.20 | $0.16 | $0.13 | $0.10 |

You pay only for the events in the table. There is no start fee, and compute time and proxies are included.

A batch of 100 reports with 20 pages each is 2,000 pages: $0.40 on the Free plan and $0.20 on Business. Only pages that gave text count, and a page range or **Maximum pages per PDF** caps the cost of long files.

### Limits

- The text keeps the order in which the PDF stores it; the actor does not re-sort it by position on the page. A file whose text boxes are stored out of order comes out in that order.
- Tables come out as lines of text, and the Markdown output has no table syntax.
- Headings are recognized by font size only. Bold text in the body size stays a plain paragraph.
- Running headers and footers stay in the text of every page. Only lines that consist of a page number, with or without the word page or a total, are dropped.
- On web pages, only links whose address ends in `.pdf` or carries `format=pdf` or `type=pdf` count. Files offered through scripts or download buttons without such an address are not found.
- The actor reads each website's `robots.txt` first and follows it, also for every redirect. A file address you entered that `robots.txt` disallows gives a free `robots_disallowed` row; disallowed files linked from a web page are skipped and counted in the `SUMMARY` record. When `robots.txt` answers with a server error or not at all, the website is skipped in that run.
- Some websites refuse the download and answer HTTP 403; those files come back as `error`. Files behind a login cannot be read, because the actor sends no cookies or credentials.
- Excel cells hold at most 32,767 characters, and a file row can have more: the six-page Form W-9 alone has 37,975 characters. For spreadsheets, use **One row per page**.
- When no address in a run can be read at all, the run ends as failed. Its rows still give the reason for each address.

### Scanned, locked and very large files

The actor reads the text layer of each PDF and has no OCR. Pages that are only images, as in scanned documents, have no text layer: they are left out of the text and counted in `pagesWithoutText`. When none of the pages read has text, the file row has `status` `no_text` and the metadata, and the file is not charged. Scans that already went through OCR carry an invisible text layer and are read like any other PDF.

A PDF that needs a password to open cannot be read. Its row has `status` `error` and the message `The PDF could not be opened: No password given`, and there is no input for passwords. PDFs that only restrict copying or printing open without a password and are read normally.

The largest file a run reads depends on the memory it runs with: 32 MB with 512 MB, 100 MB with 1 GB and 150 MB with 2 GB or more. A larger file is not downloaded past that size, also when the server does not say the size in advance, and its row has `status` `error` with the limit in `error`, such as `The file is larger than 150 MB.` A download that takes more than 3 minutes is started again, up to three attempts in all. Of long files, only the first 1,000 pages are read unless you change **Maximum pages per PDF**; `pageCount` still shows the full length.

### Rows per page

With **One row per page** on, each page that has text becomes a row with `page` (its number), `pageCount`, `text`, `wordCount` and `charCount`, plus the file's `url`, `fileName`, `sourcePage`, `title` and `author`. The links of the file are attached to its first page row. Pages without text get no row. A file that yields no text at all still comes back as one file row with its `status`, so failed files stay visible.

### FAQ

#### Is it legal to extract text from PDFs?

The actor downloads the files at the addresses you give, as a browser would, reads the text in them and keeps nothing beyond the run's output. It does not sign in anywhere. Copyright in the documents stays with their authors, and some PDFs contain personal data, so you are responsible for using the text in line with each document's license and the law that applies to you.

#### Why does `title` differ from the heading on the first page?

`title` comes from the file's metadata when it has one, and publishers often fill it with a form or catalog name. The IRS publication in the example has the metadata title `Publication 5027 (Rev. 5-2018)`, while its first heading reads Identity Theft Information for Taxpayers. The largest line of the first page is used only when the metadata title is empty.

#### Can it collect every PDF on a website?

It reads the PDFs linked from each page you list and does not follow links to other pages. List the pages that hold the documents, such as a reports or publications page, one per line.

### Related scrapers

- [Article Extractor](https://apify.com/deriverge/article-extractor)

### Support

This actor is built and maintained by deriverge s.r.o., a software company based in the Czech Republic. If a run fails or a field you need is missing, please open an issue in the **Issues** tab or write to us at info@deriverge.com. We respond in English and Czech. Runs can be scheduled in Apify Console or started from the **API** tab, which has examples for Python, JavaScript and cURL and works with Make, Zapier, n8n and the Apify MCP server. If the actor saves you time, a short review helps other people find it.

# Changelog

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

# Actor input Schema

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

One per line: a link to a PDF file, or a web page whose linked PDFs should all be extracted (for example a reports or documents page).

## `format` (type: `string`):

Plain text in paragraphs, or Markdown with headings detected from font sizes and bullet and numbered lists.

## `rowPerPage` (type: `boolean`):

Output each page as its own row with its page number, ready for chunking in RAG pipelines. Otherwise one row per PDF with the whole text.

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

Page range to extract, for example 1-5, 8, 10- (from page 10 to the end). Empty means all pages.

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

Stop after this many pages of each PDF.

## `includeLinks` (type: `boolean`):

List the web links found in the PDF.

## `maxPdfsPerWebPage` (type: `integer`):

When a web page is given, extract at most this many PDFs it links to.

## Actor input object example

```json
{
  "urls": [
    "https://arxiv.org/pdf/1706.03762"
  ],
  "format": "text",
  "rowPerPage": false,
  "maxPagesPerPdf": 1000,
  "includeLinks": true,
  "maxPdfsPerWebPage": 50
}
```

# Actor output Schema

## `documents` (type: `string`):

One row per PDF (or per page) with the text or Markdown, metadata and links.

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

PDFs extracted, scanned files without text, pages charged.

# 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://arxiv.org/pdf/1706.03762"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("deriverge/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://arxiv.org/pdf/1706.03762"] }

# Run the Actor and wait for it to finish
run = client.actor("deriverge/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://arxiv.org/pdf/1706.03762"
  ]
}' |
apify call deriverge/pdf-text-extractor --silent --output-dataset

```

## MCP server setup

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