DocumentationAPI Reference

Core

Conversion Files

Every source and output belongs to a conversion. Upload inline, reference an earlier file, or read its raw content.

Reference a file from an earlier conversion

To reuse a file already produced by an earlier conversion, send its file_… id as the file (or files) input on a later call instead of the file's bytes. It can be a source file from a previous conversion (matched by extension) or an output file (matched by format, e.g. a markdown output can feed POST /v1/convert/markdown/html). To submit a brand-new file too large for one multipart request, use POST /v1/uploads instead — see “Uploading a file larger than the multipart limit” below.

Convert a stored PDF's Markdown output to HTML
curl -X POST https://api.markitdown.ai/v1/convert/markdown/html \
  -H "x-api-key: mdai_…" \
  -H "Content-Type: application/json" \
  -d '{ "file": "file_b2…" }'

The referenced file must belong to your own account (otherwise 404 not_found, so existence is never leaked), and its conversion must not have expired or been deleted (410 file_expired).

Uploading a file larger than the multipart limit

A single multipart/form-data request tops out at 100 MB regardless of plan (Cloudflare's request-body limit on our zone). A file within your plan's per-file limit — Lite 100 MB, Pro 200 MB, Max unlimited (bounded by credits and storage instead) — but over that transport ceiling uploads directly to storage in parts instead, on the public API and the web app alike. This exists only to get the bytes past Cloudflare; it is unrelated to OCR or any parsing limit.

POSThttps://api.markitdown.ai/v1/uploadsStart a chunked upload — returns a part plan and presigned PUT URLs
1. Create the upload
curl -X POST https://api.markitdown.ai/v1/uploads \
  -H "x-api-key: mdai_…" \
  -H "Content-Type: application/json" \
  -d '{ "name": "report.pdf", "bytes": 314572800 }'
201 Created
{
  "id": "upl_3f7c…",
  "name": "report.pdf",
  "bytes": 314572800,
  "mime": "application/pdf",
  "part_size": 31457280,
  "part_count": 10,
  "parts": [ { "number": 1, "url": "https://…" }, … ],
  "expires_at": "2026-09-27T00:00:00.000Z"
}

2. PUT each byte range to its part's url (any order, in parallel if you like — each is valid for 24 hours) and keep the ETag response header from every part.

POSThttps://api.markitdown.ai/v1/uploads/{id}/completeFinish the upload once every part has been PUT
3. Complete it
curl -X POST https://api.markitdown.ai/v1/uploads/upl_3f7c…/complete \
  -H "x-api-key: mdai_…" \
  -H "Content-Type: application/json" \
  -d '{ "parts": [ { "number": 1, "etag": "\"…\"" }, … ] }'

The server HEADs the assembled object and rejects it (413 upload_too_large, deleting the object) if the real size does not match what you declared or exceeds your plan's limit.

4. Submit the conversion with the upload's id instead of raw bytes: { "upload": "upl_3f7c…" } (or "uploads": […], up to 1000, for several files) on POST /v1/convert/{name} — it behaves exactly like a multipart file.

If a part fails or your process is interrupted, GET /v1/uploads/{id} returns which parts already landed (parts_uploaded) and fresh presigned URLs for the ones still missing (parts), so you resume instead of starting over. DELETE /v1/uploads/{id} aborts it. An upload not completed within 24 hours is aborted automatically, and a completed-but-unused upload is deleted after the same 24 hours.

Submitting an upload id that is not yours, was never completed, or has expired returns 404 not_found, 409 upload_not_completed, or 410 file_expired respectively.

Read a file's content

GEThttps://api.markitdown.ai/v1/conversions/{id}/files/{file_id}Get one file's metadata
GEThttps://api.markitdown.ai/v1/conversions/{id}/files/{file_id}/contentGet one file's raw bytes, by its own MIME type

There is no per-file delete — DELETE /v1/conversions/{id} removes the conversion and every source and output file it holds. A file cannot be deleted alone because output files exist only in relation to the source that produced them.

When should I reference a file instead of uploading it again?

For a one-off conversion, send the file straight to POST /v1/convert/{name} as multipart. Reference an existing file_… id when you want to run the same source through a different name or options (a stored PDF converted again with pages set), or chain a parse output into a render call (a stored markdown output converted with /v1/convert/markdown/html) — either way the bytes are reused, not re-uploaded.

Is there a presigned upload for the public API?

Yes — POST /v1/uploads (above) works the same way on the public API and the web app. Use it for a file over the multipart request-body limit; a smaller file can still go straight to POST /v1/convert/{name} as multipart bytes.

What file types can I upload?

PDF; Word (.doc, .docx, .odt, .rtf); PowerPoint (.ppt, .pptx, .odp); spreadsheets (.xls, .xlsx, .ods, .csv); HTML; plain text and Markdown; and images (.png, .jpg, .jpeg, .webp, .gif, .bmp, .tif, .tiff) — each behind its own name endpoint. GET /v1/formats returns the same list together with your plan's page and file-size limits, so a client can check a file before uploading it.

On this page