# PDF Text Extractor — Extract Text from PDF Links (`nexgensignal/pdf-text-extractor-api`) Actor

Extract text from PDF links, or from every PDF a page links to. One row per PDF: full text, page and word counts, optional per-page text, title, subject, dates and producer. Scanned or locked PDFs are skipped, not billed. Part of NexGen Signal — data tools and official-source data products

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

## Pricing

from $33.50 / 1,000 pdf documents

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 — Extract Text from PDF Links

Give PDF links, or web pages that link to PDFs, and get **the text of each PDF** as one row: full text, page and word counts, optional per-page text with page labels, and the document's title, subject, keywords, creating application, producer and dates.

Text is read with PDFium, the engine inside Chrome, so columns, ligatures and non-Latin scripts come out cleanly and fast. Long documents are fine: a record holds up to 1.5 million characters, and anything longer is also saved in full as a text file with a link. Scanned PDFs with no text layer and password-protected PDFs are skipped and not billed.

The unit of output is one PDF delivered. Skipped files are not billed.

### Who buys this and for what job

- **AI and RAG builders** turning PDF libraries into text for embedding and search.
- **Analysts and researchers** pulling text from reports, filings and papers in bulk.
- **Compliance teams** indexing published documents for keyword monitoring.
- **Developers** who need a PDF-to-text step in a pipeline without running their own parser.

Buyers usually search for this as **"PDF to text API"**, **"extract text from PDF URL"** or **"PDF scraper"**.

### 📊 Sample Output

![Table of PDFs: file name, title, page count, word count and created date.](https://api.apify.com/v2/key-value-stores/IXCaMKjxSmUTLHhmq/records/pdf-text-extractor-api.png)

| file_name | title | page_count | word_count | created_at |
|---|---|---|---|---|
| p1fa.pdf | Publication 1 (FA) (Rev. 9-2017) | 2 | 2329 | 2020-07-31T17:22:37Z |
| p1ar.pdf | Publication 1 (AR) (Rev. 9-2017) | 2 | 1797 | 2020-07-31T17:36:59Z |
| p1bn.pdf | Publication 1 (BN) (Rev. 9-2017) | 2 | 4637 | 2020-07-31T17:29:52Z |
| p1fr.pdf | Publication 1 (FR) (Rev. 9-2017) | 2 | 2432 | 2020-07-31T17:17:26Z |
| p1guj.pdf | Publication 1 (GUJ) (Rev. 9-2017) | 2 | 3913 | 2020-07-31T17:10:00Z |

Rows from live run `c2eJyMXNXuF1mPNr2` on Apify (40 PDFs collected from two IRS forms pages). Notice the page counts run from a 1-page form to publications of over 100 pages; each is one record.

### ✅ What you get

Every field of each record:

- `record_id`, `sha256`, `bytes` — file hash (record_id is its first 24 characters) and size.
- `url`, `final_url`, `found_on_page`, `file_name` — the PDF link, where it was served from, the page it was found on, and the file name.
- `text`, `text_chars`, `word_count` — the text (pages separated by a blank line) and its size.
- `text_truncated`, `full_text_url` — true when the text was over 1.5 million characters; the full text is then saved as a file at full_text_url.
- `pages` — per-page text with page number and label (when includePages is on).
- `page_count`, `pages_read`, `pdf_version`, `page_labels_used`, `is_tagged` — document structure.
- `title`, `subject`, `keywords`, `creator_app`, `producer`, `created_at`, `modified_at` — document metadata (the Author field is not delivered).
- `read_at` — when it was read (UTC).

### ⚙️ Sample inputs

**One PDF (the Store example)**

```json
{
  "pdfUrls": ["https://www.irs.gov/pub/irs-pdf/fw4.pdf"],
  "maxDocuments": 10
}
```

Full text and metadata of the W-4 form.

**Every PDF a page links to**

```json
{
  "pdfUrls": ["https://www.irs.gov/forms-instructions-and-publications"],
  "maxDocuments": 50
}
```

Reads the page's PDF links, in page order.

**Per-page text, first 10 pages only**

```json
{
  "pdfUrls": ["https://www.irs.gov/pub/irs-pdf/p17.pdf"],
  "includePages": true,
  "maxPagesPerDocument": 10,
  "maxDocuments": 1
}
```

For page-level search or chunking.

Input fields:

| Field | Meaning |
|---|---|
| `pdfUrls` | PDF links, or pages with PDF links (up to 2,000). |
| `followPdfLinks` | read PDFs linked from pages (default true). |
| `maxPdfsPerPage` | PDF links taken per page (default 50). |
| `includePages` | add per-page text (default false). |
| `maxPagesPerDocument` | pages read per PDF (default 2,000). |
| `maxFileMb` | skip PDFs bigger than this (default 100). |
| `maxDocuments` | stop after this many delivered PDFs (default 1,000). Each delivered PDF is billed. |

### 🧾 JSON sample record

```json
{
  "record_id": "92444d8856ce55d9e25dca8b",
  "url": "https://www.irs.gov/pub/irs-pdf/fw4.pdf",
  "final_url": "https://www.irs.gov/pub/irs-pdf/fw4.pdf",
  "found_on_page": null,
  "file_name": "fw4.pdf",
  "bytes": 208845,
  "sha256": "92444d8856ce55d9e25dca8b6d1420634fc68b11e1ab1f760916ea29ddd312b2",
  "pdf_version": "1.7",
  "page_count": 5,
  "pages_read": 5,
  "text": "Form W-4\n2026\nEmployee’s Withholding Certificate\nDepartment of the Treasury\nInternal Revenue Service\nComplete Form W-4 so that your employer can withhold the correct federal income tax from your pay.\nGive Form W-4 to your employer.\nYour withholding is subject to review by the IRS.\nOMB No. 1545-0074\nStep 1:\nEnter\nPersonal\nInformation\n(a) First name and middle initial Last name\nAddress\nCity or town, state, and ZIP code\n(b) Social security number\nDoes your name match the\nname on your social security\ncard? If not, to ensure you get\ncredit for your earnings,\ncontact SSA at 800-772-1213\nor go to www …",
  "text_chars": 25713,
  "word_count": 4954,
  "text_truncated": false,
  "full_text_url": null,
  "pages": [
    {
      "page": 1,
      "label": null,
      "text": "Form W-4\n2026\nEmployee’s Withholding Certificate\nDepartment of the Treasury\nInternal Revenue Service\nComplete Form W-4 so that your employer can withhold the correct federal income tax from your pay.\n …",
      "chars": 4728
    },
    {
      "page": 2,
      "label": null,
      "text": "Form W-4 (2026) Page 2\nGeneral Instructions\nSection references are to the Internal Revenue Code unless\notherwise noted.\nFuture Developments\nFor the latest information about developments related to For …",
      "chars": 7271
    },
    "… 3 more pages"
  ],
  "title": "2026 Form W-4",
  "subject": "Employee's Withholding Certificate",
  "keywords": "Fillable",
  "creator_app": "Designer 6.5",
  "producer": "Designer 6.5",
  "created_at": "2025-12-11T13:15:41Z",
  "modified_at": "2025-12-11T13:15:41Z",
  "page_labels_used": false,
  "is_tagged": true,
  "read_at": "2026-10-17T03:50:39Z"
}
```

### 🔧 How it works

PDFs are downloaded 6 at a time, checked for the %PDF signature, and read page by page with pypdfium2 (PDFium). A PDF whose pages give no text at all is reported as scanned (it needs OCR) and not billed. The PDF's Author metadata is never delivered.

Every run reads each host's robots.txt first: a path it disallows is skipped, listed in the run receipt and never charged. Only public internet addresses are fetched (no private, loopback or cloud-metadata addresses, checked again on every redirect). No logins, no CAPTCHA solving, no proxies.

Delivery is pushed first and charged after. The run receipt (`RUN_RECEIPT`) lists every link with its outcome (for pages: PDF links found and what happened to each), the robots.txt result per host, and the delivered and charged counts.

### 💰 Pricing example

Per PDF delivered, no start fee, no monthly charge:

| Apify plan | Price per PDF |
|---|---|
| Free | $0.05 |
| Bronze | $0.045 |
| Silver | $0.04 |
| Gold, Platinum, Diamond | $0.0335 |

- 10 PDFs: $0.50 on the Free plan, $0.335 on Gold.
- 1,000 PDFs: $50.00 on Free, $33.50 on Gold.
- 10,000 PDFs: $500.00 on Free, $335.00 on Gold.

**Coverage and size.** Proof runs on 17 Oct 2026: 40 IRS PDFs from two pages in about 8 seconds, from 1-page forms to the 126-page Form 1040 instructions, each read in full. Text-layer PDFs only; for scans use the Image OCR Text Extractor.

### ⚖️ Legal & ToS

Documents belong to their owners. You choose the links. Make sure you have the right to download and use the content you point it at; the actor fetches only what you give it (and, where you turn it on, what those pages link to).

Every run reads each host's robots.txt first: a path it disallows is skipped, listed in the run receipt and never charged. Only public internet addresses are fetched (no private, loopback or cloud-metadata addresses, checked again on every redirect). No logins, no CAPTCHA solving, no proxies.

### ❓ FAQ

**Does it OCR scanned PDFs?**
No. A PDF with no text layer is skipped and not billed; the Image OCR Text Extractor reads scans.

**Are tables kept?**
Text comes out in reading order with line breaks; table cells are not split into columns.

**Can it open password-protected PDFs?**
No. They are skipped and not billed.

### 🆘 Troubleshooting

- **skipped: no text layer** — The PDF is a scan; use OCR.
- **text_truncated is true** — The document has over 1.5 million characters; download full_text_url (add your token if storages are private).
- **SOURCE UNAVAILABLE** — None of your links could be reached after retries. Nothing was charged.

### 🔗 Related Actors

- [PR Newswire Scraper](https://apify.com/nexgensignal/press-release-monitor-api) — press releases by company or topic
- [Website Tech Stack Detector](https://apify.com/nexgensignal/website-tech-stack-api) — what a site is built with
- [Website Contact Scraper](https://apify.com/nexgensignal/bulk-website-contact-extractor) — contact details from website lists

🏢 **About NexGen Signal** — data tools and official-source data products on Apify. Browse the catalog: [apify.com/nexgensignal](https://apify.com/nexgensignal)

### ⭐ Found this useful?

If this actor saved you time, a rating on its Apify Store page helps other buyers find it.

Part of NexGen Signal — data tools and official-source data products.

# Actor input Schema

## `pdfUrls` (type: `array`):

Direct PDF links, or web pages whose PDF links should be read. Up to 2,000 per run.

## `followPdfLinks` (type: `boolean`):

When a link is a web page, read the PDF files it links to.

## `maxPdfsPerPage` (type: `integer`):

Most PDF links to read from one page, in page order.

## `includePages` (type: `boolean`):

Add a pages list with each page's number, label and text.

## `maxPagesPerDocument` (type: `integer`):

Read at most this many pages of each PDF.

## `maxFileMb` (type: `integer`):

Skip PDFs bigger than this.

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

Stop after this many PDFs are delivered. Each delivered PDF is billed; skipped ones (scanned with no text, password-protected, not a PDF) are not.

## Actor input object example

```json
{
  "pdfUrls": [
    "https://www.irs.gov/pub/irs-pdf/fw4.pdf"
  ],
  "followPdfLinks": true,
  "maxPdfsPerPage": 50,
  "includePages": false,
  "maxPagesPerDocument": 2000,
  "maxFileMb": 100,
  "maxDocuments": 10
}
```

# Actor output Schema

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

The delivered (and billed) records.

## `receipt` (type: `string`):

Per-link outcome, robots result, delivered and charged counts.

# 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 = {
    "pdfUrls": [
        "https://www.irs.gov/pub/irs-pdf/fw4.pdf"
    ],
    "maxDocuments": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("nexgensignal/pdf-text-extractor-api").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 = {
    "pdfUrls": ["https://www.irs.gov/pub/irs-pdf/fw4.pdf"],
    "maxDocuments": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("nexgensignal/pdf-text-extractor-api").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 '{
  "pdfUrls": [
    "https://www.irs.gov/pub/irs-pdf/fw4.pdf"
  ],
  "maxDocuments": 10
}' |
apify call nexgensignal/pdf-text-extractor-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nexgensignal/pdf-text-extractor-api"
        }
    }
}
```

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/ofc9VKOK92eyJlWRG/builds/TP4GaUJGHAklypqUo/openapi.json
