PDF Text & Metadata Extractor (Apify Actor)

Convert PDFs to Markdown or plain text for LLM and RAG pipelines. Optional chunking (chunkSize, chunkOverlap) returns one item per chunk with page numbers, plus metadata. Charged only for PDFs that yield extractable text. Runs as an Apify Actor, callable from MCP clients via the Apify MCP server (https://mcp.apify.com/?actors=pennydata/pdf-text-extractor-pay-per-pdf).

Hosted MCP Server

npx add-mcp 'https://mcp.apify.com/?actors=pennydata/pdf-text-extractor-pay-per-pdf'

Installs into Claude Code, Codex, Cursor and more

Documentation

PDF Text & Metadata Extractor (pay per PDF) (pennydata/pdf-text-extractor-pay-per-pdf) Actor

Convert PDFs to Markdown or plain text for LLM and RAG pipelines. Optional chunking (chunkSize, chunkOverlap) returns one item per chunk with page numbers, plus metadata and table-like lines. You are charged only for PDFs that yield extractable text. Built with AI assistance (Claude).

Pricing

from $3.00 / 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 section below.

Each client library has reference documentation the quickstart does not restate: JavaScript/TypeScript (npm install apify-client) and Python (pip install apify-client).

README

PDF Text & Metadata Extractor (pay per PDF)

PDF to Markdown and RAG chunks for LLMs. Set outputFormat to markdown for headings, lists and tables preserved, and chunkSize (for example 800 tokens, chunkOverlap 100) to get one dataset item per chunk with pageStart/pageEnd, ready for a vector database. Still one flat charge per PDF, however many chunks it yields. Defaults stay plain text, one item per PDF.

Give it a list of public PDF URLs. You are charged only for PDFs that yield extractable text; failed, non-PDF and scanned/image-only PDFs (no text layer) are free. For each one you get a dataset item with:

  • text (whole document) and pages[] (page, text, charCount, tableLikeLineCount, optional lines[])
  • pageCount and metadata (title, author, subject, keywords, creator, producer, creation/modification date, PDF version)
  • lines[]: text grouped by visual row, split into cells at wide horizontal gaps; rows with 3+ cells are flagged tableLike (a lightweight way to recover simple tables, not a full table-structure parser)

Broken URLs, non-PDF responses, oversized files and timeouts produce an item with status: "error" and an error message. The run continues with the next URL.

Input

FieldDescription
urlsPDF URLs (required)
maxFileSizeMBSkip files larger than this (default 25)
maxPagesExtract only the first N pages, 0 = all
includeLinesInclude per-page line/cell grouping (default true)
outputFormattext (default) or markdown (headings from font size, bullets, pipe tables)
chunkSize0 = one item per PDF (default). Above 0 = one item per RAG chunk of about this many tokens, with chunkIndex, pageStart, pageEnd, pages
chunkOverlapTokens repeated between consecutive chunks (default 0)
maxPdfsProcess at most this many URLs per run (default 100)
timeoutSecsDownload timeout per PDF (default 60)

Switching from another PDF text extractor

Saved tasks, API calls and integrations written for other Store PDF text extractors (for example gochujang/pdf-text-extractor and automation-lab/pdf-text-extractor) run here after you change only the actor id. These input fields are accepted:

Field you already useWhat happens here
urls (strings or {url} objects)used as is
url, pdfUrls, startUrls, sourcesmerged into urls
urlsText (one URL per line)split and merged into urls
timeoutPerPdfSecssame as timeoutSecs
maxPdfMbsame as maxFileSizeMB
endPagesame as maxPages (first N pages)
includePages: falsedrops pages[], keeps text
outputFormat (text or markdown), chunkSize, chunkOverlapsupported, same meaning as in the Input table above
maxConcurrency, password, extractImagesignored

Not supported (a note is written to the run log and the run continues): base64Pdfs (URLs only), startPage above 1, extractTables, any outputFormat other than text or markdown (treated as text), and password-protected PDFs. Output field names are this actor's own (text, pages, pageCount, metadata), so check your downstream mapping. Not affiliated with those actors or their authors.

Pricing

Pay per event: one pdf-extracted event (USD 0.003) per PDF that is parsed successfully. Errors and PDFs with no extractable text (status: "no_text") are not charged. At most maxPdfs (default 100) URLs are processed per run.

Price per PDF

This actor charges USD 0.003 per PDF parsed successfully, flat for every plan, with no per-run start fee. A comparable Store actor (automation-lab/pdf-text-extractor, prices read from its public Store page on 2026-10-21) lists a USD 0.005 per-run start fee plus USD 0.00345 per PDF on the free plan. Example: 10 PDFs in one run cost USD 0.03 here versus about USD 0.0395 there. Check each actor's current pricing before you compare; prices change.

Limits

  • Text-based PDFs only. Scanned/image PDFs contain no text layer, so they return empty text (no OCR).
  • Password-protected PDFs return an error item.
  • Only public http(s) URLs; localhost/private network addresses are refused.

Built with pdfjs-dist (Apache-2.0) and the Apify SDK.

Local run

mkdir -p storage/key_value_stores/default
echo '{"urls":[{"url":"https://example.com/a.pdf"}]}' > storage/key_value_stores/default/INPUT.json
npm install && npm start

Non-affiliation

Not affiliated with or endorsed by Adobe or any PDF publisher whose files you supply.

AI disclosure

Built with AI assistance (Claude); maintained by the publisher.

Related PennyData actors

I built this and earn from its runs. Written with AI assistance (Claude).


Other languages

Español: Extraer texto de PDF en masa

Descarga una lista de PDFs por URL y extrae el texto, el número de páginas y los metadatos en JSON, con opción de dividir el texto en fragmentos para IA. Sin clave de API. Precio: 0,003 USD por PDF extraído con éxito (más 0,00005 USD por el inicio del actor).

Ejemplo de entrada:

{"urls":[{"url":"https://www.w3.org/WAI/WCAG21/working-examples/pdf-table/table.pdf"}],"maxPages":0}

Português: Extrair texto de PDF em massa

Baixe uma lista de PDFs por URL e extraia o texto, o número de páginas e os metadados em JSON, com opção de dividir o texto em trechos para IA. Sem chave de API. Preço: US$ 0,003 por PDF extraído com sucesso (mais US$ 0,00005 pelo início do actor).

Exemplo de entrada:

{"urls":[{"url":"https://www.w3.org/WAI/WCAG21/working-examples/pdf-table/table.pdf"}],"maxPages":0}

Deutsch: Text aus PDF-Dateien extrahieren

Lädt eine Liste von PDFs per URL herunter und extrahiert Text, Seitenzahl und Metadaten als JSON, optional in Abschnitte für KI-Anwendungen aufgeteilt. Kein API-Schlüssel nötig. Preis: 0,003 USD pro erfolgreich verarbeitetem PDF (plus 0,00005 USD für den Start des Actors).

Beispieleingabe:

{"urls":[{"url":"https://www.w3.org/WAI/WCAG21/working-examples/pdf-table/table.pdf"}],"maxPages":0}

I built this and earn from its runs. Written with AI assistance (Claude).

Actor input Schema

urls (type: array):

Public http(s) URLs of PDF files. Each successfully processed PDF is one billable event; failed URLs yield an error item and are free. Also accepted as aliases: pdfUrls, startUrls, sources, url, urlsText (see Switching from another PDF extractor in the README). Also accepted as aliases: pdfUrls, startUrls, sources, url, urlsText (see Switching from another PDF extractor in the README).

maxFileSizeMB (type: integer):

PDFs larger than this are skipped with an error item (checked via Content-Length and while downloading).

maxPages (type: integer):

Only extract the first N pages. 0 means all pages. Page count in the output always reflects the full document.

includeLines (type: boolean):

Add per-page 'lines' with text grouped by visual row and split into cells where wide horizontal gaps occur (rows with 3+ cells are flagged tableLike). Turn off for smaller output.

outputFormat (type: string):

'text' (default) keeps the plain text output. 'markdown' converts a PDF to Markdown: headings from font size, bullet lists and pipe tables, in 'text' and each page's 'markdown' field. Use markdown for LLM prompts and RAG.

chunkSize (type: integer):

0 (default) returns one item per PDF. A value above 0 returns one dataset item per RAG chunk of about this many tokens (4 characters per token), split on paragraph boundaries, each with pageStart, pageEnd, pages and chunkIndex, ready for a vector database. Still billed once per PDF. Try 500 to 1000.

chunkOverlap (type: integer):

Tokens of trailing text repeated at the start of the next chunk so context is not lost at boundaries (only used when chunkSize is above 0). Capped at half the chunk size.

timeoutSecs (type: integer):

Maximum time to wait for each PDF download before giving up with an error item.

maxPdfs (type: integer):

Process at most this many URLs per run (cost guard).

url (type: string):

Compatibility alias: one PDF URL, merged with urls.

urlsText (type: string):

Compatibility alias: PDF URLs pasted one per line, merged with urls.

pdfUrls (type: array):

Compatibility alias for urls (strings or {url} objects).

startUrls (type: array):

Compatibility alias for urls (strings or {url} objects).

sources (type: array):

Compatibility alias for urls (strings or {url} objects).

timeoutPerPdfSecs (type: integer):

Compatibility alias for timeoutSecs.

maxPdfMb (type: integer):

Compatibility alias for maxFileSizeMB.

endPage (type: integer):

Compatibility alias: extract the first N pages (same as maxPages).

includePages (type: boolean):

Compatibility: false drops the pages[] array from each item (text is kept).

Actor input object example

{
  "urls": [
    {
      "url": "https://www.w3.org/WAI/WCAG21/working-examples/pdf-table/table.pdf"
    }
  ],
  "maxFileSizeMB": 25,
  "maxPages": 0,
  "includeLines": true,
  "outputFormat": "text",
  "chunkSize": 0,
  "chunkOverlap": 0,
  "timeoutSecs": 60,
  "maxPdfs": 100
}

Actor output Schema

results (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

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": [
        {
            "url": "https://www.w3.org/WAI/WCAG21/working-examples/pdf-table/table.pdf"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("pennydata/pdf-text-extractor-pay-per-pdf").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

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": [{ "url": "https://www.w3.org/WAI/WCAG21/working-examples/pdf-table/table.pdf" }] }

# Run the Actor and wait for it to finish
run = client.actor("pennydata/pdf-text-extractor-pay-per-pdf").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

echo '{
  "urls": [
    {
      "url": "https://www.w3.org/WAI/WCAG21/working-examples/pdf-table/table.pdf"
    }
  ]
}' |
apify call pennydata/pdf-text-extractor-pay-per-pdf --silent --output-dataset

MCP server setup

{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,pennydata/pdf-text-extractor-pay-per-pdf"
        }
    }
}

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/JzoscX8Mgn2kfiuxd/builds/Rb7l08kllGXEvALey/openapi.json