API reference

API reference

This reference is also available as plain text for AI tools at /llms-full.txt and as an OpenAPI 3.1 spec.

Quickstart

Get a key from your dashboard, then:

curl https://api.pdfinspector.com/v1/convert \
  -H "Authorization: Bearer $PDFINSPECTOR_API_KEY" \
  -F file=@report.pdf

Have a URL instead of a file? Send JSON:

curl https://api.pdfinspector.com/v1/convert \
  -H "Authorization: Bearer $PDFINSPECTOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/report.pdf", "pages": "1-5"}'

Using the API without a key

Without a key you can make one test call a day per IP address, up to 10 text pages, without OCR. Scanned pages are listed in pages_needing_ocr. Everything else needs a key with credits.

curl https://api.pdfinspector.com/v1/convert \
  -H "Accept: text/markdown" \
  -F file=@report.pdf

Accept: text/markdown returns only the Markdown as plain text. Leave it out to get the full JSON.

Markdown URL shortcut

Put https://api.pdfinspector.com/md/ in front of any PDF link and you get plain Markdown back. It works with any tool that can fetch a URL, including an AI agent's built-in web fetch, with nothing to install:

https://api.pdfinspector.com/md/https://example.com/annual-report.pdf

Optional query parameters: pages and ocr. Anonymous limits apply unless you send your key in the Authorization header.

Authentication

Send your key as a bearer token: Authorization: Bearer <key>.

  • sk_live_…: secret key. Server-side only.
  • pk_live_…: publishable key. Allowed in browsers. It only works from origins you list in the dashboard and stops at the monthly page cap you set.
  • sk_test_…: test key. Returns realistic sample output and never uses credits.

The MCP server also supports OAuth sign-in, so Claude and Claude Code users don't need to paste a key.

Keys for agents

An agent can create a key with one API call. It doesn't need a browser, a form or a CAPTCHA.

curl https://api.pdfinspector.com/v1/keys \
  -H "Content-Type: application/json" \
  -d '{"email": "you@example.com", "name": "my-app", "agent": "claude-code"}'
{
  "key": "sk_live_…",              // shown once, store it as PDFINSPECTOR_API_KEY
  "credits_remaining": 0,
  "checkout_url": "https://pdfinspector.com/checkout/…",
  "dashboard_url": "https://pdfinspector.com/dashboard"
}
  • The key is returned right away with no credits. It works as soon as credits are bought through checkout_url.
  • If you pass an email, that person also gets the checkout link by email.
  • Agents: store the key in the project's secrets or .env as PDFINSPECTOR_API_KEY, never in code, and show the checkout_url to your user. Agents can't pay.
  • Check limits any time with GET /v1/usage.

POST /v1/convert

Convert a PDF to Markdown. Send either multipart/form-data with a file, or application/json with a url or file_base64.

ParameterTypeDescription
filefileThe PDF (multipart). Max 50 MB.
urlstringPublic URL of a PDF. Alternative to file.
file_base64stringBase64-encoded PDF (JSON body). Alternative to file.
pagesstring1-indexed page selection, e.g. 1-3,7. Only these pages are processed and charged.
ocrstringauto (default): OCR only pages without real text. off: never OCR; scanned pages are listed in pages_needing_ocr. force: OCR every selected page.
per_pagebooleanAlso return a pages array with each page's Markdown and source. Default false.
webhook_urlstringRun as a job and POST the result here. See long documents.

Response

{
  "pdf_type": "mixed",            // "text_based" | "scanned" | "image_based" | "mixed"
  "confidence": 0.97,             // classification confidence, 0–1
  "page_count": 12,
  "markdown": "# Q3 Report\n\n## Revenue\n\n| Region | Q2 | Q3 |\n|---|---|---|\n…",
  "pages_ocr": [11, 12],          // pages whose text came from OCR
  "pages_needing_ocr": [],        // scanned pages NOT OCR'd (only with ocr: "off")
  "pages_with_tables": [2, 3, 7],
  "pages_with_columns": [4],
  "has_encoding_issues": false,
  "metadata": {
    "title": "Q3 Report", "author": "Finance Team", "subject": null,
    "keywords": null, "creator": "Microsoft Word", "producer": "macOS Quartz",
    "creation_date": "D:20260901103000+02'00'", "mod_date": null
  },
  "pages": [                       // only with per_page: true
    { "page": 1, "markdown": "# Q3 Report\n…", "source": "native" },
    { "page": 11, "markdown": "…", "source": "ocr" }
  ],
  "processing_time_ms": 3140,
  "usage": { "pages": 10, "ocr_pages": 2, "credits_used": 40, "credits_remaining": 19960 },
  "notices": []                    // hints, e.g. "2 scanned pages skipped: calls without a key don't include OCR…"
}
  • Tables are Markdown tables inside markdown.
  • metadata dates are returned as written in the PDF (PDF date format).
  • usage.pages counts regular pages (1 credit each) and usage.ocr_pages counts OCR pages (15 credits each). A page is never counted twice.

Scanned pages & OCR

With the default ocr: "auto", every page is checked first:

  1. Pages with real text are extracted directly. That takes milliseconds and costs 1 credit per page.
  2. Pages that are scanned, image-only or have broken font encoding go through OCR, including table structure. That costs 15 credits per page.
  3. Both are merged into one Markdown document in page order.

A PDF created on a computer usually has no OCR pages at all. Use ocr: "off" if you never want to spend OCR credits; scanned pages are then listed in pages_needing_ocr with no text. Not sure what you're dealing with? /v1/inspect tells you without converting anything.

A document with OCR pages takes a few seconds, so show a progress state in your UI.

POST /v1/inspect

Classify a PDF without converting it. It samples the file's content streams and usually answers in well under a second, even for a 300-page file. Same inputs as /v1/convert (file, url or file_base64). Uses no credits.

{
  "pdf_type": "scanned",
  "confidence": 0.99,
  "page_count": 48,
  "pages_needing_ocr": [1, 2, 3, …, 48],
  "pages_with_tables": [],
  "pages_with_columns": [],
  "is_complex_layout": false,
  "has_encoding_issues": false,
  "metadata": { "title": null, "author": null, "producer": "Canon iR-ADV C5535" }
}

Use it to warn users before a large OCR job, estimate cost, or send scans somewhere else.

POST /v1/text-items Advanced

Every text run with its position and font. Use it for highlighting, redaction overlays, or your own layout logic. Works on pages with real text; OCR isn't applied here. Accepts pages.

{
  "items": [
    { "text": "Revenue", "page": 2, "x": 72.0, "y": 690.4, "width": 58.2, "height": 14.0,
      "rotation": 0, "font": "Helvetica-Bold", "font_size": 14, "is_bold": true, "is_italic": false }
  ]
}

Coordinates are PDF points from the lower-left corner of the visible page box, with y growing upward.

Long documents & webhooks

Up to 50 pages come back in the same request. Longer documents, or any request with webhook_url, return 202 with a job:

{ "id": "job_8f2k1c", "status": "processing" }
  • Poll GET /v1/jobs/{id} every few seconds until status is completed or failed, or
  • Wait for our POST to your webhook_url. It carries a PDFInspector-Signature header (HMAC-SHA256 of the body with your webhook secret).

Job results are kept only until you fetch them: they're deleted after the first successful fetch or after 1 hour, whichever comes first.

Data retention

  • PDFs and results exist only while your request is being processed and are deleted automatically right after. There is no permanent storage and no archive.
  • File contents and extracted text are never written to our logs or error reports.
  • Nothing is used for training.
  • Long-document job results are deleted when fetched, or after 1 hour at the latest (see above).
  • We keep only billing metadata: timestamp, key id, page counts, processing time.

Errors

Errors always look like this. The message is written so a person or an AI builder can fix the problem from it, and next_action tells an agent exactly what to call next:

{ "error": {
    "code": "anonymous_limit",
    "message": "You've used today's test call. Create a key with POST /v1/keys and ask your user to buy credits through the checkout link.",
    "next_action": { "type": "create_key", "method": "POST", "url": "https://api.pdfinspector.com/v1/keys" },
    "docs": "https://pdfinspector.com/docs/#keys" } }
StatusCodeMeaning
400invalid_requestMissing file/url or bad parameter.
401invalid_keyKey missing, wrong, or revoked.
402insufficient_creditsNot enough credits, or the key's spend cap was reached. next_action is buy_credits with a checkout link for the human.
403origin_not_allowedPublishable key used from a domain not on its list.
413file_too_largeOver 50 MB or 500 pages.
422unreadable_pdfCorrupt file, not a PDF, or password-protected.
429anonymous_limitThe daily test call without a key is used up. Create a key with POST /v1/keys.
429rate_limitedToo many requests. Retry after the Retry-After header.

Limits

  • Max file size 50 MB, max 500 pages.
  • Same-request response up to 50 pages; larger documents run as a job.
  • No key: one test call a day per IP, up to 10 text pages, no OCR.
  • Rate limit: 30 requests/min with credit packs, 60/min on Builder, higher on Scale.
  • 1 page = 1 credit, 1 OCR page = 15 credits. Failed requests and /v1/inspect are free.
  • Subscription credits reset monthly and don't roll over. Pack credits last 12 months and are used after monthly credits.
  • Check your balance with GET /v1/usage.

MCP server

Remote MCP over Streamable HTTP at https://pdfinspector.com/mcp. Without auth it allows the daily test call. Otherwise use OAuth (with dynamic client registration, so any MCP client can connect) or Authorization: Bearer sk_…. When credits run out, the tool result includes the checkout link for the user. Setup for each client is on the integrations page.

ToolInputsReturns
pdf_to_markdownurl or file_base64, optional pages, ocrSame JSON as /v1/convert
inspect_pdfurl or file_base64Same JSON as /v1/inspect
get_jobidStatus and result of a long-document job
create_api_keyoptional email, nameA new key with no credits, plus a checkout link (same as POST /v1/keys)
get_usagenonePlan and credit balance