# PDF Text & Table Extractor — Markdown & JSON (`keyman98/pdf-text-extractor`) Actor

Extract text, tables and metadata from PDF URLs into clean Markdown, plain text or JSON. Pay only for PDFs processed successfully. Digital PDFs only — no OCR, no scanned files.

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

## Pricing

from $4.00 / 1,000 pdf processeds

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 & Table Extractor

Extract text from PDF files, pull out tables, and convert PDF to Markdown or plain text — in bulk, from a list of URLs, with no software to install. Give it public PDF links and get back clean text, table data, and document metadata for each file, ready to read, copy, or feed into another tool.

### What you get (output fields)

For each PDF you submit, one dataset row with:

- `url` — the PDF URL you requested.
- `fileName` — file name taken from the URL.
- `pageCount` — total pages in the PDF (null if it could not be opened).
- `pagesProcessed` — pages actually processed (limited by `maxPagesPerPdf`).
- `text` — extracted body text, as plain text or Markdown depending on `outputFormat`.
- `tables` — tables found in the PDF, each with a page number and rows of cells.
- `metadata` — title, author, and creation date, when present and requested.
- `error` — null on success, or a message explaining why this PDF failed.

### Who it's for

Anyone who needs the content of one or many PDFs without opening them by hand: reports, contracts, catalogs, course handouts, invoices. No installation needed — it runs on the Apify platform, you just supply the links.

### How to use

1. **Paste your PDF links** into the "PDF URLs" field. Links must be public and reachable by anyone — not files on your own computer.
2. **Choose your options** (sensible defaults are pre-filled):
   - *Extract tables* — also pull out tables found in the PDF, not just body text.
   - *Output format* — plain text or Markdown (Markdown keeps a readable structure, useful if you paste the result elsewhere).
   - *Max pages per PDF* — how many pages to read at most per file (keeps large PDFs predictable).
   - *Include metadata* — also include the document's title, author, and creation date, when present.
3. **Run the Actor.** Each PDF becomes one row in the dataset, with its extracted text, any tables, and metadata if requested.

### Input example (JSON)

```json
{
  "pdfUrls": [{ "url": "https://example.com/document.pdf" }],
  "extractTables": true,
  "outputFormat": "markdown",
  "maxPagesPerPdf": 50,
  "includeMetadata": true
}
```

### Output example (JSON)

```json
{
  "url": "https://example.com/document.pdf",
  "fileName": "document.pdf",
  "pageCount": 3,
  "pagesProcessed": 3,
  "text": "## Page 1\n\nText of the first page...",
  "tables": [{ "page": 2, "rows": [["Column 1", "Column 2"], ["A", "1"]] }],
  "metadata": { "title": "Sample document", "author": null, "creationDate": "D:20240101000000" },
  "error": null
}
```

### If a PDF cannot be read

The run does not fail. That row simply gets an `error` message explaining why (broken link, file unreachable, not a PDF, too large, password-protected...), and **you are not charged** for it. The rest of the list is still processed normally.

### Pricing

Pay only for PDFs processed successfully — no fixed cost, nothing charged for failed PDFs. Pricing model: **pay-per-event**.

| Event | When it's charged | Price |
| --- | --- | --- |
| `pdf-processed` | a PDF was downloaded and its text extracted successfully | 0.004 USD |

### Limitations

- No OCR: scanned PDFs (image-only) are not supported. Works only on "digital" PDFs where the text is already text inside the file — the large majority of PDFs generated by a computer (Word exports, websites, e-invoices...).
- Password-protected PDFs are not supported.
- Maximum file size: 50 MB.
- Maximum download time per PDF: 30 seconds.
- Maximum pages per PDF is capped by the `maxPagesPerPdf` input.

### FAQ

#### Can I use this on a scanned PDF?

Not yet. The result would be empty or incomplete text, since there is no OCR step.

#### Why does a row have `error` instead of text?

The PDF was unreachable, not actually a PDF, too large, or password-protected. The `error` field explains the exact reason.

#### Am I charged if a PDF fails?

No. You are only charged for PDFs processed successfully.

#### Can I extract only tables and skip the body text?

The Actor always extracts text; turn off *Extract tables* if you only want text, but there is currently no option to extract tables only.

#### Does it work on PDFs stored on my computer?

No. It needs a public URL it can download from — local files are not supported.

#### What is the difference between plain text and Markdown output?

Markdown preserves headings and structure so the result stays readable when pasted elsewhere; plain text is just the raw extracted text.

#### Is it legal to extract text from a PDF I don't own?

Extracting text for your own use or analysis is generally fine. Redistributing the extracted content of a copyrighted PDF (a paid report, a book, a proprietary contract) is a separate question this Actor doesn't answer for you — that's between you and the PDF's owner. This is not legal advice.

#### How does this compare to OCR-based extractors?

This Actor has no OCR step: it only reads text that is already stored as text inside the PDF ("digital" PDFs), not text baked into a scanned image. For that large majority of PDFs, that makes it faster and cheaper than an OCR pipeline. For scanned/image-only PDFs, you need an OCR-based tool instead.

#### Can I use this through the Apify API or an MCP server?

Yes. Like any Apify Actor, you can run it and read results through the standard Apify API, or through the Apify MCP server if you use Claude, Cursor, or another MCP-enabled client.

### Export

Results can be downloaded from the Apify dataset as JSON, CSV, or Excel, or accessed via the Apify API.

# Actor input Schema

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

Public URLs of the PDF files to process. Password-protected or scanned (image-only) PDFs are not supported.

## `extractTables` (type: `boolean`):

Detect and extract tables found in the PDF pages.

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

Format used for the extracted body text in the dataset.

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

Stop processing a PDF after this many pages (keeps cost and run time predictable on large files).

## `includeMetadata` (type: `boolean`):

Include document metadata (title, author, creation date) in the output, when present in the PDF.

## Actor input object example

```json
{
  "pdfUrls": [
    {
      "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
    }
  ],
  "extractTables": true,
  "outputFormat": "markdown",
  "maxPagesPerPdf": 50,
  "includeMetadata": true
}
```

# Actor output Schema

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

All results in the default dataset (JSON, CSV, Excel).

# 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": [
        {
            "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("keyman98/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 = { "pdfUrls": [{ "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf" }] }

# Run the Actor and wait for it to finish
run = client.actor("keyman98/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 '{
  "pdfUrls": [
    {
      "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
    }
  ]
}' |
apify call keyman98/pdf-text-extractor --silent --output-dataset

```

## MCP server setup

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