Supported Formats#
Every extension the API accepts and every format it hands back, one table per converter family. The lists here are the ones the code checks against, so an extension that is missing is genuinely rejected rather than merely undocumented.
If you want the pairing instead (this input, that output, and the endpoint path that joins them), the conversion matrix has all 51 endpoints in one scannable table.
Web pages#
Five endpoints that take a URL in a JSON body rather than a file upload.
| Endpoint | Input | Output |
|---|---|---|
POST /v1/convert/url-to-pdf |
One URL, or an array of URLs | PDF (application/pdf) |
POST /v1/convert/url-to-screenshot |
One URL, or an array of URLs | PNG (image/png) |
POST /v1/convert/url-to-markdown |
One URL, or an array of URLs | Markdown (text/markdown; charset=utf-8) |
POST /v1/convert/website-to-pdf |
One site URL, crawled or read from sitemap.xml |
ZIP of PDFs |
POST /v1/convert/website-to-screenshot |
One site URL, crawled or read from sitemap.xml |
ZIP of PNGs |
Screenshots are PNG. There is no JPEG or WebP screenshot option: the capture call is hard-coded to PNG. The two website-to-* endpoints are async only and always answer 202 with a batch_id and an output_format of zip.
Documents to PDF#
Eleven single-target endpoints, each a multipart/form-data upload with the document in the file field. Output is always a single PDF. The catch-all anything-to-pdf is the twelfth and has its own section below.
| Endpoint | Accepted input | Engine |
|---|---|---|
POST /v1/convert/html-to-pdf |
.html, .htm |
WeasyPrint |
POST /v1/convert/markdown-to-pdf |
.md, .markdown |
WeasyPrint |
POST /v1/convert/doc-to-pdf |
.doc, .docx |
LibreOffice |
POST /v1/convert/excel-to-pdf |
.xls, .xlsx |
LibreOffice |
POST /v1/convert/ppt-to-pdf |
.ppt, .pptx |
LibreOffice |
POST /v1/convert/odt-to-pdf |
.odt |
LibreOffice |
POST /v1/convert/ods-to-pdf |
.ods |
LibreOffice |
POST /v1/convert/odp-to-pdf |
.odp |
LibreOffice |
POST /v1/convert/ots-to-pdf |
.ots |
LibreOffice |
POST /v1/convert/pages-to-pdf |
.pages |
LibreOffice |
POST /v1/convert/numbers-to-pdf |
.numbers |
LibreOffice |
The engine decides which pdf_options are legal. HTML and Markdown go through WeasyPrint and accept page size, orientation, margins, scale, header and footer. The nine LibreOffice-backed endpoints take page geometry from the source document, so they honour grayscale only and return 400 when a geometry option is set explicitly.
anything-to-pdf#
POST /v1/convert/anything-to-pdf accepts 36 extensions and always returns a PDF. It is file upload only: it takes no URLs.
.bmp .csv .doc .docx .epub .gif
.heic .heif .htm .html .jpeg .jpg
.markdown .md .mdown .mkd .numbers
.odp .ods .odt .ots .pages .pdf
.png .ppt .pptx .rtf .svg .text
.tif .tiff .txt .webp .xhtml .xls
.xlsx
The extension picks the engine:
| Input group | Extensions | Engine |
|---|---|---|
| Office, OpenDocument, iWork, RTF, CSV | .doc .docx .xls .xlsx .ppt .pptx .odt .ods .odp .ots .pages .numbers .rtf .csv |
LibreOffice headless |
| HTML | .html .htm .xhtml |
WeasyPrint |
| Markdown | .md .markdown .mdown .mkd |
WeasyPrint |
| Plain text | .txt .text |
Wrapped in a monospace block, then WeasyPrint |
| EPUB | .epub |
EPUB to Markdown, then WeasyPrint |
| Raster images | .png .jpg .jpeg .gif .bmp .tiff .tif .webp .heic .heif |
Pillow |
| SVG | .svg |
CairoSVG |
.pdf |
Validated passthrough |
Two consequences of that routing:
- Page geometry (
page_size,page_width,page_height,orientation, margins,scale,header,footer) is honoured for HTML, Markdown, plain text, EPUB, image and SVG input only. Set one explicitly on office or PDF input and the request returns400.grayscaleworks for every input. - A PDF upload is accepted and passed straight through, after a check that the bytes start with
%PDF-. Combined withgrayscale, that makes this endpoint the way to turn an existing PDF grayscale.
anything-to-markdown#
POST /v1/convert/anything-to-markdown accepts 22 extensions and always returns one UTF-8 .md file.
.csv .doc .docx .epub .htm .html
.markdown .md .mdown .mkd .odp
.ods .odt .pdf .ppt .pptx .rtf
.text .txt .xhtml .xls .xlsx
Grouped by how each one is read:
| Input group | Extensions | Path |
|---|---|---|
| Text and Markdown | .txt .text .md .markdown .mdown .mkd |
Read directly |
| HTML | .html .htm .xhtml |
HTML to Markdown |
| Legacy and OpenDocument | .doc .ppt .xls .odt .ods .odp .rtf |
LibreOffice to HTML, then to Markdown |
| Native extractors | .pdf .docx .pptx .xlsx .csv .epub |
Format-specific extractor |
Three things to plan around:
- Images are rejected.
.png,.jpg,.jpeg,.gif,.bmp,.webp,.tiffand.tifreturn400withImage OCR ('<ext>') is not yet supported on this endpoint.There is no OCR on this endpoint, so a scanned page in an image file has no text path today. .ots,.pagesand.numbersare not accepted here, even thoughanything-to-pdftakes all three. Send them throughanything-to-pdffirst, since.pdfis on the list above.- Output is normalised before it is returned: CRLF becomes LF, runs of three or more blank lines collapse, and the file ends with a newline.
Extraction runs under resource caps that protect against hostile uploads. ZIP-based inputs (EPUB, DOCX, XLSX, PPTX) are rejected above 400 MB declared uncompressed or 10,000 entries. Emitted tables truncate cells at 500 characters and stop at 5,000 body rows. The PDF extractor caps at 2,000 pages and 20,000 words per page.
Data formats#
Eleven endpoints, all synchronous uploads. The extension of the file has to match the endpoint you send it to.
| Endpoint | Accepted input | Output |
|---|---|---|
POST /v1/convert/json-to-xml |
.json |
.xml |
POST /v1/convert/xml-to-json |
.xml |
.json |
POST /v1/convert/json-to-yaml |
.json |
.yaml |
POST /v1/convert/yaml-to-json |
.yaml, .yml |
.json |
POST /v1/convert/json-to-csv |
.json |
.csv |
POST /v1/convert/csv-to-json |
.csv |
.json |
POST /v1/convert/json-to-toml |
.json |
.toml |
POST /v1/convert/toml-to-json |
.toml |
.json |
POST /v1/convert/csv-to-xml |
.csv |
.xml |
POST /v1/convert/xml-to-csv |
.xml |
.csv |
POST /v1/convert/markdown-to-html |
.md, .markdown |
.html |
Images#
Twenty-two endpoints: twenty conversions between JPEG, PNG, SVG, HEIC and WebP (every ordered pair), plus pdf-to-jpeg and compress-image. Read this table as "what can I turn this into".
| Input | Available outputs |
|---|---|
JPEG (.jpeg, .jpg) |
PNG, SVG, HEIC, WebP |
PNG (.png) |
JPEG, SVG, HEIC, WebP |
SVG (.svg) |
JPEG, PNG, HEIC, WebP |
HEIC (.heic, .heif) |
JPEG, PNG, SVG, WebP |
WebP (.webp) |
JPEG, PNG, SVG, HEIC |
PDF (.pdf) |
JPEG, via pdf-to-jpeg |
| PNG, JPEG, WebP | The same format, smaller, via compress-image |
Endpoint names follow the pair: jpeg-to-png, svg-to-webp, heic-to-jpeg, and so on. Note the JPEG endpoints are spelled jpeg-, not jpg-, though both .jpeg and .jpg files are accepted.
compress-image keeps the input format: PNG in, PNG out. Its optional target_size_kb must be at least 1, and a smaller value returns 400 before any op is spent.
pdf-to-jpeg returns a raw JPEG for a single-page PDF and a ZIP of page_1.jpeg through page_N.jpeg for a multi-page one. Any converter output that begins with a ZIP signature is delivered with a .zip extension, which is why the same endpoint can hand you two different content types.
Pixel ceilings#
These are checked separately from your plan's file-size limit, because a small file can decode into an enormous bitmap.
| Limit | Value | Applies to |
|---|---|---|
| Decoded pixels | 40,000,000 | Every image endpoint, checked right after the image is opened |
| SVG output dimension | 10,000 px per side | SVG rasterisation (svg-to-*) |
| SVG output pixels | 25,000,000 total | SVG rasterisation (svg-to-*) |
| Render size | 25,000,000 px per page | pdf-to-jpeg, which scales the render down to fit |
| Page count | 500 pages | pdf-to-jpeg |
SVG requests that break a dimension ceiling return 400 before any op is counted. Passing only width or only height derives the other from the SVG's intrinsic ratio; when that ratio cannot be read, the request fails with a 400 asking for both.
Output media types#
Direct downloads carry the media type mapped from the output extension.
| Extension | Content type |
|---|---|
.pdf |
application/pdf |
.png |
image/png |
.jpeg, .jpg |
image/jpeg |
.heic |
image/heic |
.webp |
image/webp |
.svg |
image/svg+xml |
.json |
application/json |
.xml |
application/xml |
.yaml, .yml |
application/x-yaml |
.csv |
text/csv |
.toml |
application/toml |
.html |
text/html |
.md |
text/markdown; charset=utf-8 |
.zip |
application/zip |
Anything not in that list is served as application/octet-stream.
V2 inputs and outputs#
The web intelligence endpoints work in outputs rather than file formats.
POST /v2/perceive takes a URL and returns any of a closed set of outputs: markdown, html_cleaned, html_raw, screenshot, screenshot_full_page, pdf, links, images, structured. The default is ["markdown", "structured"]. Everything except structured is written to storage and returned as a signed download URL; structured comes back inline in the response body.
POST /v2/ingest/files reuses the anything-to-markdown list above verbatim, so the same 22 extensions are accepted and images are rejected the same way. A single job takes up to 200 files, each filename up to 255 characters, and the job's output is one JSONL file of chunks.
How format detection works#
Uploads are checked twice, and neither check looks at the request's Content-Type. There is no MIME allowlist for uploads anywhere in the API.
First, the filename extension. If the endpoint declares an accepted list and your extension is not on it, the request fails immediately:
{
"detail": "Invalid file format '.txt' for json-to-xml. Allowed: .json"
}
Then, the bytes. The API sniffs the first bytes of the upload and compares what it finds against what the extension claimed. It recognises PNG, JPEG, GIF, WebP, HEIC and HEIF, PDF, and the two office container types (the ZIP container behind .docx, .xlsx, .pptx, ODF, iWork and EPUB, and the older OLE2 container behind .doc, .xls, .ppt).
Text formats skip the sniff entirely. JSON, CSV, XML, YAML, TOML, Markdown, HTML, SVG and plain text have no reliable signature, so their extension is taken at face value and the converter reports the problem instead.
The sniff is deliberately conservative. It rejects only a high-confidence mismatch, meaning a signature it recognises that belongs to a different group than the extension claimed. A .png upload whose bytes are a JPEG is rejected. Bytes it does not recognise pass through. A mismatch returns 400:
{
"detail": "File content does not match the 'jpeg-to-png' input type."
}
photo.webp photo.png gets past the extension gate and then fails the byte check with the 400 above. Send the real extension and pick the endpoint that matches it.
Filenames are rewritten, not rejected, on the way to storage: only the basename survives, .. and the characters <>:"|?* are stripped, and spaces become underscores. See file ingestion for the resulting object key.
Related pages#
- Conversion matrix pairs every input with its output and endpoint path.
- File ingestion covers how to send the bytes: multipart upload, a URL the API fetches, and the size ceilings that apply.
- Errors has the full message list for
400,413and415. - Rate limits and quotas has the per-plan upload ceilings.