# Convertr API and MCP server: developer docs

Send a file or a URL, get the converted file back.

## Quick start

Convert your first file in three steps.

### 1. Get an API key

[Create an account](https://convertr.org/register), verify your email, then open [API keys](https://convertr.org/api-keys) and create a key. A key starts with `cvk_`. Convertr shows it once, so copy it right away. The free plan gives you 100 credits every month.

```bash
export CONVERTR_API_KEY="cvk_..."
```

### 2. Convert a file

Upload a file and ask for an output format. The `wait` parameter holds the request open until the job ends, up to 90 seconds.

**curl**

```bash
curl "https://convertr.org/api/v1/conversions?wait=60" \
  -H "Authorization: Bearer $CONVERTR_API_KEY" \
  -F output_format=mp3 \
  -F file=@clip.mp4
```

**Python**

```python
import os
import requests

API = "https://convertr.org/api/v1"
headers = {"Authorization": f"Bearer {os.environ['CONVERTR_API_KEY']}"}

with open("clip.mp4", "rb") as source:
    response = requests.post(
        f"{API}/conversions",
        headers=headers,
        params={"wait": 60},
        data={"output_format": "mp3"},
        files={"file": source},
    )
response.raise_for_status()
print(response.json())
```

**JavaScript**

```javascript
import { openAsBlob } from "node:fs";

const API = "https://convertr.org/api/v1";
const headers = { Authorization: `Bearer ${process.env.CONVERTR_API_KEY}` };

const form = new FormData();
form.append("output_format", "mp3");
form.append("file", await openAsBlob("clip.mp4"), "clip.mp4");

const response = await fetch(`${API}/conversions?wait=60`, {
  method: "POST",
  body: form,
  headers,
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
```

### 3. Download the result

A finished conversion has `status` set to `succeeded`. Download the file from `output.download_url`. The link works until `output.expires_at`.

```json
{
  "id": "9f1c2e7a-5b3d-4c8e-a1f0-6d2b7c9e4a31",
  "status": "succeeded",
  "progress": 100,
  "input": { "filename": "clip.mp4", "format": "mp4", "size_bytes": 4821337 },
  "output": {
    "filename": "clip.mp3",
    "format": "mp3",
    "size_bytes": 612044,
    "download_url": "https://gw1.convertr.org/api/download/...",
    "expires_at": "2026-10-31T10:15:04Z"
  },
  "credits_charged": 1,
  "error": null,
  "created_at": "2026-10-30T10:15:00Z",
  "finished_at": "2026-10-30T10:15:04Z"
}
```

```bash
curl -L -o clip.mp3 "https://gw1.convertr.org/api/download/..."
```

If the job needs longer than `wait`, the response has `status` set to `queued` or `processing`. Poll [GET /conversions/{id}](#get-conversion) until it ends.

## Authentication

Send your API key with every request, except for the two public format endpoints. Use one of these headers:

- `Authorization: Bearer cvk_...`
- `X-API-Key: cvk_...`

Create and revoke keys on the [API keys](https://convertr.org/api-keys) page. You need a verified email address. Keep keys on your server. Do not put them in browser code or in public repositories.

A missing or wrong key returns `401` with the error code `invalid_api_key`.

## Endpoints

Base URL: `https://convertr.org/api/v1`. Requests and responses use JSON, except for file uploads, which use `multipart/form-data`.

| Method | Path | Key | Purpose |
| --- | --- | --- | --- |
| GET | `/formats` | No | List every input format with its output formats. |
| GET | `/formats/{input}` | No | List the output formats of one input format. |
| POST | `/conversions` | Yes | Start a conversion from a file or a URL. |
| GET | `/conversions/{id}` | Yes | Read a conversion. Supports `wait`. |
| DELETE | `/conversions/{id}` | Yes | Delete a conversion and its files at once. |
| GET | `/account` | Yes | Read your plan, credits and limits. |
| GET | `/usage` | Yes | List credit charges, refunds and purchases. |
| GET | `/openapi.json` | No | OpenAPI 3.1 description of this API. |

### GET /formats

Public. Returns every input format with the output formats it can convert to. The `group` tells you the kind of output, such as Audio or Video.

**curl**

```bash
curl "https://convertr.org/api/v1/formats"
```

**Python**

```python
import requests

API = "https://convertr.org/api/v1"

response = requests.get(
    f"{API}/formats",
)
response.raise_for_status()
print(response.json())
```

**JavaScript**

```javascript
const API = "https://convertr.org/api/v1";

const response = await fetch(`${API}/formats`);
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
```

```json
{
  "data": [
    {
      "input": "mp4",
      "outputs": [
        { "format": "mp3", "group": "Audio" },
        { "format": "wav", "group": "Audio" },
        { "format": "webm", "group": "Video" }
      ]
    }
  ]
}
```

### GET /formats/{input}

Public. Returns the output formats of one input format. An unknown input returns `404` with the error code `not_found`.

**curl**

```bash
curl "https://convertr.org/api/v1/formats/mp4"
```

**Python**

```python
import requests

API = "https://convertr.org/api/v1"

response = requests.get(
    f"{API}/formats/mp4",
)
response.raise_for_status()
print(response.json())
```

**JavaScript**

```javascript
const API = "https://convertr.org/api/v1";

const response = await fetch(`${API}/formats/mp4`);
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
```

```json
{
  "input": "mp4",
  "outputs": [
    { "format": "mp3", "group": "Audio" },
    { "format": "wav", "group": "Audio" },
    { "format": "webm", "group": "Video" }
  ]
}
```

### POST /conversions

Starts a conversion. Send a file as `multipart/form-data`, or send a URL as JSON. Both return a [conversion object](#conversion-object).

| Field | Type | Description |
| --- | --- | --- |
| `file` | file | Multipart only. The file to convert. Required. |
| `url` | string | JSON only. A public URL of the file. Convertr downloads it. Required. |
| `output_format` | string | The format to convert to, such as `mp3`. Required. |
| `input_format` | string | The format of the input. Optional. The default is the file extension. |
| `filename` | string | JSON only. Name of the file, for example `x.mp4`. Optional. |
| `settings` | object | Conversion settings. Optional. In multipart requests, send it as a JSON string. |

In a multipart request, send `output_format` and `input_format` before the `file` part. You can also pass them as query parameters. `curl -F` keeps the order you write.

Add `?wait=N` (0 to 90 seconds) to hold the request until the job ends or N seconds pass. The response is `201` for a job that is still running. It is `200` when the job ended in the time you allowed, with `status` set to `succeeded` or `failed`.

**Upload a file:**

**curl**

```bash
curl "https://convertr.org/api/v1/conversions?wait=60" \
  -H "Authorization: Bearer $CONVERTR_API_KEY" \
  -F output_format=mp3 \
  -F file=@clip.mp4
```

**Python**

```python
import os
import requests

API = "https://convertr.org/api/v1"
headers = {"Authorization": f"Bearer {os.environ['CONVERTR_API_KEY']}"}

with open("clip.mp4", "rb") as source:
    response = requests.post(
        f"{API}/conversions",
        headers=headers,
        params={"wait": 60},
        data={"output_format": "mp3"},
        files={"file": source},
    )
response.raise_for_status()
print(response.json())
```

**JavaScript**

```javascript
import { openAsBlob } from "node:fs";

const API = "https://convertr.org/api/v1";
const headers = { Authorization: `Bearer ${process.env.CONVERTR_API_KEY}` };

const form = new FormData();
form.append("output_format", "mp3");
form.append("file", await openAsBlob("clip.mp4"), "clip.mp4");

const response = await fetch(`${API}/conversions?wait=60`, {
  method: "POST",
  body: form,
  headers,
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
```

**Convert from a URL:**

**curl**

```bash
curl "https://convertr.org/api/v1/conversions?wait=60" \
  -H "Authorization: Bearer $CONVERTR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/clip.mp4", "output_format": "mp3"}'
```

**Python**

```python
import os
import requests

API = "https://convertr.org/api/v1"
headers = {"Authorization": f"Bearer {os.environ['CONVERTR_API_KEY']}"}

response = requests.post(
    f"{API}/conversions",
    headers=headers,
    params={"wait": 60},
    json={"url": "https://example.com/clip.mp4", "output_format": "mp3"},
)
response.raise_for_status()
print(response.json())
```

**JavaScript**

```javascript
const API = "https://convertr.org/api/v1";
const headers = { Authorization: `Bearer ${process.env.CONVERTR_API_KEY}` };

const response = await fetch(`${API}/conversions?wait=60`, {
  method: "POST",
  body: JSON.stringify({"url": "https://example.com/clip.mp4", "output_format": "mp3"}),
  headers: { ...headers, "Content-Type": "application/json" },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
```

**Response without wait** (`201`):

```json
{
  "id": "9f1c2e7a-5b3d-4c8e-a1f0-6d2b7c9e4a31",
  "status": "queued",
  "progress": 0,
  "input": { "filename": "clip.mp4", "format": "mp4", "size_bytes": 4821337 },
  "output": null,
  "credits_charged": 1,
  "error": null,
  "created_at": "2026-10-30T10:15:00Z",
  "finished_at": null
}
```

### GET /conversions/{id}

Returns the current state of a conversion. It supports `?wait=N` as well, so you can poll with a long request instead of a short loop. An unknown id returns `404`.

**curl**

```bash
curl "https://convertr.org/api/v1/conversions/$CONVERSION_ID?wait=30" \
  -H "Authorization: Bearer $CONVERTR_API_KEY"
```

**Python**

```python
import os
import requests

API = "https://convertr.org/api/v1"
headers = {"Authorization": f"Bearer {os.environ['CONVERTR_API_KEY']}"}
conversion_id = "9f1c2e7a-5b3d-4c8e-a1f0-6d2b7c9e4a31"

response = requests.get(
    f"{API}/conversions/{conversion_id}",
    headers=headers,
    params={"wait": 30},
)
response.raise_for_status()
print(response.json())
```

**JavaScript**

```javascript
const API = "https://convertr.org/api/v1";
const headers = { Authorization: `Bearer ${process.env.CONVERTR_API_KEY}` };
const conversionId = "9f1c2e7a-5b3d-4c8e-a1f0-6d2b7c9e4a31";

const response = await fetch(`${API}/conversions/${conversionId}?wait=30`, {
  headers,
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
```

### DELETE /conversions/{id}

Deletes the input and output files of a conversion at once. Returns `204` with no body. Without a delete, Convertr removes files after 24 hours.

**curl**

```bash
curl -X DELETE "https://convertr.org/api/v1/conversions/$CONVERSION_ID" \
  -H "Authorization: Bearer $CONVERTR_API_KEY"
```

**Python**

```python
import os
import requests

API = "https://convertr.org/api/v1"
headers = {"Authorization": f"Bearer {os.environ['CONVERTR_API_KEY']}"}
conversion_id = "9f1c2e7a-5b3d-4c8e-a1f0-6d2b7c9e4a31"

response = requests.delete(
    f"{API}/conversions/{conversion_id}",
    headers=headers,
)
response.raise_for_status()
print(response.status_code)
```

**JavaScript**

```javascript
const API = "https://convertr.org/api/v1";
const headers = { Authorization: `Bearer ${process.env.CONVERTR_API_KEY}` };
const conversionId = "9f1c2e7a-5b3d-4c8e-a1f0-6d2b7c9e4a31";

const response = await fetch(`${API}/conversions/${conversionId}`, {
  method: "DELETE",
  headers,
});
if (!response.ok) throw new Error(await response.text());
console.log(response.status);
```

### GET /account

Returns your plan, free credits, paid credit lots and the largest file you can send. `plan` is `free`, `paid` or `unmetered`.

**curl**

```bash
curl "https://convertr.org/api/v1/account" \
  -H "Authorization: Bearer $CONVERTR_API_KEY"
```

**Python**

```python
import os
import requests

API = "https://convertr.org/api/v1"
headers = {"Authorization": f"Bearer {os.environ['CONVERTR_API_KEY']}"}

response = requests.get(
    f"{API}/account",
    headers=headers,
)
response.raise_for_status()
print(response.json())
```

**JavaScript**

```javascript
const API = "https://convertr.org/api/v1";
const headers = { Authorization: `Bearer ${process.env.CONVERTR_API_KEY}` };

const response = await fetch(`${API}/account`, {
  headers,
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
```

```json
{
  "user": { "id": 42, "name": "Ada", "email": "ada@example.com" },
  "plan": "paid",
  "free": { "monthly": 100, "used": 12, "remaining": 88, "resets_at": "2026-11-01T00:00:00Z" },
  "credits": {
    "balance": 3500,
    "lots": [
      { "id": 7, "credits": 4000, "remaining": 3500, "expires_at": "2027-10-12T09:30:00Z", "source": "purchase" }
    ]
  },
  "limits": { "max_file_bytes": 1073741824 }
}
```

### GET /usage

Lists credit movements, newest first. `kind` is `charge`, `refund`, `grant`, `purchase` or `expire`. Use `limit` and the `next_cursor` of the last page as `cursor` to read more.

**curl**

```bash
curl "https://convertr.org/api/v1/usage?limit=50" \
  -H "Authorization: Bearer $CONVERTR_API_KEY"
```

**Python**

```python
import os
import requests

API = "https://convertr.org/api/v1"
headers = {"Authorization": f"Bearer {os.environ['CONVERTR_API_KEY']}"}

response = requests.get(
    f"{API}/usage",
    headers=headers,
    params={"limit": 50},
)
response.raise_for_status()
print(response.json())
```

**JavaScript**

```javascript
const API = "https://convertr.org/api/v1";
const headers = { Authorization: `Bearer ${process.env.CONVERTR_API_KEY}` };

const response = await fetch(`${API}/usage?limit=50`, {
  headers,
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
```

```json
{
  "data": [
    { "id": 311, "kind": "charge", "credits": -1, "conversion_id": "9f1c2e7a-5b3d-4c8e-a1f0-6d2b7c9e4a31", "created_at": "2026-10-30T10:15:00Z" },
    { "id": 310, "kind": "purchase", "credits": 4000, "conversion_id": null, "created_at": "2026-10-12T09:30:00Z" }
  ],
  "next_cursor": null
}
```

### GET /openapi.json

Public. The [OpenAPI 3.1 file](https://convertr.org/api/v1/openapi.json) describes every endpoint. Use it to generate a client or to load the API into a tool.

## Conversion object

Every conversion endpoint returns this object.

| Field | Description |
| --- | --- |
| `id` | The conversion id. Use it to read or delete the conversion. |
| `status` | `queued`, `processing`, `succeeded` or `failed`. |
| `progress` | Progress from 0 to 100. |
| `input` | `filename`, `format` and `size_bytes` of the input. |
| `output` | `filename`, `format`, `size_bytes`, `download_url` and `expires_at`. It is `null` until the job succeeds. |
| `credits_charged` | Credits used for this conversion. |
| `error` | `code` and `message` when the job failed. Otherwise `null`. |
| `created_at`, `finished_at` | Timestamps in UTC. `finished_at` is `null` while the job runs. |

## Errors

Errors return JSON and a matching HTTP status.

```json
{
  "error": {
    "code": "insufficient_credits",
    "message": "Your account has no credits left. Buy credits or wait for the monthly free credits."
  }
}
```

| HTTP | Code | Meaning |
| --- | --- | --- |
| 401 | `invalid_api_key` | The key is missing, wrong or revoked. |
| 402 | `insufficient_credits` | You have no credits left. Buy credits or wait for the monthly free credits. |
| 403 | `email_not_verified` | Verify your email address first. |
| 404 | `not_found` | The conversion or the format does not exist. |
| 413 | `file_too_large` | The input is larger than your plan allows. |
| 415 | `unsupported_conversion` | Convertr cannot convert this input format to this output format. |
| 422 | `validation_failed` | A field is missing or has a wrong value. |
| 429 | `rate_limited` | Too many requests. Wait for the number of seconds in the `Retry-After` header. |
| 503 | `no_worker_available` | No conversion worker is free. Try again in a moment. |
| 500 | `internal_error` | Something went wrong on our side. Try again. |

A conversion that starts and then fails ends with `status` set to `failed` and an `error` object. Convertr refunds the credits of a failed conversion automatically.

## Credits and limits

- **Credits:** one credit pays for one conversion of an input up to 100 MB. Each further started 100 MB costs one more credit.
- **Free plan:** 100 credits per calendar month (UTC). It needs a verified email. The largest input is 100 MB.
- **Paid credits:** one-time packs, valid for 12 months. Convertr uses them after the monthly free credits. The largest input is 1 GB. Send files over 100 MB as a `url`, because uploads in the request body stop at 100 MB. The server behind a `url` must deliver the file within 75 seconds, on port 80 or 443.
- **Refunds:** failed conversions are refunded automatically.
- **Rate limits per key:** 60 conversion starts per minute and 600 other requests per minute. A limited request returns `429` and a `Retry-After` header.
- **Storage:** Convertr deletes files 24 hours after the conversion, or at once when you call `DELETE`.

See [Pricing](https://convertr.org/pricing) for the credit packs.

## MCP server

The MCP server lets AI agents and coding assistants convert files with the same API key, the same credits and the same limits. Add it to your client:

Endpoint: `https://convertr.org/mcp`. Send your key in the `X-API-Key` header.

### Claude Code

```bash
claude mcp add --transport http convertr https://convertr.org/mcp --header "X-API-Key: cvk_..."
```

### Claude Desktop and Cursor

Add this to the MCP settings file of the app:

```json
{
  "mcpServers": {
    "convertr": {
      "url": "https://convertr.org/mcp",
      "headers": { "X-API-Key": "cvk_..." }
    }
  }
}
```

### VS Code

Add this to `.vscode/mcp.json`:

```json
{
  "servers": {
    "convertr": {
      "type": "http",
      "url": "https://convertr.org/mcp",
      "headers": { "X-API-Key": "cvk_..." }
    }
  }
}
```

### Tools

| Tool | What it does |
| --- | --- |
| `list_formats` | Lists the supported input and output formats. |
| `convert_file` | Starts a conversion and returns the conversion. |
| `get_conversion` | Reads the state and the download link of a conversion. |
| `delete_conversion` | Deletes a conversion and its files. |
| `get_account` | Shows your plan, credits and limits. |

## For AI agents and tools

These files describe Convertr in a form that tools read well:

- [OpenAPI 3.1 file](https://convertr.org/api/v1/openapi.json)
- [llms.txt](https://convertr.org/llms.txt): a short map of the site, the API and the MCP server
- [llms-full.txt](https://convertr.org/llms-full.txt): the full guide, the prices and every supported format in one Markdown file
- [developers.md](https://convertr.org/developers.md): this page as Markdown
- [pricing.md](https://convertr.org/pricing.md): the pricing page as Markdown

