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 .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 returns 400. grayscale works for every input.
  • A PDF upload is accepted and passed straight through, after a check that the bytes start with %PDF-. Combined with grayscale, 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, .tiff and .tif return 400 with Image 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, .pages and .numbers are not accepted here, even though anything-to-pdf takes all three. Send them through anything-to-pdf first, since .pdf is 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."
}
Renaming a file does not convert it. Calling 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.


  • 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, 413 and 415.
  • Rate limits and quotas has the per-plan upload ceilings.