# PDF Text Extractor - Text & Metadata by URL (`praise-most-high/pdf-text-extractor-scraper`) Actor

PDF Text Extractor extracts the full text of any PDF from its URL and returns one row per file: text, page count, word count, title, author, dates and a sha256. Bulk input, optional per-page text, JSON, CSV or Excel export. Scanned or unreadable PDFs are never charged.

- **URL**: https://apify.com/praise-most-high/pdf-text-extractor-scraper.md
- **Developed by:** [angel nguyen](https://apify.com/praise-most-high) (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

$5.50 / 1,000 pdf extracteds

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 Text Extractor extracts the full text of any PDF you give it by URL and returns one clean row per file. You get the text, the page count, word and character counts, the document's own metadata (title, author, producer, creation date) and a sha256 of the file, ready to export as JSON, CSV or Excel or to pull through the API.

Use it when you have a list of PDF links and you need their text in a table: research papers for a RAG pipeline, public filings, standards documents, manuals, reports or forms.

### Key features

- **Bulk PDF to text.** Paste one URL or thousands. Each PDF becomes one row.
- **Full text, in reading order.** The whole text layer of every page, joined with page breaks.
- **Per-page text on request.** Turn on `includePageTexts` to also get a `pages` array, one entry per page, which is the shape most chunking and embedding code wants.
- **Document metadata.** Title, author, subject, keywords, creator, producer, creation and modification dates in ISO 8601, and the PDF version.
- **File fingerprint.** File size in bytes and a sha256 of the exact file that was read, so you can detect when a document changes.
- **You pay only for PDFs that yield text.** A dead link, a page that is not a PDF, a password-protected file and a scanned PDF with no text layer are skipped, listed with the reason and never charged.

### PDF text extractor: how it works

1. Add your PDF links to **PDF URLs**. Each link must return the PDF file itself.
2. Optionally set **Maximum pages per PDF** to read only the opening pages, or turn on **Include per-page text**.
3. Click **Start**. The Actor downloads each file, reads its text layer and writes one row per PDF to the dataset.
4. Export the dataset as JSON, CSV, Excel or HTML, or read it through the Apify API.

Anything that could not be extracted is written to the `SKIPPED` record in the run's key-value store, with the URL and the reason. Skipped files are not dataset rows and are not charged.

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `pdfUrls` | array of strings | required | Direct links to the PDF files to extract. |
| `maxPagesPerPdf` | integer | `0` | Read only the first N pages of each PDF. `0` reads every page. Does not change the price. |
| `includePageTexts` | boolean | `false` | Adds a `pages` array with each page's text. Does not change the price. |
| `maxFileSizeMb` | integer | `50` | Skips any PDF larger than this. Maximum 200. A skipped file is not charged. |

#### Input example

```json
{
    "pdfUrls": [
        "https://bitcoin.org/bitcoin.pdf",
        "https://www.rfc-editor.org/rfc/rfc9110.pdf"
    ],
    "maxPagesPerPdf": 0,
    "includePageTexts": false,
    "maxFileSizeMb": 50
}
```

### Output

Each row describes one PDF.

| Field | Meaning |
|---|---|
| `url`, `finalUrl` | The link you gave and the link the file was served from after redirects. |
| `fileName` | The last path segment of the final URL. |
| `fileSizeBytes`, `sha256` | Size and sha256 of the exact file read. |
| `pageCount` | Pages in the document. |
| `pagesExtracted` | Pages actually read. Lower than `pageCount` only when you set `maxPagesPerPdf`. |
| `charCount`, `wordCount` | Size of the extracted text. |
| `text` | The extracted text. Pages are separated by a blank line. |
| `textTruncated` | `true` only if a row had to be shortened to fit the dataset's item size limit. |
| `title`, `author`, `subject`, `keywords`, `creator`, `producer` | The document's own metadata. `null` when the PDF does not set the field. |
| `creationDate`, `modificationDate` | ISO 8601, or `null`. |
| `pdfVersion` | The PDF format version the file declares. |
| `pages` | Only with `includePageTexts`: `[{ "page": 1, "charCount": 2412, "text": "..." }]`. |
| `extractedAt` | When the row was produced. |

#### Sample output data

This is a real row from run `gVImysURQHM2DrMS9` of this Actor on 2026-10-03, with the input shown above plus four more URLs. One thing is shortened here: `text` shows its first two lines of the 21,235 characters the run returned. Every other value is as delivered.

```json
{
    "url": "https://bitcoin.org/bitcoin.pdf",
    "finalUrl": "https://bitcoin.org/bitcoin.pdf",
    "fileName": "bitcoin.pdf",
    "fileSizeBytes": 184292,
    "sha256": "b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553",
    "pageCount": 9,
    "pagesExtracted": 9,
    "charCount": 21235,
    "wordCount": 3558,
    "textTruncated": false,
    "title": null,
    "author": null,
    "subject": null,
    "keywords": null,
    "creator": "Writer",
    "producer": "OpenOffice.org 2.4",
    "creationDate": "2009-03-24T17:33:15.000Z",
    "modificationDate": null,
    "pdfVersion": "1.4",
    "text": "Bitcoin: A Peer-to-Peer Electronic Cash System\nSatoshi Nakamoto",
    "extractedAt": "2026-10-03T17:58:54.397Z"
}
```

The same run read a 194-page, 71,309-word standards document and a 6-page tax form into one row each.

### Pricing

This Actor uses pay-per-event pricing with one event.

| Event | Price | When it is charged |
|---|---|---|
| PDF extracted | **$0.0055** per PDF ($5.50 per 1,000) | Once for each PDF whose text was extracted and written to the dataset, whatever its page count. |

There is no start fee and no per-page fee. A 1-page PDF and a 500-page PDF cost the same.

What is never charged: a download that fails, a URL that does not return a PDF, a password-protected PDF, a file over your size limit, and a scanned PDF with no text layer.

Worked example: 200 PDF links, of which 190 yield text and 10 are dead links, cost 190 × $0.0055 = **$1.045**.

Each run extracts the PDFs you list in that run. If you run the same link again it is extracted and charged again, so keep a list of what you have already processed and send only new links. The `sha256` field lets you check whether a document changed before you re-extract it.

You can set a maximum charge per run in the run options. When the limit is reached the Actor stops, and every row you received is a row you paid for.

### FAQ

**Does it read scanned PDFs?**
No. This Actor reads the text layer of a PDF and does not run OCR. A scanned document with no text layer is skipped and not charged. If a PDF mixes scanned and text pages, you get the text pages.

**Does it keep tables and layout?**
It returns plain text in reading order. Table cells arrive as text on lines, without cell structure. Labels inside a diagram can arrive in a different order than they appear on the page.

**Can it read a PDF behind a login?**
No. The link must be reachable without cookies or a password. Some sites refuse automated downloads; those links are skipped with the HTTP status as the reason and are not charged.

**How large can a PDF be?**
Up to the `maxFileSizeMb` you set, 200 MB at most. Very large documents need more memory; raise the run's memory if a big file fails.

**Is it legal to extract text from PDFs?**
You are responsible for having the right to download and process the documents you submit. This Actor only reads the links you give it. Do not use it on documents you are not allowed to copy, and take care with documents that contain personal data.

**How do I use the text with an LLM or vector database?**
Turn on `includePageTexts` and send each `pages[].text` entry to your embedding step, keeping `url` and `page` as the citation.

### Limits and troubleshooting

- No OCR. Text must exist in the PDF's text layer.
- One row is capped at about 8 MB. A longer text is shortened and `textTruncated` is set to `true`.
- A link to a web page that displays a PDF is not a PDF link. Use the direct file URL.
- Three download attempts are made for timeouts and server errors before a link is skipped.

### Related Actors

Other Actors from the same publisher that return one typed row per record:

- [Open Library & Gutenberg Book Lookup](https://apify.com/praise-most-high/book-isbn-catalog) for book metadata by ISBN.
- [NPPES NPI Registry Lookup](https://apify.com/praise-most-high/nppes-npi-registry-lookup) for US healthcare provider records.
- [Shopify Store Product Scraper](https://apify.com/praise-most-high/shopify-store-product-catalog) for product catalogues.
- [YouTube Channel Upload Monitor](https://apify.com/praise-most-high/youtube-channel-upload-monitor) for new uploads by channel.

### Your feedback

Found a PDF that should have worked? Open an issue on the Actor's **Issues** tab with the link, and it will be looked at.

### Legal

This Actor is unofficial. It is not affiliated with, endorsed by or sponsored by Adobe, the PDF Association, or any site named in the examples above (bitcoin.org, rfc-editor.org). PDF is an open ISO standard (ISO 32000). The example documents are public and are used only to show the output shape.

# Actor input Schema

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

Direct links to the PDF files to extract, one per line. Each URL must return the PDF itself, not a web page that embeds one. You are charged once per PDF that yields text; a link that fails, is not a PDF, is password-protected or has no text layer costs nothing.

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

Read only the first N pages of each PDF. 0 reads every page. The price per PDF is the same either way; use this to keep rows small when you only need the opening pages.

## `includePageTexts` (type: `boolean`):

Add a pages array to each row with the text of every page separately, as well as the full text. Useful for chunking a document for search or RAG. Costs nothing extra.

## `maxFileSizeMb` (type: `integer`):

Skip any PDF larger than this many megabytes. A skipped file is listed with its reason and is not charged. Maximum 200.

## Actor input object example

```json
{
  "pdfUrls": [
    "https://bitcoin.org/bitcoin.pdf",
    "https://www.rfc-editor.org/rfc/rfc9110.pdf"
  ],
  "maxPagesPerPdf": 0,
  "includePageTexts": false,
  "maxFileSizeMb": 50
}
```

# Actor output Schema

## `pdfs` (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 = {
    "pdfUrls": [
        "https://bitcoin.org/bitcoin.pdf",
        "https://www.rfc-editor.org/rfc/rfc9110.pdf"
    ],
    "maxPagesPerPdf": 0,
    "includePageTexts": false,
    "maxFileSizeMb": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("praise-most-high/pdf-text-extractor-scraper").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://bitcoin.org/bitcoin.pdf",
        "https://www.rfc-editor.org/rfc/rfc9110.pdf",
    ],
    "maxPagesPerPdf": 0,
    "includePageTexts": False,
    "maxFileSizeMb": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("praise-most-high/pdf-text-extractor-scraper").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://bitcoin.org/bitcoin.pdf",
    "https://www.rfc-editor.org/rfc/rfc9110.pdf"
  ],
  "maxPagesPerPdf": 0,
  "includePageTexts": false,
  "maxFileSizeMb": 50
}' |
apify call praise-most-high/pdf-text-extractor-scraper --silent --output-dataset

```

## MCP server setup

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

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/gAo8VCd5TNZRWm3Ed/builds/SjRRapVzfx0ByFFIl/openapi.json
