# PDF to Text Markdown Extractor (Digital, No OCR) (`steady-scrape/pdf-text-markdown`) Actor

Extract plain text and Markdown from digital (text-based) PDFs you supply by URL or file. Batch-friendly. No OCR in v1 — scanned/image-only PDFs are reported and not charged.

- **URL**: https://apify.com/steady-scrape/pdf-text-markdown.md
- **Developed by:** [SteadyScrape](https://apify.com/steady-scrape) (community)
- **Categories:** Developer tools, AI, Automation
- **Security:** Limited permissions
- **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 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](#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 Text Markdown Extractor (Digital, No OCR)

**Who it's for:** Teams that need plain text or Markdown from digital (text-based) PDFs in batch — reports, manuals, filings — without an OCR bill.

**What you get:** Per-file text and/or Markdown, page count and basic metadata. Clear statuses for encrypted, empty and image-only/scanned PDFs (those are free).

**Price:** $0.004 per successfully extracted digital PDF + $0.003 once per run, charged only if at least one PDF was extracted. Scanned/image-only, empty, encrypted and failed files are free. 1,000 PDFs in one run = $4.003.

Point the Actor at **direct HTTPS PDF URLs** (or Apify file upload URLs). It downloads each file and extracts text locally with **pypdf**. **No OCR in v1** — scanned/image-only PDFs return `image_only` and are not charged. **No third-party extraction vendor.**

Made by **SteadyScrape (steady-scrape)**.

### What you can do with it

1. **Batch digital PDFs → text.** Feed a list of direct PDF links and get one row per file.
2. **Markdown for docs pipelines.** Optional Markdown with light heading heuristics and page breaks.
3. **Honest scanned handling.** Image-only PDFs are labeled, not silently empty-charged.

### Input

- **pdfUrls** (required): direct `https` links to PDF files.
- **format**: `text` | `markdown` | `both` (default).
- **maxFiles**: soft cap for this run (harder env cap may apply).
- **includeMetadata**: PDF title/author when present.

Example:

```json
{
  "pdfUrls": ["https://www.orimi.com/pdf-test.pdf"],
  "format": "both",
  "maxFiles": 5
}
```

Under **LIMITED_PERMISSIONS** the Actor downloads URLs itself — HTML site crawls / request-queue site walking are out of scope.

### Output

| status | meaning | charged |
|---|---|---|
| `ok` | digital text extracted | yes (`pdf-extracted`) |
| `empty` | no pages/text | no |
| `encrypted` | password-protected | no |
| `image_only` | scanned / no extractable text (no OCR v1) | no |
| `too_large` / `too_many_pages` | over env caps | no |
| `fetch_error` / `invalid` / `skipped` | download/parse/max-charge | no |

### Pricing

**`pdf-extracted` $0.004** per successful digital PDF + **`actor-start` $0.003** once per run (only when at least one PDF was extracted), plus Apify's standard tiny per-run start event. Non-`ok` files are free. Set **Maximum cost per run** in Apify options.

### Use cases

- **RAG and search indexes:** split the Markdown on headings and embed the chunks, keeping `source` for citations.
- **Document archives:** extract text and page counts from reports, manuals, filings and specs in batch.
- **OCR triage:** find which files in a batch are scanned (`image_only`) before paying for OCR elsewhere.

### FAQ

**Does it read scanned PDFs?** No. There is no OCR in this version. Scanned or image-only files return `image_only` and are free.
**How good is the Markdown?** Headings are detected heuristically (PDFs don't reliably mark them), and tables or multi-column layouts may come out as plain reading-order text.
**Can it find PDFs on a website for me?** No. Give direct PDF links, or upload files in the Console.
**Password-protected files?** They return `encrypted` and are free.

**Worked cost example:** 200 digital PDFs in one run = 200 × $0.004 + $0.003 ≈ **$0.80**.

### Integrations

- **Apify API and SDKs:** call it from Python, JavaScript or any HTTP client and read results from the run's dataset.
- **Schedules and webhooks:** run it on a schedule in Apify and trigger your own endpoint when a run finishes.
- **Make, Zapier, n8n:** use Apify's own integrations for these tools to start runs and pass dataset rows on.
- **AI agents (MCP):** agents can call it through the Apify MCP server.

### Limits (v1)

- No OCR. No vendor API.
- Env caps: max files/run, max pages/file, max bytes/file.
- Direct PDF URLs only (not “search this site for PDFs”).

### More from SteadyScrape

- [AI Page to JSON: URL + Schema Extractor](https://apify.com/steady-scrape/ai-page-to-json): turn public web pages into validated JSON from your own field list or JSON Schema; failed, blocked and JS-only pages are free.
- [YouTube Transcript API: Captions with Timestamps](https://apify.com/steady-scrape/youtube-transcripts): existing YouTube captions with timestamped segments for up to 200 videos per run; videos without captions are free.
- [Google News Monitor: Brand & Topic Alerts, Only New Articles](https://apify.com/steady-scrape/google-news-monitor): scheduled Google News alerts for brands, companies and topics that return only articles new since the last run.

See every **SteadyScrape (steady-scrape)** Actor on [our Apify profile](https://apify.com/steady-scrape).

# Actor input Schema

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

Direct HTTPS links to PDF files. Under LIMITED_PERMISSIONS the Actor downloads each URL itself (no request queue crawl of HTML sites). Prefer stable direct .pdf links.

## `pdfFileUrls` (type: `array`):

Alternative field for Apify console file-upload links (same as pdfUrls). Either field works.

## `format` (type: `string`):

Output format: text, markdown, or both.

## `maxFiles` (type: `integer`):

Maximum PDF files processed this run.

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

Title, author, page count when present in the PDF info dict.

## `pdfFiles` (type: `array`):

Upload digital PDFs in the Apify Console. Prefer this for private files. Same charging rules as URLs.

## Actor input object example

```json
{
  "pdfUrls": [],
  "format": "both",
  "maxFiles": 20,
  "includeMetadata": true
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `summary` (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 = {
    "pdfUrls": []
};

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

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

```

## MCP server setup

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