> ## Documentation Index
> Fetch the complete documentation index at: https://docs.doconda.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Convert files

> Turn any file into another format with no AI: to Markdown for your model, Markdown to Word, PDF to Word, anything to PDF.

> [!NOTE]
>
> Assumes you have completed the [quickstart](https://docs.doconda.com/quickstart.md).

`POST /v1/convert` turns a file into another format without AI, in one call. This guide covers which formats convert to
which, how to read the result, what it costs and the four ways to pass the file.

## Convert a file

Send the file and the target format in `to`. Doconda answers with the converted text or a link to the converted file.

```python Python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
from doconda import Doconda

doconda = Doconda()  # reads DOCONDA_API_KEY

result = doconda.convert({"url": "https://example.com/contract.pdf"}, "md")
print(result["text"][:60], result["pages"])
```

```ts TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
import { Doconda } from "@doconda/sdk"

const doconda = new Doconda() // reads DOCONDA_API_KEY

const result = await doconda.convert({ url: "https://example.com/contract.pdf" }, "md")
console.log(result.text?.slice(0, 60), result.pages)
```

The response is a `conversion`:

```json theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
{
  "object": "conversion",
  "id": "doc_01JAB3H8K2M7V6X5T1R0NPQ4ZW",
  "from": "pdf",
  "to": "md",
  "text": "# Lease agreement\n\nThis agreement is made…",
  "output": null,
  "pages": 7,
  "expires_at": null
}
```

`pages` counts the pages (PDF, Word), slides (PowerPoint) or sheets (Excel) of the file you sent.

## What becomes what

| You send | `to` |
| - | - |
| Word (`.docx`, `.doc`), `.odt`, Markdown, TXT, HTML | `docx`, `pdf`, `md`, `txt`, `html`, `odt`, `rtf` |
| Excel (`.xlsx`, `.xls`), CSV | `xlsx`, `csv`, `pdf`, `md`, `txt` |
| PowerPoint (`.pptx`, `.ppt`) | `pptx`, `pdf`, `md`, `txt` |
| PDF | `docx`, `md`, `txt` |
| Pictures (PNG, JPEG) | `pdf`, `md` |

Any other pair is rejected with `400 conversion_not_supported`, and the message lists the formats that file can become.
A file that can't be converted fails with `422` and its error code (`convert_failed` when nothing more specific applies).

## Read the result

Where the result lands depends on `to`:

* **`md`, `txt`, `html` and `csv`** come back in `text`, and `output` is `null`. Doconda deletes the result as soon as
  it answers, so `GET /convert/{id}` returns `404 not_found` afterwards.
* **`docx`, `pdf`, `odt`, `rtf`, `xlsx` and `pptx`** come back in `output`, and `text` is `null`. `output.url` is a
  download link valid 5 minutes. The file is deleted 15 minutes after it was made (`expires_at`); until then,
  `GET /convert/{id}` returns a fresh link.

```python Python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
from pathlib import Path
import httpx

result = doconda.convert("notes.md", "docx")
Path("notes.docx").write_bytes(httpx.get(result["output"]["url"]).content)
```

```ts TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
import { writeFile } from "node:fs/promises"

const result = await doconda.convert("notes.md", "docx")
await writeFile("notes.docx", new Uint8Array(await (await fetch(result.output!.url)).arrayBuffer()))
```

How each format is read:

* **Word**: headings, lists, tables, pictures and footnotes. Shapes, text boxes and charts don't go into Markdown, HTML
  or RTF.
* **PowerPoint**: one section per slide (separated by `---`), the title as `##`, bullets with their level, tables,
  pictures by their alternative text and speaker notes as a quote.
* **Excel**: one table per visible sheet, with the values the cells show (formulas calculated). To CSV, only the first
  sheet.
* **PDF**: headings, paragraphs, lists and tables rebuilt from the page; repeated headers, footers and page numbers are
  left out.
* **Scanned PDFs and pictures**: read with OCR.
* **To PDF**: documents use the same fonts Doconda uses everywhere (Calibri as Carlito, Cambria as Caladea…).

## Wait for a long conversion

A call waits up to 120 s. A long scanned PDF can take minutes: then the call answers `202` with
`{ "object": "conversion", "id", "status" }`, where `status` is `queued` or `running`. Call `GET /convert/{id}` within
15 minutes to pick up the result; it also answers `202` while the conversion is still running.

The SDKs' `convert()` does this for you: it polls every 2 s and returns the finished conversion.

## Price

The first 1,000 conversions each calendar month (UTC) are free for each organization. After that, each one costs
€0.01 from your balance, whatever its number of pages ([pricing](https://docs.doconda.com/guides/account.md#pricing)).

* Failed conversions are not charged and don't count toward the free 1,000.
* Past the free ones with no balance left, the call is rejected with `402 insufficient_balance`.
* A conversion doesn't appear in the documents list and sends no webhooks.
* Conversions count toward the limit of 120 documents per minute per organization; above it, `429 rate_limited`.

## Four ways to pass a file

`file` takes the same four forms here as `file` and `sources` in `POST /documents`:

| How | Example |
| - | - |
| A document Doconda already made, by its ID | `"doc_01JAB3…"` (Doconda uses its Word, PowerPoint or Excel file, or its PDF if that's all it has) |
| An uploaded file | `"file_01JAB3…"` |
| A public URL (`http` or `https`) | `{ "url": "https://…/report.docx" }` |
| Its bytes in base64 | `{ "data": "UEsDBBQ…", "filename": "report.docx" }` |

You can also send `multipart/form-data`, with the `to` field before the `file` field:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
curl https://api.eu.doconda.com/v1/convert \
  -H "Authorization: Bearer $DOCONDA_API_KEY" \
  -F to=pdf \
  -F file=@report.docx
```

Behavior to be aware of:

* Files are limited to 20 MB.
* URLs must point to public addresses; private and local addresses are rejected.
* A file sent by URL, base64 or multipart is deleted 15 minutes after the call.

## From the SDK

`doconda.convert(file, to)` takes an uploaded `file_…` or `doc_…` ID, a URL, a local path, or bytes with a `filename`,
and returns the finished conversion.

```python Python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
text = doconda.convert("file_01JAB3…", "md")["text"]
url = doconda.convert("https://example.com/notes.md", "docx")["output"]["url"]
pdf = doconda.convert(b"# Notes\n\nFirst point.", "pdf", filename="notes.md")
```

```ts TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
const { text } = await doconda.convert("file_01JAB3…", "md")
const { output } = await doconda.convert("https://example.com/notes.md", "docx")
const pdf = await doconda.convert(new TextEncoder().encode("# Notes\n\nFirst point."), "pdf", { filename: "notes.md" })
```

Bytes without a `filename` raise an error before any request, because the extension tells Doconda the format. Agents
get the same operation as the `convert_file` tool in the [MCP server](https://docs.doconda.com/agents/mcp.md).

## Next steps

> **[Create a document](https://docs.doconda.com/guides/create.md)**
>
> Make a new Word, PowerPoint, Excel or PDF file from a sentence or your Markdown.

> **[Edit a file](https://docs.doconda.com/guides/edit.md)**
>
> Change part of a file and keep the rest as it was.

> **[API reference](https://docs.doconda.com/api-reference/introduction.md)**
>
> Every parameter of `POST /convert` and `GET /convert/{id}`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.