# PDF to Text Extractor — Native Text & Metadata (`junipr/pdf-to-text-extractor`) Actor

Extract text, page metadata, outlines, links, and document info from PDFs with page-level output and automation-friendly exports.

- **URL**: https://apify.com/junipr/pdf-to-text-extractor.md
- **Developed by:** [junipr](https://apify.com/junipr) (community)
- **Categories:** Automation, Developer tools
- **Stats:** 3 total users, 0 monthly users, 72.4% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.20 / 1,000 page extracteds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## PDF to Text Extractor — Native Text & Metadata

Extract text, page metadata, outlines, links, and document info from PDFs with page-level output and automation-friendly exports. Process batches of PDFs by URL with configurable concurrency, progress logging, and structured JSON results.

***

### Features

- **Text extraction** from text-based PDFs using the proven `pdf-parse` library
- **Metadata extraction** — title, author, subject, creator, producer, creation date, modification date, and PDF version
- **Page-by-page output** — get individual page text and character counts instead of one combined blob
- **Multiple output formats** — plain text, markdown (paragraph-structured), or full JSON
- **Batch processing** — provide many PDF URLs and process them concurrently (up to 10 at once)
- **Max pages limit** — extract only the first N pages for cost control on large documents
- **Progress logging** — detailed logs for each PDF: download size, parse status, page count
- **Error resilience** — per-PDF error capture so one bad PDF doesn't abort the batch
- **Zero-config** — runs immediately with the bounded W3C default PDF, no setup required

***

### Input

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `pdfUrls` | array | W3C default PDF | List of `{ url, label? }` objects to process |
| `outputFormat` | string | `"text"` | `text`, `markdown`, or `json` |
| `extractMetadata` | boolean | `true` | Extract PDF metadata (title, author, dates, etc.) |
| `pageByPage` | boolean | `false` | Output each page separately with character counts |
| `maxPages` | integer | `0` (all) | Max pages per PDF (0 = no limit) |
| `maxConcurrency` | integer | `3` | Simultaneous PDFs (1–10) |
| `requestTimeout` | integer | `60000` | Download timeout in milliseconds |

#### Input Example

```json
{
  "pdfUrls": [
    { "url": "https://example.com/report.pdf", "label": "annual-report" },
    { "url": "https://example.com/manual.pdf", "label": "user-manual" }
  ],
  "outputFormat": "text",
  "extractMetadata": true,
  "pageByPage": true,
  "maxPages": 50,
  "maxConcurrency": 5,
  "requestTimeout": 90000
}
```

***

### Output

Each processed PDF produces one dataset item. Results are available as JSON/CSV via the Apify dataset API.

#### Output Example

```json
{
  "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
  "label": "annual-report",
  "fileName": "dummy.pdf",
  "metadata": {
    "title": "Dummy PDF",
    "author": null,
    "subject": null,
    "creator": "Writer",
    "producer": "LibreOffice 3.3",
    "creationDate": "D:20100909004945-07'00'",
    "modDate": null,
    "pdfVersion": "1.4"
  },
  "text": "Dummy PDF file\n\nThis is a dummy PDF...",
  "pageCount": 1,
  "pages": [
    {
      "pageNumber": 1,
      "text": "Dummy PDF file\n\nThis is a dummy PDF...",
      "charCount": 247
    }
  ],
  "extractedAt": "2025-01-01T12:00:00.000Z",
  "errors": []
}
```

***

### Cost

Pricing is **pay-per-page-extracted** at **$5.20 per 1,000 pages extracted** in the queued monetization update. You are only charged for pages that are successfully extracted — failed downloads and parse errors are free.

| Usage | Estimated Cost |
|-------|---------------|
| 1,000 pages | $5.20 |
| 10,000 pages | $52.00 |
| 100,000 pages | $520.00 |

Use the `maxPages` setting to cap extraction per PDF and control costs on large documents.

***

### Limitations

- **Text-based PDFs only** — scanned/image PDFs require OCR and are not supported by this actor. Text extraction will return empty strings for image-only pages.
- **No password-protected PDFs** — encrypted PDFs that require a password are not supported.
- **URL access required** — PDFs must be publicly accessible via HTTP/HTTPS. PDFs behind login walls or requiring cookies will fail to download.
- **Memory** — very large PDFs (500+ pages, 100MB+) may require more than the default 2 GB memory. Increase the memory limit in the run options if you encounter out-of-memory errors.
- **No OCR fallback** — if you need to extract text from scanned PDFs, consider pairing this actor with an OCR service.

***

### Use Cases

- **RAG / LLM pipelines** — extract clean text from documents for embedding and retrieval
- **Document search** — build searchable indexes from PDF libraries
- **Data extraction** — pull structured content from reports, manuals, and whitepapers
- **Compliance and archival** — convert PDFs to plain text for long-term storage and auditing
- **Batch processing** — process hundreds of PDFs concurrently with a single actor run

***

### Competitive Advantage vs Other Extractors

The leading PDF extractor on Apify Store (928+ users) extracts text but provides no metadata, no page-level output, and no progress logging. This actor adds:

- **Full metadata** — title, author, dates, PDF version, and creator information
- **Page-by-page output** — get individual pages with character counts, ideal for chunked LLM ingestion
- **Structured JSON** — every result is a typed dataset item, not a raw text blob
- **Progress logs** — know exactly which PDFs succeeded, how many pages were extracted, and what failed
- **Multiple output formats** — plain text, markdown-structured, or full JSON with metadata embedded

***

### Related Actors

- [PDF to HTML Converter](https://apify.com/junipr/pdf-to-html) — Convert PDF documents to semantic HTML with heading detection, table extraction, and image support
- [RAG Web Extractor](https://apify.com/junipr/rag-web-extractor) — Extract clean, chunked text from web pages for LLM pipelines
- [Website to RSS](https://apify.com/junipr/website-to-rss) — Turn any website into an RSS feed for monitoring and automation

***

#### FAQ

**Does this work on scanned PDFs?**

No. This actor uses text extraction from the PDF content stream. Scanned PDFs are essentially images embedded in a PDF container — there is no text layer to extract. If your PDFs are scanned, you need an OCR solution.

**Can I process PDFs from Google Drive or Dropbox?**

Only if the PDF is served as a direct public download URL (e.g., a shared link with `dl=1` for Dropbox). Links that redirect to a preview page won't work. Use the direct download URL format for your cloud storage provider.

**What happens if one PDF in my batch fails?**

The actor continues processing the remaining PDFs. The failed PDF will have an empty `text` field and a non-empty `errors` array in the dataset. Successful PDFs are unaffected.

**How do I use the `pageByPage` output for LLM chunking?**

Set `pageByPage: true` and each dataset item will include a `pages` array where every element has `pageNumber`, `text`, and `charCount`. You can further filter or chunk pages in your downstream pipeline based on character count.

**What is the `outputFormat: "markdown"` option?**

Markdown mode normalizes the extracted text into paragraph-separated blocks (double newlines between paragraphs). It does not add headers, bullets, or tables — the PDF's raw text doesn't contain enough structure for reliable markdown formatting. For heading detection and rich HTML, use the [PDF to HTML Converter](https://apify.com/junipr/pdf-to-html) instead.

# Actor input Schema

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

List of PDF URLs to extract text from. Each entry is an object with a 'url' field and an optional 'label' for identification.

## `outputFormat` (type: `string`):

Format for the extracted text. 'text' returns plain text, 'markdown' adds basic structure hints, 'json' returns a structured JSON object with text and metadata combined.

## `extractMetadata` (type: `boolean`):

Extract PDF metadata such as title, author, subject, creator, producer, creation date, and modification date.

## `pageByPage` (type: `boolean`):

When enabled, output includes a 'pages' array with text and character count for each individual page. When disabled, all pages are combined into a single text block.

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

Maximum number of pages to extract per PDF. Set to 0 to extract all pages. Useful for previewing large documents or managing costs.

## `maxConcurrency` (type: `integer`):

Maximum number of PDFs to process simultaneously. Increase for faster batch processing of many PDFs.

## `requestTimeout` (type: `integer`):

Timeout in milliseconds for downloading each PDF from a URL. Increase for very large files or slow servers.

## Actor input object example

```json
{
  "pdfUrls": [
    {
      "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
      "label": "w3c-dummy"
    }
  ],
  "outputFormat": "text",
  "extractMetadata": true,
  "pageByPage": false,
  "maxPages": 0,
  "maxConcurrency": 3,
  "requestTimeout": 60000
}
```

# Actor output Schema

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

Extracted text, metadata, and page-level content for each processed PDF

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("junipr/pdf-to-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("junipr/pdf-to-text-extractor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call junipr/pdf-to-text-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=junipr/pdf-to-text-extractor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/zO9Z3hnW90YGGYd7Y/builds/j3U9zOdpt8hzdQSYj/openapi.json
