# PDF Text Extractor - Tables, Markdown, RAG Chunks & OCR (`scrapewise/pdf-text-extractor`) Actor

Extract text from PDF files by URL: clean text, Markdown with headings, tables as rows, metadata, bookmarks, one row per document, per page or per RAG chunk. Optional OCR for scanned PDFs in 20 languages. Google Drive, Dropbox and arXiv links work. Failed files are free.

- **URL**: https://apify.com/scrapewise/pdf-text-extractor.md
- **Developed by:** [Scrapewise Data](https://apify.com/scrapewise) (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 $2.55 / 1,000 pdf extracted (up to 20 pages)s

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Extractor

PDF Text Extractor turns **PDF files into clean text, Markdown, tables and RAG chunks**, from a list of links or from files in your Apify storage, **without a browser, an API key or any setup**. Each PDF comes back with its full text, a Markdown version with headings and lists rebuilt from the layout, **every table as rows and columns**, the document metadata (title, author, dates, page count) and its bookmarks. Pick one row per document, per page or per chunk ready for a vector database. Scanned PDFs can be read with **optional OCR in 20 languages**.

**US$ 3 per 1,000 PDFs of up to 20 pages. Longer files: US$ 0.001846 per extra 10 pages. OCR: US$ 0.001154 per scanned page, only when you turn it on. No monthly fee, no start fee. Failed files are free.**

### At a glance

- **Input:** PDF links (direct, arXiv, Google Drive, Dropbox, GitHub), an uploaded file, or the PDFs in one of your key-value stores
- **Text:** in reading order, two-column papers included
- **Markdown:** headings from font sizes, joined paragraphs, bullet lists, tables in place, running headers and footers removed
- **Tables:** each one as a list of rows, plus a Markdown table, with its page number
- **RAG chunks:** paragraph-aware chunks with overlap, section path, page range and token estimate
- **Metadata:** title (from the file or, when missing, from the first page), author, subject, keywords, created and modified dates, PDF version, page count, bookmarks
- **OCR:** optional, only on pages without a text layer, 20 languages, charged per page actually read
- **Error rows:** free, with an `errorCode` that says what to fix

### One real row

Input `https://arxiv.org/pdf/2005.11401` in Document mode (collected on 2026-10-03; `text`, `markdown`, `tables` and `outline` trimmed here):

```json
{
  "type": "document",
  "url": "https://arxiv.org/pdf/2005.11401",
  "fileName": "2005.11401v4.pdf",
  "title": "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks",
  "titleSource": "content",
  "author": null,
  "creator": "LaTeX with hyperref",
  "producer": "pdfTeX-1.40.21",
  "createdAt": "2021-04-13T00:48:38Z",
  "modifiedAt": "2021-04-13T00:48:38Z",
  "pdfVersion": "1.5",
  "pageCount": 19,
  "pagesExtracted": 19,
  "fileSizeBytes": 885323,
  "wordCount": 9868,
  "charCount": 69137,
  "text": "Retrieval-Augmented Generation for\nKnowledge-Intensive NLP Tasks\n...",
  "markdown": "# Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks\n\n...\n\n## Abstract\n\nLarge pre-trained language models have been shown to store factual knowledge in their parameters, and achieve state-of-the-art results when fine-tuned on downstream NLP tasks. ...",
  "tables": [
    {
      "page": 8,
      "index": 1,
      "rows": 6,
      "columns": 3,
      "data": [
        ["", "Factuality", "Specificity"],
        ["BART better", "7.1%", "16.8%"],
        ["RAG better", "42.7%", "37.4%"],
        ["Both good", "11.7%", "11.8%"],
        ["Both poor", "17.7%", "6.9%"],
        ["No majority", "20.8%", "20.1%"]
      ],
      "markdown": "|  | Factuality | Specificity |\n| --- | --- | --- |\n| BART better | 7.1% | 16.8% |\n..."
    }
  ],
  "tableCount": 7,
  "outline": [
    { "level": 1, "title": "1 Introduction", "page": 1 },
    { "level": 1, "title": "2 Methods", "page": 2 },
    { "level": 2, "title": "2.1 Models", "page": 3 }
  ],
  "ocrPages": 0,
  "scannedPages": 0,
  "extraPageBlocks": 0,
  "warning": null,
  "scrapedAt": "2026-10-03T13:24:18Z",
  "errorCode": null
}
```

The same file in RAG chunk mode gives 88 rows like this one:

```json
{
  "type": "chunk",
  "url": "https://arxiv.org/pdf/2005.11401",
  "title": "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks",
  "chunkIndex": 9,
  "chunkCount": 88,
  "text": "For FEVER [56] fact verification, we achieve results within 4.3% of state-of-the-art pipeline models which use strong retrieval supervision.\n\n...\n\n## 2 Methods\n\nWe explore RAG models, which use the input sequence x to retrieve text documents z and use them as additional context when generating the target sequence y. ...",
  "section": "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks > 1 Introduction",
  "pageStart": 2,
  "pageEnd": 3,
  "charCount": 1095,
  "tokenEstimate": 274,
  "overlapChars": 140
}
```

### What you can do with it

- **RAG and AI agents:** turn reports, papers, manuals and contracts into chunks with section and page numbers, and load them into Pinecone, Weaviate, Qdrant, Chroma or pgvector. The page range of each chunk lets your app cite the source.
- **Tables to spreadsheets:** pull the tables out of financial reports, government statistics and research papers as rows you can open in Excel or load into a database.
- **Search and archives:** make a folder of PDFs searchable, with title, author and dates as filters. Scanned files included, with OCR.
- **Document pipelines:** run it after a crawler that collects PDF links, on a schedule, or from Make, n8n and Zapier, and get JSON back.

### Input

| Field | What it does |
|---|---|
| `urls` | PDF links. Direct `.pdf` links, `arxiv.org/abs/...`, Google Drive and Dropbox share links, GitHub file links |
| `pdfFile` | One PDF uploaded in the Console |
| `storeId`, `keyValueStoreKeys` | Read PDFs saved in one of your key-value stores (pick it in the Console, or pass its ID): every `.pdf` record, or only the keys you list |
| `outputMode` | `document` (default), `pages` or `chunks` |
| `includeMarkdown` | Add the `markdown` field. Default on |
| `extractTables` | Find tables. Default on. Turning it off makes long files many times faster |
| `includePages` | In Document mode, add a `pages` list with the text of each page |
| `removeHeadersFooters` | Leave running headers, footers and page numbers out of Markdown and chunks. Default on |
| `chunkSize`, `chunkOverlap` | Characters per chunk (default 1000) and repeated from the previous chunk (default 150) |
| `pageRange` | For example `1-5, 8, 12-` |
| `maxPagesPerPdf` | Stop after this many pages of each file |
| `pdfPassword` | Password for protected PDFs |
| `ocr` | `off` (default), `auto` (only pages without text) or `force` (every page) |
| `ocrLanguage` | `eng`, `deu`, `fra`, `spa`, `por`, `ita`, `nld`, `pol`, `swe`, `rus`, `ukr`, `tur`, `ara`, `hin`, `jpn`, `kor`, `chi_sim`, `chi_tra`, `vie`, `ind`. Join two with `+` |
| `ocrMaxPagesPerPdf` | OCR stops after this many pages of each file. Default 50 |
| `maxPdfs`, `maxPdfSizeMb` | Limits per run and per file (default 100 MB) |
| `proxyConfiguration` | Optional. Blocked downloads are retried once through Apify datacenter proxy on their own |

Chunks for a vector database, from two reports:

```json
{
  "urls": [
    "https://nvlpubs.nist.gov/nistpubs/CSWP/NIST.CSWP.29.pdf",
    "https://www.irs.gov/pub/irs-pdf/p17.pdf"
  ],
  "outputMode": "chunks",
  "chunkSize": 1200,
  "chunkOverlap": 200
}
```

A scanned document, first 10 pages, with OCR:

```json
{
  "urls": ["https://www.archives.gov/files/research/jfk/releases/docid-32204484.pdf"],
  "ocr": "auto",
  "ocrLanguage": "eng",
  "pageRange": "1-10"
}
```

### Output fields

**Document rows:** `type`, `url`, `fileName`, `title`, `titleSource` (`metadata` or `content`), `author`, `subject`, `keywords`, `creator`, `producer`, `createdAt`, `modifiedAt`, `pdfVersion`, `pageCount`, `pagesExtracted`, `fileSizeBytes`, `wordCount`, `charCount`, `text`, `markdown`, `tables` (each with `page`, `index`, `rows`, `columns`, `data`, `markdown`), `tableCount`, `outline` (bookmarks with `level`, `title`, `page`), `pages` (optional), `ocrPages`, `scannedPages`, `extraPageBlocks`, `warning`, `scrapedAt`.

**Page rows:** `type`, `url`, `fileName`, `title`, `pageCount`, `page`, `text`, `markdown`, `tables`, `tableCount`, `wordCount`, `charCount`, `ocr`, `scanned`, `author`, `createdAt`, `warning`, `scrapedAt`.

**Chunk rows:** `type`, `url`, `fileName`, `title`, `pageCount`, `chunkIndex`, `chunkCount`, `text`, `section`, `pageStart`, `pageEnd`, `charCount`, `tokenEstimate`, `overlapChars`, `author`, `createdAt`, `scrapedAt`.

#### Error codes (never charged)

| errorCode | Meaning |
|---|---|
| `NOT_FOUND` | The link answers 404 or the host does not exist |
| `NOT_A_PDF` | The link returns a web page or another file type. Use the direct link to the file |
| `BLOCKED` | The server refused the download (login needed, or it blocks cloud servers) |
| `DOWNLOAD_FAILED` | The download broke after 3 tries |
| `FILE_TOO_LARGE` | Above `maxPdfSizeMb` |
| `PASSWORD_REQUIRED`, `WRONG_PASSWORD` | Set or fix `pdfPassword` |
| `CORRUPTED_PDF`, `PDF_TOO_COMPLEX` | The file is damaged, or needs more memory than the run has |
| `NO_TEXT` | Scanned or blank PDF and OCR is off (or OCR found nothing). The row still carries the title, page count and how many pages are scans |
| `DUPLICATE` | Same file as another link of the run |
| `STORE_NOT_FOUND` | The key-value store could not be opened. Pick it in the `storeId` field |
| `FREE_PLAN_LIMIT` | The Free plan page limit of the run was reached before this file |
| `INVALID_URL`, `INVALID_INPUT` | A link or option the Actor does not understand |
| `EXTRACTION_TIMEOUT`, `NOT_REACHED`, `UNEXPECTED` | Time ran out, the spending limit of the run was reached, or something unforeseen broke. Only that file is lost |

### Pricing

| Event | Price |
|---|---|
| PDF extracted, up to 20 pages | US$ 0.003 (US$ 3 per 1,000 PDFs; US$ 2.55 on Gold and above) |
| Each extra 10 pages after page 20 | US$ 0.001846 |
| OCR page (only with `ocr` on, only pages OCR turned into text) | US$ 0.001154 |

Examples: a 12-page paper costs US$ 0.003. A 60-page report costs US$ 0.003 + 4 x 0.001846 = about US$ 0.0104. An 8-page scan with OCR costs US$ 0.003 + 8 x 0.001154 = about US$ 0.0122. Tables, Markdown, chunks and metadata are included, and the three output modes cost the same. There is no start fee and no Apify compute bill on top. Error rows are free. On the Apify Free plan a run reads up to 300 pages and 20 OCR pages.

### How to run it

- **Console:** paste the links and press Start. Download the dataset as JSON, CSV or Excel.
- **API:** `POST https://api.apify.com/v2/acts/<actor>/run-sync-get-dataset-items?token=<token>` with the input JSON as the body returns the rows in the same call.
- **Schedules and integrations:** run it on a schedule, or from Make, n8n, Zapier and LangChain through the Apify integrations, and send the dataset to your database or vector store.
- **From another Actor:** save PDFs to a key-value store and pass its ID in `storeId`.

### FAQ

**How good is the Markdown?** Headings come from font sizes and bold lines, so documents that set headings in a larger or bolder font come out well (papers, reports, manuals). A PDF has no real structure inside, so slides, forms and magazine layouts come out as plain paragraphs. Text inside charts and diagrams is kept as text.

**Which tables are found?** Tables drawn with lines and research-paper tables with horizontal rules. Tables with no lines at all (columns separated only by spaces) are returned as text, not as rows. Merged cells come back as empty cells next to the value.

**Does OCR read tables or handwriting?** No. OCR pages give text only, and it is tuned for printed or typed pages. Quality follows the scan: a clean 300 dpi scan reads well, an old fax does not.

**Do I pay for a PDF that fails?** No. A dead link, a web page instead of a file, a wrong password and a scan with OCR off are free rows that say what happened.

**How fast is it?** A 19-page paper and a 32-page report together finish in about 8 seconds. At the default 1 GB of memory, pages without tables take a few milliseconds each, pages with tables 1 to 2 seconds and OCR about 7 seconds per page. With 4 GB, table pages take about half a second and OCR about 2 seconds per page.

**Can I control the cost of long files?** Yes: `pageRange`, `maxPagesPerPdf` and `ocrMaxPagesPerPdf` cap each file, and the maximum charge you set for the run is respected (the Actor reads only the pages the limit covers).

### Limitations

- Text comes in the order stored in the file. That is the reading order in most PDFs, but some design tools store text boxes out of order.
- Formulas come out as loose symbols. Images and charts are not described.
- Right-to-left scripts and vertical text are extracted but Markdown structure is best on left-to-right text.
- Rows above the dataset size limit (very long books) keep the first 200,000 characters in the row and link to the full text and Markdown files (`textUrl`, `markdownUrl`).
- Files above 300 MB are not read. Long files with table detection on take time at the default 1 GB of memory (a 142-page tax guide with 65 tables: about 4 and a half minutes). Run with 4 GB to read them about 4 times faster (the same guide: about 1 minute), or turn off `extractTables`. The price is the same.
- Free plan: up to 300 pages and 20 OCR pages per run.

This Actor reads only files you point it to. Make sure you have the right to process them. If something breaks, open an issue on the Actor page with the link of the PDF.

### Changelog

- **2026-10-03, 0.1:** first release. Links, upload and key-value store input; text, Markdown, tables, metadata and bookmarks; document, page and RAG chunk rows; page ranges and passwords; optional OCR in 20 languages; free error rows.

# Actor input Schema

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

Links to PDF files, one per line. Direct .pdf links, arXiv pages (arxiv.org/abs/...), Google Drive and Dropbox share links and GitHub file links all work (for example https://arxiv.org/pdf/2005.11401). Each PDF gives one row, or one row per page or chunk depending on 'One row per'. Links that are not PDFs or cannot be downloaded return a free error row. Also accepts 'pdfUrls', 'startUrls' or 'url'.

## `pdfFile` (type: `string`):

Upload one PDF from your computer instead of (or in addition to) the links above. It is stored in your account and read like any other link. Through the API, pass a public URL of the file.

## `storeId` (type: `string`):

Pick one of your Apify key-value stores (through the API, pass its ID). Every record whose key ends in .pdf is read, or only the keys listed below. Handy when another Actor or your own code already saved the files there, so nothing has to be public.

## `keyValueStoreKeys` (type: `array`):

Optional. Record keys to read from the key-value store above, as a JSON list (for example \["invoice-001.pdf"]). Leave empty to read every record whose key ends in .pdf. Ignored when no store is picked.

## `outputMode` (type: `string`):

Document (default): one row per PDF with the full text, Markdown, tables, metadata and bookmarks. Page: one row per page, with its number, text and Markdown. RAG chunk: the Markdown split into overlapping chunks with the section heading and page numbers of each chunk, ready to embed in a vector database. The price is the same in the three modes.

## `includeMarkdown` (type: `boolean`):

On by default: adds a 'markdown' field with headings rebuilt from font sizes, paragraphs joined across line breaks, bullet lists, tables as Markdown tables and repeated page headers and footers removed. Ideal for LLM prompts. Turn off to keep only the plain 'text' field. RAG chunks are always built from the Markdown.

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

On by default: finds tables and returns each one in 'tables' as rows and columns (and as a Markdown table inside the Markdown). Works on tables drawn with lines and on research-paper tables with horizontal rules. Turn off to read very long PDFs (hundreds of pages) many times faster when you only need the text. No effect on price.

## `includePages` (type: `boolean`):

In Document mode, also adds a 'pages' list with the page number, text and Markdown of each page, so you keep one row per PDF and can still cite pages. Off by default. Ignored in Page and RAG chunk modes, which already split by page.

## `removeHeadersFooters` (type: `boolean`):

On by default: leaves running headers, footers and page numbers that repeat on most pages out of the Markdown and the chunks, so they do not break paragraphs or pollute embeddings. The plain 'text' field always keeps everything as printed.

## `chunkSize` (type: `integer`):

RAG chunk mode only: maximum characters per chunk before the overlap (200 to 20,000, default 1,000, about 250 tokens at 4 characters per token). Chunks break at paragraphs, headings and tables when possible, never in the middle of a word. Larger chunks mean fewer rows; the price per PDF does not change.

## `chunkOverlap` (type: `integer`):

RAG chunk mode only: how many characters from the end of each chunk are repeated at the start of the next one (default 150, at most half the chunk size), so a sentence cut at a boundary still appears whole in one chunk. 0 for no overlap.

## `pageRange` (type: `string`):

Read only these pages, for example 1-5, 8, 12- (12- means page 12 to the end). Pages are numbered from 1 and the range applies to every PDF in the run. Leave empty to read every page. Fewer pages means fewer '10 extra pages' charges on long files.

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

Stop after this many pages of each PDF (for example 50). Every row still says how many pages the file has. Leave empty to read all pages. Caps your cost on very long files: the first 20 pages are in the PDF price and each started block of 10 more is an extra event.

## `pdfPassword` (type: `string`):

Password for protected PDFs. Tried on every PDF in the run that asks for one; PDFs without a password ignore it. A wrong password returns a free error row. Stored encrypted and never written to the output.

## `ocr` (type: `string`):

Scanned PDFs are images and have no text to copy. Off (default): scanned pages come back empty. Auto: OCR reads only the pages without a text layer and charges the 'OCR page' event only for those. Force: OCR every page, for PDFs whose text layer is broken or garbled. Pages with normal text are never sent to OCR in Auto.

## `ocrLanguage` (type: `string`):

Language of the scanned text, which helps OCR read accents and non-Latin scripts (default English). Through the API you can join two with a plus sign, for example eng+deu, for documents that mix languages. Ignored when OCR is off.

## `ocrMaxPagesPerPdf` (type: `integer`):

OCR stops after this many scanned pages of each PDF (1 to 5,000, default 50); the row carries a warning with how many scanned pages were left unread. Caps your OCR cost on long scanned files. Pages with a normal text layer do not count.

## `maxPdfs` (type: `integer`):

Read at most this many PDFs in the run, across links, upload and key-value store (for example 100). Leave empty for all. Useful to test a large list or a store before reading everything.

## `maxPdfSizeMb` (type: `integer`):

PDFs larger than this many megabytes (1 to 300, default 100) are not downloaded: they get a free FILE_TOO_LARGE row. Raise it for large scanned books; lower it to skip heavy files.

## `proxyConfiguration` (type: `object`):

Not needed for most files: downloads go straight to the server, and a blocked download is retried once through Apify datacenter proxy. Turn the proxy on here only to send every download through it, for example for a site that blocks cloud servers. Off by default.

## Actor input object example

```json
{
  "urls": [
    "https://arxiv.org/pdf/2005.11401",
    "https://nvlpubs.nist.gov/nistpubs/CSWP/NIST.CSWP.29.pdf"
  ],
  "outputMode": "document",
  "includeMarkdown": true,
  "extractTables": true,
  "includePages": false,
  "removeHeadersFooters": true,
  "chunkSize": 1000,
  "chunkOverlap": 150,
  "ocr": "off",
  "ocrLanguage": "eng",
  "ocrMaxPagesPerPdf": 50,
  "maxPdfSizeMb": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

## `resultsCsv` (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://arxiv.org/pdf/2005.11401",
        "https://nvlpubs.nist.gov/nistpubs/CSWP/NIST.CSWP.29.pdf"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapewise/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 = {
    "urls": [
        "https://arxiv.org/pdf/2005.11401",
        "https://nvlpubs.nist.gov/nistpubs/CSWP/NIST.CSWP.29.pdf",
    ],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapewise/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 '{
  "urls": [
    "https://arxiv.org/pdf/2005.11401",
    "https://nvlpubs.nist.gov/nistpubs/CSWP/NIST.CSWP.29.pdf"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call scrapewise/pdf-text-extractor --silent --output-dataset

```

## MCP server setup

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