Skip to content
MCPBytes
AvailableDocuments

PDF Text Extraction

Give your agent the text of any PDF.

  • API
  • MCP
1 credit per 25 pagesMCP tool: extract_pdf
Exampleextract_pdf

Before

paper.pdf12 pages

After

48,211 characters
  • text.txt
  • pages.json
Example output. Every page starts with a --- Page N --- marker.

What it does

PDFs as plain text, page by page.

Send a PDF and MCPBytes returns its text with a marker before every page. Agents get the beginning inline and read on in chunks, so a long document never floods the context. pages.json locates every page in the text and carries the document's metadata.

Input format
.pdf (not password-protected)
Pages
Up to 500 per job; continue with start_page
Outputs
text.txt with page markers, pages.json with page offsets and metadata
Reading
First bytes inline over MCP; the rest in chunks of up to 100,000 bytes
Scanned pages
No OCR: pages without a text layer come back empty

How it works

One PDF in, readable text out.

  1. 01

    Send a PDF

    Send the file in the request, give a public https URL, or let your agent upload it through MCP.

  2. 02

    The job runs

    Text extraction takes a few seconds. Poll the job, or let the MCP tool wait for the result.

  3. 03

    Read the text

    Agents get the beginning inline and read on with read_job_file. Or download text.txt. Results are kept for 24 hours.

Pricing

Pay per job, in credits.

Costs 1 credit per 25 pages (at least 1). The price follows the size of the input, and a job that fails costs nothing.

Pricing details

Start free

Available now

100free credits when you sign up

  • Every MCPBytes tool, one credit balance
  • Sign in with GitHub or email, no card required
  • Charged per job, by the size of the input
  • A job that fails costs nothing

Price per job

PDF Text

Per 25 pages
1 credit

Limits

PDF size
Up to 500 pages and 20 MB
At a time
1 job
Uploads
Up to 50 MB
Results
Kept for 24 hours

API

Call it from code or from an agent.

The same tool runs over the REST API and as MCP tools. One API key works for both.

Start a job

Send the file as the request body. Options such as start_page go in the query string.

# The PDF is the request body
curl "https://api.mcpbytes.com/v1/tools/extract_pdf/jobs?filename=paper.pdf" \
  -H "Authorization: Bearer $MCPBYTES_API_KEY" \
  --data-binary @paper.pdf
Response202 Accepted
{
  "id": "j_7c2m9x4f1q8w3e6r5t0y2u7i9o",
  "tool": "extract_pdf",
  "status": "queued",
  "input": {
    "name": "paper.pdf",
    "bytes": 1204331,
    "source": "body"
  },
  "options": {
    "start_page": 1,
    "max_pages": 500
  },
  "error": null,
  "result": null,
  "compute_ms": null,
  "credits_charged": null,
  "created_at": "2026-09-19T12:00:00.000Z",
  "started_at": null,
  "finished_at": null,
  "outputs_expire_at": "2026-09-20T12:00:00.000Z"
}

Get the result

Poll the job until it succeeds or fails, then download the files.

Requestread.sh
# JOB_ID is the id from the create response
curl https://api.mcpbytes.com/v1/jobs/$JOB_ID \
  -H "Authorization: Bearer $MCPBYTES_API_KEY"

# Read the text, 20,000 bytes at a time
curl "https://api.mcpbytes.com/v1/jobs/$JOB_ID/files/text.txt?offset=0" \
  -H "Authorization: Bearer $MCPBYTES_API_KEY"
Response200 OK · trimmed
{
  "id": "j_7c2m9x4f1q8w3e6r5t0y2u7i9o",
  "tool": "extract_pdf",
  "status": "succeeded",
  "result": {
    "files": [
      {
        "name": "pages.json",
        "bytes": 1842,
        "sha256": "",
        "url": "https://api.mcpbytes.com/blob/dl/j_7c2m9x4f1q8w3e6r5t0y2u7i9o/pages.json?t=…"
      },
      {
        "name": "text.txt",
        "bytes": 48903,
        "sha256": "",
        "url": "https://api.mcpbytes.com/blob/dl/j_7c2m9x4f1q8w3e6r5t0y2u7i9o/text.txt?t=…"
      }
    ],
    "pages": 12,
    "first_page": 1,
    "last_page": 12,
    "empty_pages": 0,
    "chars": 48211,
    "notes": []
  },
  "compute_ms": 1310,
  "credits_charged": 1,
  "finished_at": "2026-09-19T12:00:03.000Z",
  "outputs_expire_at": "2026-09-20T12:00:00.000Z"
}

MCP

Connect once. Your agent calls extract_pdf and gets the download URLs back. This endpoint has only this tool's family, which keeps your agent's tool list short; one endpoint has every tool.

Claude Code

The first form signs in through the browser when you run /mcp. In .mcp.json (project root), ${MCPBYTES_API_KEY} is read from the environment when Claude Code connects. Claude Code docs

claude mcp add --transport http mcpbytes \
  https://api.mcpbytes.com/mcp/pdf
Codex (CLI, IDE extension, ChatGPT desktop)

Codex reads the key from the environment variable each time it connects. The same table can go in ~/.codex/config.toml by hand; codex mcp add has no --header flag. Codex docs

codex mcp add mcpbytes --url https://api.mcpbytes.com/mcp/pdf \
  --bearer-token-env-var MCPBYTES_API_KEY
Cursor

~/.cursor/mcp.json (all projects) or .cursor/mcp.json (one project). Remote servers take no "type". Without the header, the client signs you in with GitHub or an email link (OAuth). Cursor docs

~/.cursor/mcp.json
{
  "mcpServers": {
    "mcpbytes": {
      "url": "https://api.mcpbytes.com/mcp/pdf",
      "headers": {
        "Authorization": "Bearer ${env:MCPBYTES_API_KEY}"
      }
    }
  }
}
VS Code (GitHub Copilot)

.vscode/mcp.json, or "MCP: Open User Configuration". The top-level key is "servers". VS Code asks for the key once and stores it securely. Without the header, the client signs you in with GitHub or an email link (OAuth). VS Code docs

.vscode/mcp.json
{
  "inputs": [
    {
      "type": "promptString",
      "id": "mcpbytes-api-key",
      "description": "MCPBytes API key (mcpb_...)",
      "password": true
    }
  ],
  "servers": {
    "mcpbytes": {
      "type": "http",
      "url": "https://api.mcpbytes.com/mcp/pdf",
      "headers": {
        "Authorization": "Bearer ${input:mcpbytes-api-key}"
      }
    }
  }
}
Gemini CLI

~/.gemini/settings.json or .gemini/settings.json. The key is "httpUrl" ("url" means the older SSE transport). Without the header, the client signs you in with GitHub or an email link (OAuth). Then sign in with /mcp auth. Gemini CLI docs

~/.gemini/settings.json
{
  "mcpServers": {
    "mcpbytes": {
      "httpUrl": "https://api.mcpbytes.com/mcp/pdf",
      "headers": {
        "Authorization": "Bearer ${MCPBYTES_API_KEY}"
      }
    }
  }
}
OpenCode

opencode.json or ~/.config/opencode/opencode.json. Variables are written {env:NAME}. "oauth": false makes a wrong key an error instead of a sign-in; remove it and the headers to sign in with OAuth. OpenCode docs

opencode.json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "mcpbytes": {
      "type": "remote",
      "url": "https://api.mcpbytes.com/mcp/pdf",
      "enabled": true,
      "oauth": false,
      "headers": {
        "Authorization": "Bearer {env:MCPBYTES_API_KEY}"
      }
    }
  }
}
oh-my-pi (omp)

.omp/mcp.json (project) or ~/.omp/agent/mcp.json (user). Without the header, the client signs you in with GitHub or an email link (OAuth). Then run /mcp reauth mcpbytes in a session. omp also picks up servers already set up for Claude Code, Cursor, VS Code, Gemini CLI and Codex. oh-my-pi docs

.omp/mcp.json
{
  "mcpServers": {
    "mcpbytes": {
      "type": "http",
      "url": "https://api.mcpbytes.com/mcp/pdf",
      "headers": {
        "Authorization": "Bearer ${MCPBYTES_API_KEY}"
      }
    }
  }
}
pi

pi has no MCP support, by design: its author recommends command-line tools described by a skill. Install the MCPBytes skill (it drives the REST API with a small script), or add MCP with the pi-mcp-adapter extension. pi docs

mkdir -p ~/.pi/agent/skills/mcpbytes
curl -fsSL https://mcpbytes.com/skills/mcpbytes/mcpbytes.tar.gz \
  | tar -xz -C ~/.pi/agent/skills/mcpbytes
DeepSeek

DeepSeek's API does not run MCP servers itself: the agent you use DeepSeek models in does (Claude Code, OpenCode, oh-my-pi, Cline: use that agent's entry here). DeepSeek's own agent, DeepSeek Harness (dsh, a developer preview), takes this patch in ~/.dsh/cordis.patch.yml; it supports headers, not OAuth. DeepSeek docs

~/.dsh/cordis.patch.yml
- insert:
    - id: mcp-mcpbytes
      name: '@deepseek-ai/dsh-mcp-client'
      config:
        serverName: mcpbytes
        transport: streamable-http
        url: https://api.mcpbytes.com/mcp/pdf
        headers:
          Authorization: !!js '`Bearer ${process.env.MCPBYTES_API_KEY}`'
Windsurf

~/.codeium/windsurf/mcp_config.json. Without the header, the client signs you in with GitHub or an email link (OAuth). Cascade allows 100 tools in total, so a single-family endpoint helps. Windsurf docs

~/.codeium/windsurf/mcp_config.json
{
  "mcpServers": {
    "mcpbytes": {
      "serverUrl": "https://api.mcpbytes.com/mcp/pdf",
      "headers": {
        "Authorization": "Bearer ${env:MCPBYTES_API_KEY}"
      }
    }
  }
}
Goose

~/.config/goose/config.yaml (Windows: %APPDATA%\Block\goose\config\config.yaml), or "goose configure" > Add Extension > Remote Extension (Streamable HTTP). The key is "uri". Without the header, the client signs you in with GitHub or an email link (OAuth). Goose docs

~/.config/goose/config.yaml
extensions:
  mcpbytes:
    type: streamable_http
    name: mcpbytes
    enabled: true
    uri: "https://api.mcpbytes.com/mcp/pdf"
    headers:
      Authorization: "Bearer ${MCPBYTES_API_KEY}"
    env_keys:
      - MCPBYTES_API_KEY
    timeout: 300
Zed

settings.json, or Settings > AI > MCP Servers > Add Server > Add Remote Server. Zed's settings cannot read environment variables, so sign in instead of storing a key: with no Authorization header, Zed starts the OAuth sign-in. Zed docs

settings.json
{
  "context_servers": {
    "mcpbytes": {
      "url": "https://api.mcpbytes.com/mcp/pdf"
    }
  }
}
Claude (desktop and web) and ChatGPT

These add remote servers as connectors and sign in with OAuth; there is no field for an API key. Claude: Customize > Connectors > Add custom connector. ChatGPT: turn on Developer mode in Settings, then create an app. Enter this URL: Claude docs

Connector URL
https://api.mcpbytes.com/mcp/pdf
Any client that only starts local (stdio) servers

mcp-remote runs locally and forwards to the endpoint. The header is written without a space and takes its value from "env", because some clients on Windows break arguments that contain spaces. Use it for Cline too. Without the --header arguments and "env", mcp-remote signs you in with OAuth. Any client that only starts local docs

mcp-remote
{
  "mcpServers": {
    "mcpbytes": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://api.mcpbytes.com/mcp/pdf",
        "--transport",
        "http-only",
        "--header",
        "Authorization:${MCPBYTES_AUTH}"
      ],
      "env": {
        "MCPBYTES_AUTH": "Bearer <your key>"
      }
    }
  }
}
MCP tools
extract_pdf
Starts a job that extracts the text of a PDF, page by page, and returns the beginning of it; continue with read_job_file (text.txt). pages.json has per-page offsets and the document metadata. Scanned pages have no text (no OCR). Give upload_id (from create_upload) or a public https url. Waits up to wait_seconds for the result. Costs 1 credit per 25 pages (at least 1).
create_upload
Returns a one-hour URL to PUT a local file to; then call the tool with the upload_id.
get_job
Status and results of a job; waits up to wait_seconds for it to finish.
list_jobs
Your most recent jobs, newest first.
read_job_file
Reads a text output (.txt .json .md .csv) of a succeeded job in chunks.

Use cases

Where it fits.

  • Research agents

    Let an agent read papers and reports it finds, without a PDF library in its sandbox.

  • Long documents

    Read a 300-page manual chunk by chunk, or jump to a page with the offsets in pages.json.

  • RAG ingestion

    Turn PDFs into page-marked text for chunking and embedding.

  • Contracts and filings

    Pull the text of a filing to search, compare or summarize it.

  • Link following

    Give the URL of a PDF and get its text back. The file is fetched for you.

  • Have a different workflow in mind?

    Read the docs

From API key to first call in minutes.

Sign in with GitHub, create a key, and point your agent or code at MCPBytes. Your first 100 credits are free.

# Sign in with GitHub when prompted
claude mcp add --transport http mcpbytes \
  https://api.mcpbytes.com/mcp

# Or use an API key
claude mcp add --transport http mcpbytes \
  https://api.mcpbytes.com/mcp \
  --header "Authorization: Bearer $MCPBYTES_API_KEY"