REST API documentation

conv2pdf REST API — convert, merge, split, compress, protect and number PDFs from your application — 17 tools exposed via 6 REST endpoints. Hosting in France, GDPR-compliant, no US third party in the processing chain.

Quick start

  1. Create an account (free, email — no password required)
  2. From your dashboard, create an API key (free Dev plan, 300 conversions included to test, valid 12 months, no renewal)
  3. Authenticate your calls with the Authorization: Bearer cpdf_live_… header

Base URL

https://api.conv2pdf.com/v1

SDK, OpenAPI & Postman

Use conv2pdf with n8n

The official conv2pdf node for n8n runs 14 of the 17 tools, all but epub-to-pdf, ps-to-pdf and split-pdf, on the files that flow through your workflows. On a self-hosted n8n, open Settings > Community Nodes, select Install and enter n8n-nodes-conv2pdf, then create a Conv2pdf API credential with your key.

The node reads the file from a binary field of each item and returns the converted file the same way. It merges the files of several items, or of several fields of one item, deletes the job from conv2pdf once the result is downloaded, and waits and retries when the API asks it to slow down. The n8n AI Agent can use it as a tool to read the quota of the key. Source, usage notes and changelog are on GitHub.

Use conv2pdf with Zapier

The official conv2pdf app on Zapier adds an action to your Zaps for 15 of the 17 tools, all but ps-to-pdf and split-pdf: office documents (including Apple Pages), images, HEIC photos and EPUB books to PDF, HEIC to JPG, and, for PDFs, merging, page extraction, rotation, page numbers, watermark, compression, password protection, unlocking, and conversion to Word or to images. Connect it with your API key and map a file from a previous step: the action returns the converted file and the quota of the key. Zapier gives a step 30 seconds, so very large files are better converted through the API directly. AI assistants such as Claude, ChatGPT and Cursor can also call these actions through Zapier MCP. The source is on GitHub.

Use conv2pdf with an AI assistant (MCP)

The official conv2pdf MCP server gives an AI assistant 15 of the 17 tools of the API, all but ps-to-pdf and split-pdf, on the files of your computer: Claude Code, Claude Desktop, Cursor and any Model Context Protocol client that runs a local server. The assistant gives the path of a file; the server sends it to conv2pdf, writes the result next to the original without ever overwriting an existing file, then deletes the job from conv2pdf. It needs Node.js 22 or later, and your API key in the CONV2PDF_API_KEY environment variable.

In Claude Code:

claude mcp add --env CONV2PDF_API_KEY=cpdf_live_... --transport stdio conv2pdf -- npx -y conv2pdf-mcp

On Windows, put cmd /c before npx. In Claude Desktop, Cursor and the other clients configured with a JSON file:

{
  "mcpServers": {
    "conv2pdf": {
      "command": "npx",
      "args": ["-y", "conv2pdf-mcp"],
      "env": { "CONV2PDF_API_KEY": "cpdf_live_..." }
    }
  }
}

In Claude Code, the conv2pdf plugin installs the same server with a skill that tells the assistant which tool to call, in which order, and what a call costs:

/plugin marketplace add jamalofski/conv2pdf-mcp
/plugin install conv2pdf@conv2pdf

Then ask for what you need: "convert report.docx to PDF", "merge these three PDFs", "compress scan.pdf and protect it with a password". The server exposes 13 tools: convert_to_pdf for office documents, images, HEIC photos and EPUB books, one tool per PDF operation, and get_quota to read the quota of the key. Each conversion counts in the quota like a call to the API. The Claude and ChatGPT web apps, which do not run a local server, go through Zapier MCP. Source and usage notes are on GitHub.

Authentication

Every request must include the HTTP header Authorization: Bearer <your_key>. A revoked or non-existent key returns a 401 status. An exceeded quota returns 429 with reset details.

Endpoints

GET /v1/tools

Returns the list of available tools, their limits and accepted formats.

curl https://api.conv2pdf.com/v1/tools \
  -H "Authorization: Bearer cpdf_live_..."

POST /v1/convert/:tool

Performs a conversion. Files are sent as multipart/form-data.

17 tools available. The up-to-date machine list (accepted formats, file bounds) is returned by GET /v1/tools.

Tool (:tool)InputFilesOutput
image-to-pdfPNG, JPG, WEBP, GIF, TIFF1PDF
heic-to-jpgHEIC, HEIF1JPG
heic-to-pdfHEIC, HEIF1PDF
office-to-pdfDOC(X/M), ODT, RTF, TXT, XLS(X/M), ODS, CSV, PPT(X/M), ODP, ODG, SXW/SXC/SXI/SXD, FODT/FODS/FODP/FODG, PAGES1PDF
epub-to-pdfEPUB1PDF
ps-to-pdfPS, EPS1PDF
pdf-to-wordPDF1DOCX
pdf-to-imagePDF1ZIP (1 image/page)
merge-pdfPDF2 to 20PDF
split-pdfPDF1ZIP (1 PDF/page)
extract-pagesPDF1PDF
compress-pdfPDF1PDF
rotate-pdfPDF1PDF
protect-pdfPDF1PDF
unlock-pdfPDF1PDF
watermark-pdfPDF1PDF
page-numbers-pdfPDF1PDF

Optional parameters (form-data fields):

extract-pages keeps the requested pages in a single PDF; split-pdf, which takes no field, returns each page in its own file (page-001.pdf, page-002.pdf…), gathered in a ZIP archive. Either way the result carries those pages only: not the pages left out, not the attachments, not the document metadata. A selection the document does not allow returns 400 invalid_page_range, with the reason in cause (syntax, page_zero, beyond_last_page or whole_document), the refused part in range and, once the PDF has been opened, its page count in total_pages. split-pdf returns 422 pdf_single_page on a single-page PDF, 422 too_many_pages beyond 200 pages and 422 output_too_large if the archive would exceed 600 MB.

Until October 2026, extraction was called split-pdf. A split-pdf call that carries a ranges field is still served as extract-pages: an existing integration has nothing to change. One difference: pages now come out in the order written (5,1,3 returns pages 5, 1 and 3) instead of sorted.

epub-to-pdf lays the book out on A4 pages, with its table of contents and internal links; the fonts of the book are replaced by standard fonts. A book whose pages would come out blank (a fixed layout of full-page images) returns 422 epub_blank_output, a book beyond 1,500 pages 422 epub_too_long, a DRM-protected book 422 password_protected. office-to-pdf converts Apple Pages documents, recognized by their .pages extension; Numbers and Keynote files are refused (415 unsupported_content, with detected_type iwork_numbers or iwork_keynote).

ps-to-pdf converts PostScript and EPS files, recognised from their content: text stays text, drawings stay vector, an EPS gives one page at the size of the drawing, and a document that declares no page size is set on A4. A PostScript program that fails returns 422 source_unreadable, a file that draws no page 422 ps_blank_output, a document of more than 500 pages 422 too_many_pages, a conversion of more than 30 seconds 504 conversion_timeout.

Example — Image → PDF (curl)

curl -X POST https://api.conv2pdf.com/v1/convert/image-to-pdf \
  -H "Authorization: Bearer cpdf_live_..." \
  -F "file=@photo.jpg"

Example — Merge PDFs (curl)

curl -X POST https://api.conv2pdf.com/v1/convert/merge-pdf \
  -H "Authorization: Bearer cpdf_live_..." \
  -F "file=@doc1.pdf" \
  -F "file=@doc2.pdf" \
  -F "file=@doc3.pdf"

Example — Compression (curl)

curl -X POST https://api.conv2pdf.com/v1/convert/compress-pdf \
  -H "Authorization: Bearer cpdf_live_..." \
  -F "file=@big.pdf" \
  -F "quality=medium"

Example — PDF → Word (curl)

curl -X POST https://api.conv2pdf.com/v1/convert/pdf-to-word \
  -H "Authorization: Bearer cpdf_live_..." \
  -F "file=@report.pdf"

Returns an editable .docx file. A scanned PDF (no text layer) returns 422 pdf_scanned_needs_ocr: OCR is available to Premium accounts on the website only, and is not exposed through the API; beyond 500 pages, 422 pdf_too_many_pages.

Example — PDF → Image (curl)

curl -X POST https://api.conv2pdf.com/v1/convert/pdf-to-image \
  -H "Authorization: Bearer cpdf_live_..." \
  -F "file=@report.pdf" \
  -F "format=png"

Renders each page as an image at 150 DPI and returns a .zip archive (one image per page). The format field is png (default) or jpg. Beyond 100 pages, 422 too_many_pages; if the result exceeds the maximum size, 422 output_too_large.

Example — Password protection (curl)

curl -X POST https://api.conv2pdf.com/v1/convert/protect-pdf \
  -H "Authorization: Bearer cpdf_live_..." \
  -F "file=@confidential.pdf" \
  -F "password=secret123" \
  -F "prevent_print=on" \
  -F "prevent_copy=on"

AES-256 encryption. The resulting PDF will prompt for the password on opening and apply the selected restrictions.

Successful response

The quota object is only present for calls authenticated with an API key.

{
  "job_id": "abc123…",
  "status": "success",
  "download_url": "/v1/download/abc123…",
  "size_bytes": 124533,
  "quota": {
    "plan": "starter",
    "quota": 1000,
    "used": 42,
    "soft_cap_limit": 1100,
    "status": "ok",
    "period_end": 1715789012345,
    "quota_period_end": 1715789012345,
    "credits_expire_at": null
  }
}

GET /v1/download/:jobId

Downloads the converted file. The file is served with its output MIME type (application/pdf, DOCX for PDF → Word, or application/zip for PDF → Image) and Cache-Control: no-store. Available for 1 hour after conversion.

curl https://api.conv2pdf.com/v1/download/abc123… \
  -H "Authorization: Bearer cpdf_live_..." \
  -o output.pdf

GET /v1/job/:jobId

Returns a job’s status and metadata (status, size, dates). Handy to check that a conversion is ready before downloading it.

curl https://api.conv2pdf.com/v1/job/abc123… \
  -H "Authorization: Bearer cpdf_live_..."

DELETE /v1/job/:jobId

Immediately deletes a job and its file, without waiting for the automatic expiry (1 hour).

curl -X DELETE https://api.conv2pdf.com/v1/job/abc123… \
  -H "Authorization: Bearer cpdf_live_..."

GET /v1/quota

Returns the quota state of the key: plan, usage, caps and dates, the same quota object a conversion returns. The call is free and has no side effect, so it is the one to use to check a key, for example in the credential test of a connector. It also answers when the quota is at its cap (status is hard_cap) or the Dev trial has expired (credits_expire_at is in the past), where conversions answer 429 or 402.

curl https://api.conv2pdf.com/v1/quota \
  -H "Authorization: Bearer cpdf_live_..."
{
  "plan": "starter",
  "quota": 1000,
  "used": 42,
  "soft_cap_limit": 1100,
  "status": "ok",
  "period_end": 1715789012345,
  "quota_period_end": 1715789012345,
  "credits_expire_at": null
}

Examples — Node.js

import { readFile } from 'node:fs/promises';

const file = await readFile('./photo.jpg');
const formData = new FormData();
formData.append('file', new Blob([file]), 'photo.jpg');

const res = await fetch('https://api.conv2pdf.com/v1/convert/image-to-pdf', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer cpdf_live_...' },
  body: formData
});
const data = await res.json();
console.log(data.download_url);

Examples — Python

import requests

with open('photo.jpg', 'rb') as f:
    r = requests.post(
        'https://api.conv2pdf.com/v1/convert/image-to-pdf',
        headers={'Authorization': 'Bearer cpdf_live_...'},
        files={'file': f}
    )
print(r.json()['download_url'])

Examples — PHP

Official SDK (recommended)

Install the SDK: composer require conv2pdf/php. See the repo and its examples.

use Conv2pdf\Conv2pdf;

$c = new Conv2pdf('cpdf_live_...');
$job = $c->convert('image-to-pdf', 'photo.jpg');
$c->download($job['download_url'], 'photo.pdf');

Without a dependency (raw request)

$ch = curl_init('https://api.conv2pdf.com/v1/convert/image-to-pdf');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer cpdf_live_...'],
  CURLOPT_POSTFIELDS => ['file' => new CURLFile('photo.jpg')],
]);
$data = json_decode(curl_exec($ch), true);
echo $data['download_url'];

Error codes

Every error response carries an error field: a stable code you can branch on. The extra fields listed below tell you what to fix. Refusals at the door (upload, plan, content) also carry plan; errors raised during conversion carry job_id and status (rejected for a verdict on the file, failed for a breakdown).

CodeErrorCause
400not_enough_files / too_many_filesFile count outside tool bounds. The body carries min and provided, or max.
400field_requiredRequired field missing or empty: ranges on extract-pages, text on watermark-pdf. The body carries field, plus example when the expected shape is not obvious. Quota is not consumed.
400invalid_rotation / invalid_page_range / invalid_quality / invalid_formatParameter outside the accepted values. The body carries what is accepted (allowed, valid) and, for a page selection, the reason in cause, the refused part in range and the page count of the document in total_pages. Quota is not consumed.
400password_too_short / password_too_longPassword outside bounds on protect-pdf. The body carries min or max. Quota is not consumed.
400bad_requestMultipart body refused by the parser: forbidden field name, invalid JSON field, or a body cut short in flight (code: "malformed_multipart"). The body carries code.
401missing_bearer_token / invalid_api_key / account_not_provisionedMissing auth, invalid or revoked key, or a key with no API account attached.
402plan_limit_filesMore files than the plan allows per conversion: 1 on the Dev plan, 20 on paid plans. The body carries plan and max_allowed — the limit actually applied, which is higher on a tool that requires several files (merge-pdf takes 2 even on Dev).
402credits_expiredDev plan trial credits past their validity date (12 months from when they were granted), whatever is left. The body carries expired_at and upgrade.
403forbiddenJob belongs to another key
404tool_not_found / job_not_foundTool or job does not exist
409job_not_readyThe job exists but has no output: conversion pending, failed or rejected. The body carries status (pending / failed / rejected).
410file_expired / job_deletedResource is gone for good: 1 h TTL exceeded, or job removed via DELETE /v1/job/:jobId. Do not retry.
413file_too_largeFile beyond the plan limit: 10 MB on the Dev plan, 200 MB on paid plans. When a higher plan would accept the file, the response carries upgrade with that plan, its monthly price excl. tax and its limit.
413payload_too_largeWhole request beyond 220 MB, files and fields combined. The body carries max_bytes.
413too_many_partsToo many elements in the multipart body: 20 files, 10 fields, 32 parts in total. The body carries limit (the bound that was crossed) and max.
415unsupported_media_typeThe request Content-Type cannot be parsed. Conversions are sent as multipart/form-data; everything else lands here — a raw body (application/pdf, application/octet-stream…), application/json, application/x-www-form-urlencoded, a missing header, or multipart/form-data without a boundary. The refusal happens before the body is read. The response carries the received type (received) and the list of parsable types (accepted).
415unsupported_contentFile content does not match the tool. The type is determined from CONTENT, not from the filename extension: a valid PDF is accepted even without an extension, and a file renamed to .pdf is rejected. The extension only settles what the content cannot: .doc/.xls/.ppt, plain text and CSV, Pages and Numbers. Numbers and Keynote files are refused with detected_type iwork_numbers or iwork_keynote. The response carries file, the detected type (detected_type) and bytes.
415unsupported_formatimage-to-pdf: image in a format the tool cannot read. The body carries received and accepted.
422empty_fileEmpty file (0 bytes). The body carries file.
422password_protectedPassword-protected (encrypted) file on a tool that cannot decrypt it, including a DRM-protected EPUB book or a locked iWork document; remove the protection before converting. The body carries file.
422needs_password / wrong_passwordEncrypted PDF on a tool that can handle it: password missing, or refused by the file. Send the password field. Neither a job nor quota is consumed.
422pdf_not_protected / pdf_already_protectedunlock-pdf on an unprotected PDF, or protect-pdf on an already encrypted one.
422pdf_scanned_needs_ocrPDF → Word: scanned document with no text layer. OCR is website-only, for Premium accounts; the API always returns this code.
422pdf_too_many_pages / too_many_pagesPDF → Word: more than 500 pages. Other PDF tools have their own ceiling, returned in max_pages.
422pdf_single_pagesplit-pdf: the PDF has a single page, there is nothing to split.
422epub_blank_output / epub_too_longepub-to-pdf: pages that would come out blank (a fixed layout of full-page images, such as comics or picture books), or a book beyond 1,500 pages (the body carries pages and max_pages).
422ps_blank_outputps-to-pdf: the file draws no page. This is the case of a font or a library of procedures saved as PostScript.
422unsupported_characterswatermark-pdf: the text contains characters the embedded fonts cannot render. The body carries field. Quota is not consumed.
422output_too_largeThe output would exceed the tool ceiling: images of a heavy document on pdf-to-image, an archive beyond 600 MB on split-pdf. The body carries max_bytes, plus format on pdf-to-image.
422source_unreadableFile of the right type, but the engine cannot open it: corrupt, truncated, or non-standard structure.
429rate_limitedRate exceeded: 20 conversions per minute per key on POST /v1/convert/:tool, 300 requests per minute per IP on the other endpoints. The response carries retry_after (seconds) and the Retry-After header; the monthly quota is not consumed.
429quota_exceededQuota reached. On paid plans, the body carries quota_period_end, the next reset date: do not retry before it. On the Dev plan, both quota_period_end and period_end are null — the included conversions do not renew, and a trial is not billed — and the body carries upgrade, the plan that reopens access.
500conversion_failedThe engine failed. The body carries job_id and status: "failed". Quota is refunded: our own breakdowns are not billed.
500internal_errorBreakdown outside conversion. No detail is published — 5xx responses are masked server-side.
503server_busyConversion queue saturated (retry within 5 s — Retry-After header)
504conversion_timeoutEngine time budget exceeded. The body carries timeout_ms, and the job is failed. Quota is refunded.

Quotas and reset

Each account has a quota according to its plan (see API pricing), shared by all its keys. On paid plans, the quota is monthly and resets on an anniversary date (not on the 1st of the month): the timestamp of the next reset is returned in quota_period_end, and the used counter goes back to zero. On the Dev plan, the 300 conversions included at signup are valid for 12 months from when they were granted (credits_expire_at) and do not renew (quota_period_end is null, and so is period_end: a trial is not billed). An email warns you at 80% of your quota and when it runs out, on every plan, and 30 days before your trial credits expire. A small overage is tolerated (soft_cap_limit, +10%) before the 429 block. You can read the quota state at any time, at no cost, with GET /v1/quota.

A conversion is counted when it succeeds, and when the file is refused after it has been read: unreadable, scanned, too many pages, already protected. These cost nothing: a file refused on arrival (type, size, missing password), a refused parameter (page range, rotation, quality, password too short…) and a failure on our side.

Privacy

Files (input and output) are deleted after 1 hour. The only exception on the service's side: if a conversion fails, the uploaded file is kept for 72 hours to find the cause, then deleted. Only one thing can extend a converted file's life, and it goes through the website: from the dashboard, the account holder can create a share link for a job — including one produced by the API — in which case the shared copy lives until the link expires (seven days at most on Free, thirty on Premium) or until it is revoked. No result caching is applied. All processing happens on OVHcloud servers in France. See our privacy policy for details.

Support

For any technical question, use our contact form (subject "Technical question"). Business and Custom plans: priority support with contractual SLA.