# PDF to Markdown: clean text from any PDF URL (`ochinimus/pdf-to-markdown`) Actor

Convert PDF links to clean markdown text, page by page, with page count and word count. Works on PDFs with a text layer, up to 10 MB each; scanned images are not OCRed.

- **URL**: https://apify.com/ochinimus/pdf-to-markdown.md
- **Developed by:** [Serghei Ochinca](https://apify.com/ochinimus) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 pdfs

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 to Markdown: clean text from any PDF URL

**Turn PDF links into clean markdown text, page by page, with the page count, the word count and the file size. One JSON item per PDF, ready for search, summarising, RAG pipelines or LLM prompts.**

Paste a list of public PDF URLs. The actor fetches each file once, extracts its text layer and returns it as markdown, with every page under its own heading (`## Page 1`, `## Page 2`, and so on). No file upload, no browser, no setup.

### What you get, per PDF

- **markdown:** the text of the PDF, page by page, each page under a `## Page N` heading.
- **pages**, **words** (words of the text itself, not counting the page headings) and **bytes** (file size).
- **title** from the PDF's metadata, when the file has one.
- **truncated:** `false` when the markdown holds the whole text, `true` when the text was longer than 300,000 characters and was cut.
- **url** (what you supplied), **final_url** (after redirects), `rights`, `source` and `fetched_at`.

### Use cases

- Feed reports, papers, filings or manuals to an LLM or a RAG pipeline as plain text.
- Index or search a set of PDFs without running your own parser.
- Pull the text of a list of PDF links on an Apify schedule and export it as JSON, CSV or Excel.
- Check how long a PDF is (pages, words) before you decide to process it further.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `urls` | array of strings | prefill `["https://bitcoin.org/bitcoin.pdf"]` | Public http or https links to PDF files (up to 50). |

With no input at all, the actor converts that one example PDF, so the default run always returns data.

### How to run it

**In the Apify Console:** open the Actor, paste the PDF links into the Input tab and click Start. The dataset has an **Overview** table (one row per PDF, without the full text) and can be exported as JSON, CSV or Excel; the markdown is in each item.

**Through the API:** one request starts a run and returns the items (replace `YOUR_API_TOKEN` with your Apify API token):

```bash
curl -X POST "https://api.apify.com/v2/acts/ochinimus~pdf-to-markdown/run-sync-get-dataset-items" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://bitcoin.org/bitcoin.pdf"]}'
```

The answer is a JSON array with one item per PDF that converted. See the Apify API docs for asynchronous runs and schedules.

### Sample output

A real result for the example PDF, captured live on 14 Oct 2026. The `markdown` field is shortened for this page; the real one holds the whole text.

```json
{
  "url": "https://bitcoin.org/bitcoin.pdf",
  "final_url": "https://bitcoin.org/bitcoin.pdf",
  "title": null,
  "pages": 9,
  "words": 3559,
  "bytes": 184292,
  "truncated": false,
  "markdown": "## Page 1\n\nBitcoin: A Peer-to-Peer Electronic Cash System\nSatoshi Nakamoto\nsatoshin@gmx.com\nwww.bitcoin.org\nAbstract. A purely peer-to-peer version of electronic cash would allow online\npayments to be sent directly from one party to another without going through a\nfinancial institution. Digital signatures provide part of the solution, but the main\nbenefits are lost if a trusted third party is still required to ...",
  "rights": "fetched once from the URL you supplied, on your request, and not stored; the content belongs to its publisher and you are responsible for your use of it",
  "source": "AgentFeed (x402.ochinimus.app), PDF fetched once from the URL you supplied",
  "fetched_at": "2026-10-14T15:35:50.423Z"
}
```

### Pricing

Pay per event: **$0.01 per PDF converted** (event `pdf`), plus Apify's tiny per-run start fee.

You are charged only for PDFs that come back with text. A link that cannot be fetched, a file that is not a PDF, a private or blocked address, a site whose robots.txt forbids it, a PDF with no text, and any data-source error return nothing and cost nothing.

### Data source and its limits

- **Source: AgentFeed (x402.ochinimus.app).** It fetches the one URL you supply, once, on your request, extracts the text and does not store it.
- **Only PDFs with a text layer convert.** A scanned PDF that is only page images is not OCRed: it returns nothing and costs nothing.
- **Limits per file:** 10 MB, and 10 seconds to fetch. Only http and https links on ports 80 and 443 are fetched, up to 3 redirects.
- **robots.txt is honoured.** If the site forbids automated fetching, the link returns nothing.
- **Private, local and reserved addresses are refused.**
- **Text is cut at 300,000 characters per PDF** (`truncated` tells you).
- Text comes out as the PDF's text layer provides it: multi-column pages, tables and footnotes may not read in order. Images are not described.
- The PDF's content belongs to its publisher, and you are responsible for how you use it.

### Built for agents too

The same data is sold per call to AI agents over the x402 protocol (USDC, no account): [x402.ochinimus.app](https://x402.ochinimus.app), route `/api/pdf-markdown`, OpenAPI at [x402.ochinimus.app/openapi.json](https://x402.ochinimus.app/openapi.json).

Source code: [github.com/seekdaseek/agentfeed-apify](https://github.com/seekdaseek/agentfeed-apify) (MIT), folder `pdf-to-markdown`.

# Actor input Schema

## `urls` (type: `array`):

Links to PDF files, one per line (up to 50). Each must be a public http or https URL that serves a PDF of at most 10 MB with a text layer, and whose site allows automated fetching in its robots.txt. Every PDF converted is one result. A link that cannot be fetched, is not a PDF, or holds no text returns nothing and is not charged.

## Actor input object example

```json
{
  "urls": [
    "https://bitcoin.org/bitcoin.pdf"
  ]
}
```

# Actor output Schema

## `overview` (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 = {
    "urls": [
        "https://bitcoin.org/bitcoin.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ochinimus/pdf-to-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 = { "urls": ["https://bitcoin.org/bitcoin.pdf"] }

# Run the Actor and wait for it to finish
run = client.actor("ochinimus/pdf-to-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 '{
  "urls": [
    "https://bitcoin.org/bitcoin.pdf"
  ]
}' |
apify call ochinimus/pdf-to-markdown --silent --output-dataset

```

## MCP server setup

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