# PDF to Markdown & Text Extractor (tables, metadata) (`despottt/pdf-to-text-markdown`) Actor

Convert PDF URLs into clean Markdown or plain text with headings, lists and tables, plus metadata, page and word counts. Ideal for RAG, LLM pipelines and document search. Flags scanned PDFs that need OCR.

- **URL**: https://apify.com/despottt/pdf-to-text-markdown.md
- **Developed by:** [Luca Despot](https://apify.com/despottt) (community)
- **Categories:** Developer tools, AI
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 documents

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/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 to Markdown & Text Extractor (tables, metadata)

Turn **any PDF link into clean Markdown or plain text**, ready for **LLMs, RAG, vector databases, search or analysis**. Headings, paragraphs, bullet lists and **tables** keep their structure, and you also get the metadata (title, author, dates), page and word counts.

- ✅ **LLM-ready Markdown:** `#` and `##` headings come from font size and weight, hyphenated line breaks are joined, and rotated margin text (like arXiv stamps) is removed.
- ✅ **Tables** become Markdown tables, with the raw cells also returned as JSON.
- ✅ **Per-page output** (`perPage`) for chunking and page citations.
- ✅ **Page ranges** (`1-3,7,10-`) so you only pay for and process what you need.
- ✅ **Honest about scans:** image-only PDFs are marked `needsOcr: true` instead of returning empty or garbled text.
- ✅ **REST API (Standby):** `GET /?pdfUrls=<url>` returns the JSON immediately. You can call it from an agent or pipeline.
- ✅ **Robust:** checks file size, `%PDF` signature and redirects, and retries. A bad URL gives an `error` item and doesn't break the run.

### Use cases

RAG and chatbots over documentation · invoices, reports and papers into a database · grabbing tables from government or financial reports · bulk research-paper ingestion · document search indexes.

### Input

```json
{ "pdfUrls": ["https://arxiv.org/pdf/1706.03762"], "outputFormat": "markdown", "pages": "1-4", "extractTables": true, "perPage": false }
```

### Output (one item per PDF)

```json
{
  "url": "https://arxiv.org/pdf/1706.03762",
  "fileName": "1706.03762",
  "pageCount": 15,
  "pagesExtracted": 4,
  "meta_title": null,
  "meta_author": null,
  "meta_createdAt": "2024-04-10T21:11:43+00:00",
  "tablesFound": 0,
  "wordCount": 1747,
  "needsOcr": false,
  "markdown": "# Attention Is All You Need\n\nAshish Vaswani∗ ...\n\n## Abstract\n\nThe dominant sequence transduction models ...\n\n## 1 Introduction\n...",
  "tables": [],
  "scrapedAt": "2026-10-27T21:40:00+00:00"
}
```

A real sample is in the *Output* tab. With `perPage: true` you also get `pages: [{page, text, markdown, tables, chars}]`.

### API (Standby)

```
GET https://<your-standby-url>/?pdfUrls=https://www.irs.gov/pub/irs-pdf/fw4.pdf&outputFormat=both
Authorization: Bearer <APIFY_TOKEN>
```

### Pricing

Pay per PDF processed. Pages are capped with `maxPagesPerPdf` (default 300), and failed downloads are free.

### Limits and FAQ

- **No OCR.** The Actor reads the PDF's text layer. Scanned documents are flagged with `needsOcr`.
- **Password-protected** PDFs are not supported unless the password is empty.
- Very complex layouts (multi-column magazines) may need light cleanup. Tables without ruling lines are sometimes read as text.
- Only process documents you're allowed to use.
- **Issues and feature requests:** use the *Issues* tab.

# Actor input Schema

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

Direct links to PDF files (http/https). One dataset item per PDF.

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

Markdown keeps structure; ideal for LLMs and RAG.

## `pages` (type: `string`):

Page range, e.g. <code>1-3,7,10-</code>. Empty = all pages.

## `perPage` (type: `boolean`):

Add a <code>pages</code> array with text/markdown/tables for every page (useful for chunking and citations).

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

Detect tables, render them as Markdown tables and return their cells as JSON.

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

0 = no limit.

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

Larger files are skipped with an error item.

## `maxItems` (type: `integer`):

0 = all.

## Actor input object example

```json
{
  "pdfUrls": [
    "https://arxiv.org/pdf/1706.03762",
    "https://www.irs.gov/pub/irs-pdf/fw4.pdf"
  ],
  "outputFormat": "markdown",
  "pages": "",
  "perPage": false,
  "extractTables": true,
  "maxPagesPerPdf": 300,
  "maxFileSizeMb": 50,
  "maxItems": 0
}
```

# Actor output Schema

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

All dataset items produced by the run

# 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://arxiv.org/pdf/1706.03762",
        "https://www.irs.gov/pub/irs-pdf/fw4.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("despottt/pdf-to-text-markdown").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://arxiv.org/pdf/1706.03762",
        "https://www.irs.gov/pub/irs-pdf/fw4.pdf",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("despottt/pdf-to-text-markdown").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://arxiv.org/pdf/1706.03762",
    "https://www.irs.gov/pub/irs-pdf/fw4.pdf"
  ]
}' |
apify call despottt/pdf-to-text-markdown --silent --output-dataset

```

## MCP server setup

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

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/gWGlODSkybjaXl4jm/builds/RWMMefhmLjCuZ3ZaH/openapi.json
