KolmoPDF API Guide
Base URL: https://www.kolmopdf.com
This guide covers the public Jobs API for PDF parsing, layout-preserving PDF translation, and format conversion. You can read it yourself or paste it into an AI assistant.
AI agent setup
Use KolmoPDF from Claude Code, Codex, or Cursor in Agent mode. Copy this prompt into your agent to install the official skill and configure your own API key:
Read https://www.kolmopdf.com/agent-setup/SKILL.md and follow the instructions to install and configure the official KolmoPDF skill for this agent.
Read the setup instructions before running them. The agent needs web access, terminal commands, and file access; chat-only clients can provide instructions but cannot install anything themselves.
Standalone skill (recommended)
npx skills add komoai2026/kolmopdf-skill
Select your agent and installation scope. This installs the complete official skill, including scripts/jobs.mjs and its parameter/workflow references. It uses the Jobs API directly; MCP is optional. The bundled helper requires Node.js 20+ and works on Windows, macOS, and Linux without Bash, jq, or extra packages.
Create an API key at API Management, then configure KOLMOPDF_API_KEY privately in the environment used to launch your agent. Do not paste the key into a conversation or commit it to a project. The following examples use placeholders:
export KOLMOPDF_API_KEY='YOUR_API_KEY'
$env:KOLMOPDF_API_KEY = 'YOUR_API_KEY'
These settings apply to that terminal and its child processes, not an already-running agent in another terminal. Restart the agent from the configured environment or use its private environment settings. Refresh/restart the client if it has not discovered the new skill yet.
Verify authentication with GET /api/v1/balance or the MCP balance tool. This does not process a document. Installing a skill does not automatically authenticate it, and no PDF should be uploaded just to check setup.
Claude Code plugin (optional)
Use the native plugin instead of the standalone skill when you want its bundled MCP integration:
/plugin marketplace add komoai2026/claude-plugin
/plugin install kolmopdf@kolmopdf
Configure KOLMOPDF_API_KEY in the agent's environment and refresh/restart as needed. Do not install both the plugin and standalone skill unless you intentionally need both.
Other MCP clients
The existing @kolmopdf/mcp-server runs locally over stdio with Node.js 20+ and reads KOLMOPDF_API_KEY from its environment. It is not a remote HTTP/OAuth connector. See the official client setup guide for configuration. File paths must be accessible on the machine running the server.
Start with your document
Ask your agent to parse a PDF to Markdown, translate a PDF while keeping its layout, or export Markdown to Word/PDF. The installed skill estimates processing credits and handles the upload, job progress, and downloaded files. For reading or summaries, it can reuse adequate existing text or offer high-fidelity parsing before uploading.
A subscription is not required to create an API key. Free, PAYG, Go, and Plus accounts can create one key; Pro supports up to ten. Web, API, and MCP share credits. If needed, buy one-time credits; setup does not automatically purchase credits or submit a paid task.
How it works
Long jobs do not block the HTTP request. You create a job, get a job id back right away, then check progress or wait for a webhook. When the job succeeds, download the result.
Typical flow:
- Create a job with your API key and a file. Response is 202 Accepted with an id like
job_01H.... - Track the job with status GET, SSE, or webhook.
- Download when
statusissucceeded.
You can close the client after create. Keep the job id (or your own client_reference_id) and come back later.
Result files are kept for 7 days. If parse uses images_as_url=true, image URLs last 30 days.
Auth
Create keys on the site API Management page. Free, PAYG, Go, and Plus accounts can create 1 API key; Pro gets up to 10 keys. Web, API, and MCP share the account balance.
Buy one-time credits at https://www.kolmopdf.com/credits ($3 per 1,000 credits), without subscribing or enabling automatic recharge. HTTP 402 responses include points_required, current_points, and purchase_url. A key-specific spending limit is separate from the account balance; buying credits does not raise that limit.
Send the key in a header:
Authorization: Bearer sk-xxx
or:
X-API-Key: sk-xxx
Legacy query-string authentication is supported for compatibility, but do not put a real API key in a URL: URLs can be saved in browser history and server logs. Use headers for new integrations.
Points for a key = the lower of account balance and that key’s remaining limit. Account points and API points share the same balance.
Quick start (Windows, macOS, Linux)
First configure KOLMOPDF_API_KEY as shown above. Test it without spending credits:
curl --fail-with-body -sS -H "Authorization: Bearer $KOLMOPDF_API_KEY" \
'https://www.kolmopdf.com/api/v1/balance'
Invoke-RestMethod -Uri 'https://www.kolmopdf.com/api/v1/balance' `
-Headers @{ Authorization = "Bearer $env:KOLMOPDF_API_KEY" }
To parse and download a small PDF without installing a skill or MCP, save the following as kolmopdf-quickstart.mjs. It needs only Node.js 20+, no npm packages or jq. Parsing costs 2 credits per page. It saves the actual server filename, including ZIP results, in the current directory.
import { readFile, writeFile } from 'node:fs/promises';
import { basename } from 'node:path';
const base = 'https://www.kolmopdf.com';
const key = process.env.KOLMOPDF_API_KEY;
const file = process.argv[2];
if (!key || !file) {
throw new Error('Set KOLMOPDF_API_KEY, then run: node kolmopdf-quickstart.mjs document.pdf');
}
const headers = { Authorization: `Bearer ${key}` };
async function json(route, options = {}) {
const response = await fetch(new URL(route, base), {
...options, headers, signal: AbortSignal.timeout(60000),
});
const body = await response.json();
if (!response.ok) throw new Error(body.message || body.error?.message || `HTTP ${response.status}`);
return body;
}
const form = new FormData();
form.append('file', new Blob([await readFile(file)]), basename(file));
let job = await json('/api/v1/jobs/parse', { method: 'POST', body: form });
console.log('Job:', job.id, '| Credits:', job.points_deducted);
const deadline = Date.now() + 30 * 60 * 1000;
while (['queued', 'processing'].includes(job.status) && Date.now() < deadline) {
await new Promise(resolve => setTimeout(resolve, 3000));
job = await json(job.links.self);
}
if (job.status !== 'succeeded') {
throw new Error(`Job ${job.id}: ${job.error?.message || job.status}. Keep this id; do not resubmit to recover a download.`);
}
const response = await fetch(new URL(job.result.download_url, base), { headers });
if (!response.ok) throw new Error(`Download failed: HTTP ${response.status}`);
const filename = basename(job.result.filename);
await writeFile(filename, Buffer.from(await response.arrayBuffer()));
console.log('Saved:', filename, '| Kind:', job.result.kind);
node kolmopdf-quickstart.mjs "document.pdf"
If you already installed the skill, use its bundled scripts/jobs.mjs instead: node "<HELPER_PATH>" balance or node "<HELPER_PATH>" parse "document.pdf". Resolve <HELPER_PATH> to the installed skill directory; the helper also handles ZIP extraction. Do not install a second integration just to use this example.
The links and result.download_url in responses are relative paths: resolve them against the Base URL. Status, SSE, and download requests all need the same API-key header; opening a download URL in a browser alone does not authenticate it. The Bash examples below use Bash line continuations; use the Node.js example on Windows. The optional Bash filename extraction uses jq.
Common rules
Max file size: 300 MB.
PDF parse / PDF translation: max 800 pages.
Each API key: up to 3 jobs running at once; more jobs wait in queue.
Optional header on create: Idempotency-Key. Generate a unique value for each new job; reuse it only when retrying that same submission. Reusing it for a different file returns the earlier job, not a new result. Retries with the same key do not charge twice.
Optional form fields on create:
client_reference_id— your own id so you can find the job laterwebhook_url— we POST when the job finishes (optional)webhook_secret— optional secret for webhook signature
Job status
Use:
GET /api/v1/jobs/{id}
{id} is the job_... value from create.
status can be: queued, processing, succeeded, failed, cancelled.
The body may also include phase, progress (0–100 or null), message, and links.
List jobs:
GET /api/v1/jobs?status=processing&client_reference_id=order_123&limit=20
Cancel:
POST /api/v1/jobs/{id}/cancel
Balance:
GET /api/v1/balance
Download (only after success):
GET /api/v1/jobs/{id}/download
HEAD /api/v1/jobs/{id}/download
Do not guess the extension. When status is succeeded, result tells you the real file:
"result": {
"filename": "paper.zip",
"kind": "zip",
"content_type": "application/zip",
"bytes": 1843200,
"sha256": "…",
"files": [
{ "name": "paper.md", "kind": "markdown" },
{ "name": "outline.md", "kind": "markdown" }
],
"download_url": "/api/v1/jobs/job_01H.../download"
}
kind is one of: zip, pdf, markdown, docx, html, latex, binary.
Save with result.filename. Download headers match: Content-Type, Content-Disposition (filename + filename*), X-Kolmo-Result-Kind, optional Content-Digest / X-Kolmo-Result-Sha256. HEAD returns the same headers without the body.
If a PDF viewer says the file is corrupt, check the result metadata and first bytes: %PDF is a PDF; PK is a ZIP container. A docx result also starts with PK and must keep its .docx extension. Rename an incorrectly named PDF to .zip only when the result is a ZIP bundle, not a Word document.
Wait for completion (SSE first)
SSE (preferred for CLIs / agents): GET /api/v1/jobs/{id}/events with Accept: text/event-stream. Use curl -N (no buffer). Events use id: + event: + data:. Stop on job.succeeded / job.failed / job.cancelled. Resume with Last-Event-ID. Terminal data includes the same result object as GET job.
Webhook: pass webhook_url when you create the job (your own HTTPS endpoint). We POST job.succeeded / job.failed. The payload includes data.result. Always GET the job again before treating it as final. Deliveries may retry; dedupe on the event id. Agents without a public URL should use SSE, not invent a webhook.
Poll (fallback): GET /api/v1/jobs/{id} every few seconds with backoff until a terminal status.
1. PDF parsing
Create:
POST /api/v1/jobs/parse
Content type: multipart form. Field file = PDF.
Useful form fields:
table_mode—markdown(default) orimageformula_format—dollar(default) orbracketenable_translation—true/falsetarget_language—zhenjakofrdeesrupt(when translation is on)output_options— comma list:original,translated,bilingualimages_as_url— when enrichment is off,truereturns a single Markdown file with public image URLs (30-day). When enrichment sidecars are produced, download is still a ZIP whose primary entry is that Markdown (plusoutline.md/summary.md/ …). Clients must detect ZIP vs raw markdown (magicPKorContent-Type).skip_rotation_detection,enable_cross_page_merge—true/falseenrichment— optional AI reading aids attached after parse (does not rewrite the primary Markdown body).- Omit → default
outline,summarywhen the server hasENRICHMENT_LLM_API_KEYconfigured. none→ disable aids; download shape matches pre-enrichment behaviour.- Examples:
outline,outline,summary,outline,summary,verification,tables. - If the source text is longer than 600,000 characters, AI aids are skipped; parse still succeeds and download is unchanged.
- When aids are produced, download is a ZIP: original parse output plus sidecars (
outline.md,summary.md,enrichment_meta.json, …).
- Omit → default
Points: 2 / page parse only; 3 / page with translation. Enrichment sidecars do not cost extra points.
Example:
curl -X POST 'https://www.kolmopdf.com/api/v1/jobs/parse' \
-H "Authorization: Bearer $KOLMOPDF_API_KEY" \
-F 'file=@document.pdf' \
-F 'table_mode=markdown' \
-F 'enable_translation=false'
Example create response (202):
{
"id": "job_01H...",
"object": "job",
"type": "pdf-to-markdown",
"status": "queued",
"phase": "queued",
"progress": 0,
"points_deducted": 20,
"remaining_points": 80,
"links": {
"self": "/api/v1/jobs/job_01H...",
"events": "/api/v1/jobs/job_01H.../events",
"cancel": "/api/v1/jobs/job_01H.../cancel",
"download": null
},
"created_at": "2026-08-06T12:00:00.000Z"
}
Then wait (SSE) and save using the filename from GET job — never hard-code .zip / .pdf / .md:
# Wait until event: job.succeeded | job.failed | job.cancelled
curl -N -sS -H "Authorization: Bearer $KOLMOPDF_API_KEY" \
-H 'Accept: text/event-stream' \
'https://www.kolmopdf.com/api/v1/jobs/job_01H.../events'
# Read result.filename (and result.kind) from GET, then:
NAME=$(curl -sS -H "Authorization: Bearer $KOLMOPDF_API_KEY" \
'https://www.kolmopdf.com/api/v1/jobs/job_01H...' | jq -r '.result.filename')
curl -L -H "Authorization: Bearer $KOLMOPDF_API_KEY" \
'https://www.kolmopdf.com/api/v1/jobs/job_01H.../download' \
-o "$NAME"
Parse download shape
| Condition | result.kind |
|---|---|
| Default parse (md + images) | zip |
images_as_url=true and enrichment off / skipped | markdown |
Enrichment sidecars produced (default outline,summary) | zip even if images_as_url=true |
enrichment=none | same as pre-enrichment |
Do not assume images_as_url=true is a raw .md file. Always use result.kind / magic PK.
2. PDF layout translation
Create:
POST /api/v1/jobs/translate-pdf
Multipart form. Field file = PDF.
Useful fields:
sourceLanguageorsource_language— defaultentargetLanguageortarget_language— defaultzhlayoutModes/output_modes/outputModes—translated_only,side_by_side(comma or JSON array)enable_image_translation/enableImageTranslationenable_table_translation/enableTableTranslationglossary— optional CSV glossary (UTF-8, max 2MB). Header language codes:en,zh,ja,ko,fr,de,es,ru,pt(any subset).
Points: 2 / page.
Glossary example:
en,zh
apple,香蕉
SenB,森巴牌
Example:
curl -X POST 'https://www.kolmopdf.com/api/v1/jobs/translate-pdf' \
-H "Authorization: Bearer $KOLMOPDF_API_KEY" \
-F 'file=@document.pdf' \
-F 'sourceLanguage=en' \
-F 'targetLanguage=zh' \
-F 'layoutModes=translated_only' \
-F 'glossary=@glossary.csv'
Status and download use the same job endpoints as above.
layoutModes | result.kind |
|---|---|
one of translated_only / side_by_side | usually pdf |
| both modes | zip of PDFs |
Never save as translated.pdf until you have read result.filename. A ZIP saved as .pdf opens as “file is damaged”.
3. Format conversion
Create:
POST /api/v1/jobs/convert
Multipart form. Field file = Markdown (.md / .markdown) or a ZIP of markdown + images.
Target format field: format / target_format / targetFormat.
Values: word, docx, html, pdf, latex, tex (default word).
Optional layout field: style_prompt / stylePrompt. Natural-language typesetting instructions, max 1000 characters after trim. Omit or leave blank to keep the default academic layout (no extra LLM call). Do not send style_spec / style_preset / pdf_engine unless you know you need them.
Points: 1 / task.
Example:
curl -X POST 'https://www.kolmopdf.com/api/v1/jobs/convert' \
-H "Authorization: Bearer $KOLMOPDF_API_KEY" \
-F 'file=@document.md' \
-F 'targetFormat=pdf' \
-F 'style_prompt=12pt, 1.5 line spacing, first-line indent two characters'
Again, track and download with GET /api/v1/jobs/{id} and .../download. Use result.filename. If the input was a ZIP and the target is LaTeX, kind may be zip rather than latex.
Minimal script shape
POST create → save job id
GET /events (SSE) until job.succeeded | job.failed | job.cancelled
fallback: loop GET job with backoff
if succeeded: GET job → result.filename / result.kind
GET download -o "$filename"
if kind is docx → keep .docx (Word files also have PK magic)
if kind is zip and name is not .zip → rename to .zip
Or create with webhook_url (your backend) and only GET when the webhook fires.
Errors (short)
401 — bad or missing API key / plan not allowed
402 — not enough points
400 — bad file or params
409 — download before the job finished
500 — server error; if the job failed after charge, points are refunded
Common error_code values: invalid_api_key, insufficient_points, no_file_found, parse_file_not_pdf, parse_file_too_large, parse_page_limit_exceeded.
Endpoint list
POST /api/v1/jobs/parse
POST /api/v1/jobs/translate-pdf
POST /api/v1/jobs/convert
GET /api/v1/jobs/{id}
GET /api/v1/jobs
GET /api/v1/jobs/{id}/events
GET /api/v1/jobs/{id}/download
HEAD /api/v1/jobs/{id}/download
POST /api/v1/jobs/{id}/cancel
GET /api/v1/balance