REST API · OpenAPI 3.1

Convert any document via API

PDF, Word, Excel, images, audio, email — all converted to clean Markdown (or DOCX, CSV, EPUB, and more) with a single HTTP call. AI-enhanced quality, async webhooks, batch processing.

Building an integration? Skip raw HTTP — use the official Python or TypeScript SDK with typed models, auth, and task helpers. View SDK documentation →

Quick start

Upload a file and get structured Markdown back in under 5 minutes.

cURL
# 1. Create an API key at scanheroai.com/settings/api-keys
# 2. Upload a PDF and get Markdown back in seconds

curl -X POST https://api.scanheroai.com/v1/tasks \
  -H "X-Api-Key: sh_your_key" \
  -F "file=@report.pdf" \
  -F 'options_json={"output_format":"markdown"}'
PythonSDK →
$ pip install scanhero

from scanhero import ScanHero

sh = ScanHero(api_key="sh_...")

# Small files return immediately
task = sh.tasks.create("report.pdf")
print(task.output_markdown)

# Large files are async — poll until done
task = sh.tasks.create("recording.mp4")
task = sh.tasks.wait(task.task_id)

# Refine with an LLM prompt
task = sh.tasks.adjust(task.task_id, "Summarise in bullet points")

# Download as DOCX
docx = sh.tasks.download(task.task_id, format="docx")
open("output.docx", "wb").write(docx)
TypeScriptSDK →
$ npm install @scanhero/sdk

import { ScanHero } from "@scanhero/sdk";

const sh = new ScanHero({ apiKey: "sh_..." });
// or: const sh = ScanHero.fromEnv();  // reads SCANHERO_API_KEY

// Upload a PDF
const task = await sh.tasks.createTaskV1TasksPost({
  formData: {
    file: new Blob([pdfBytes], { type: "application/pdf" }),
    options_json: JSON.stringify({ output_format: "markdown" }),
  },
});

// Poll until done (large files)
let result = task;
while (result.status === "pending" || result.status === "processing") {
  await new Promise((r) => setTimeout(r, 2000));
  result = await sh.tasks.getTaskV1TasksTaskIdGet({ taskId: task.task_id });
}
console.log(result.output_markdown);

// Refine with an LLM prompt
await sh.tasks.adjustTaskV1TasksTaskIdAdjustPost({
  taskId: task.task_id,
  requestBody: { prompt: "Summarise in bullet points" },
});

// Profile and credit balance
const me = await sh.users.getMeV1UsersMeGet();
console.log(me.credits);

Authentication

Every request must include your API key in the X-Api-Key header.

X-Api-Key: sh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Never expose your API key client-side. Use it only in server-side code or CI/CD pipelines. Set a monthly budget per key at API → API keys.

Tasks

A task is one document conversion. Upload any supported file via POST /v1/tasks — not one endpoint per format. See Tasks vs direct convert for the few exceptions (DjVu and legacy PPT).

MethodEndpointDescription
POST/v1/tasksUpload a file and start conversion
GET/v1/tasks/{id}Poll status and retrieve output
GET/v1/tasksList your recent tasks
POST/v1/tasks/{id}/adjustRefine output with an LLM prompt
GET/v1/tasks/{id}/downloadDownload output in a specific format
GET/v1/tasks/estimate-costPreview credit cost before uploading
Sync (≤5 MB)
Response includes output_markdown immediately
Async (>5 MB)
Returns status: pending — poll or use webhooks
20+ input formats
PDF, DOCX, XLSX, MP3, EML, and more
20+ output formats
Markdown, DOCX, CSV, EPUB, LaTeX, RST…

Tasks vs direct convert

The interactive reference shows only three routes under the convert tag — that is intentional. Almost every file type goes through POST /v1/tasks (plus optional download). The convert endpoints are binary passthrough shortcuts that skip Markdown and LLM processing.

Rule of thumb: if you want Markdown, DOCX, CSV, transcripts, or email extraction → use tasks. If you need DjVu or legacy PPT→PPTX with no intermediate step → use convert.
Input / goalWhat you getAPI route
PDF, DOCX, DOC, RTF, ODTStructured documents → Markdown or other formatsPOST /v1/tasks
XLSX, XLS, ODS, CSVSpreadsheets → Markdown, CSV, XLSX, …POST /v1/tasks
PPTX (modern)Presentations → Markdown or download as other formatsPOST /v1/tasks
PNG, JPEG, TIFF, WEBP, HEIC, GIF, SVGImages → Markdown (OCR) or sidecarPOST /v1/tasks
MP3, WAV, M4A, MP4, WebM, …Audio / video → transcript MarkdownPOST /v1/tasks
EML, MBOX, PST, MSGEmail archives → Markdown per messagePOST /v1/tasks
EPUB, HTML, LaTeX, DjVu inputBooks, web captures, TeX sourcesPOST /v1/tasks
Any completed taskRe-export as DOCX, PDF, EPUB, LaTeX, SRT, RST, …GET /v1/tasks/{id}/download?format=…
PNG/JPEG/TIFF/WEBP → DjVu onlyDirect DjVu compression (no Markdown step)POST /v1/convert/image-to-djvu
PDF → DjVu onlyDirect multi-page DjVu (no Markdown step)POST /v1/convert/pdf-to-djvu
Legacy .ppt → PPTX onlyLibreOffice passthrough (no Markdown step)POST /v1/convert/ppt-to-pptx

Set the initial output via options_json on task create, e.g. {"output_format":"markdown"}. See the tasks and convert sections in the OpenAPI reference for request fields.

Webhooks

Register a URL to receive push notifications when tasks complete. Ideal for async (large file) processing.

Register a webhook

curl -X POST https://api.scanheroai.com/v1/webhooks \
  -H "X-Api-Key: sh_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your.app/hooks",
    "events": ["task.completed", "task.failed"]
  }'

Payload you receive

{
  "task_id": "3f2a…",
  "status": "done",
  "output_markdown": "# Invoice\n\n**Date:** 2026-01-15…",
  "quality_score": 0.97,
  "credits_used": 10
}

Verify authenticity with the X-Scan-Hero-Signature header (HMAC-SHA256).

Official SDKs

Prefer a typed client over raw HTTP? We ship official SDKs for the two most common stacks. Both authenticate with X-Api-Key and cover the same REST surface as this documentation. Source lives in the repo under sdk/python and sdk/typescript.

LanguagePackageInstallNotes
Python 3.9+scanheropip install scanheroHandcrafted helpers — tasks.wait(), retries, file uploads from path/bytes
TypeScript / JS@scanhero/sdknpm install @scanhero/sdkAuto-generated from /openapi.json — full typed API, Node 18+ fetch

Python SDK

Official SDK with full type hints, automatic retry on 429/5xx, and built-in async polling. Built on httpx (single dependency).

📦
pip install scanhero
One dependency: httpx
🔁
tasks.wait()
Polls until done automatically
🔒
Type-safe
Dataclass models, mypy-friendly
from scanhero import ScanHero

sh = ScanHero(api_key="sh_...")

# Small files return immediately
task = sh.tasks.create("report.pdf")
print(task.output_markdown)

# Large files are async — poll until done
task = sh.tasks.create("recording.mp4")
task = sh.tasks.wait(task.task_id)

# Refine with an LLM prompt
task = sh.tasks.adjust(task.task_id, "Summarise in bullet points")

# Download as DOCX
docx = sh.tasks.download(task.task_id, format="docx")
open("output.docx", "wb").write(docx)

TypeScript / JavaScript SDK

Generated with openapi-typescript-codegen from the live OpenAPI spec. Every endpoint, model, and enum is typed. Works in Node.js and modern browsers.

📦
npm i @scanhero/sdk
Zero runtime deps (native fetch)
OpenAPI-synced
Regenerate when the API changes
🔒
Full types
TaskResponse, ProcessingOptions, …
import { ScanHero } from "@scanhero/sdk";

const sh = new ScanHero({ apiKey: "sh_..." });
// or: const sh = ScanHero.fromEnv();  // reads SCANHERO_API_KEY

// Upload a PDF
const task = await sh.tasks.createTaskV1TasksPost({
  formData: {
    file: new Blob([pdfBytes], { type: "application/pdf" }),
    options_json: JSON.stringify({ output_format: "markdown" }),
  },
});

// Poll until done (large files)
let result = task;
while (result.status === "pending" || result.status === "processing") {
  await new Promise((r) => setTimeout(r, 2000));
  result = await sh.tasks.getTaskV1TasksTaskIdGet({ taskId: task.task_id });
}
console.log(result.output_markdown);

// Refine with an LLM prompt
await sh.tasks.adjustTaskV1TasksTaskIdAdjustPost({
  taskId: task.task_id,
  requestBody: { prompt: "Summarise in bullet points" },
});

// Profile and credit balance
const me = await sh.users.getMeV1UsersMeGet();
console.log(me.credits);

Services are namespaced on the client: sh.tasks, sh.jobs, sh.webhooks, sh.templates, and more — mirroring the REST paths below.

Regenerating SDKs from OpenAPI

When the API changes, regenerate typed clients from /openapi.json:

# TypeScript (repo: sdk/typescript)
cd sdk/typescript && npm run generate

# Against local backend:
OPENAPI_URL=http://127.0.0.1:8080/openapi.json npm run generate
# Python (optional codegen output)
pip install openapi-python-client
openapi-python-client generate \
  --url https://api.scanheroai.com/openapi.json \
  --output-path sdk/python-generated

# Handcrafted SDK: edit sdk/python/ directly

Credits

New accounts start with 100 free credits. Paid plans add more.

OperationCost
Document conversion (any format, ≤5 MB)10 credits
Overage+1 credit / MB above 5 MB
Audio / Video transcription10 + 2 credits / minute
Email batch (.mbox, .pst)10 + 1 credit / email
LLM adjustment roundtoken-priced (min. 1 credit; typically 1–10+ per round)
Batch job (N files)10 × N upfront

Ready to build?

Start with 100 free credits. No credit card required.