# Free API and REST API

## Free API
No key and no account. Send a file to `POST https://audo.ai/api/try/<tool>` and get the result back. Each tool processes the first part of the file for free, a few times a day.

```bash
curl -F file=@interview.wav https://audo.ai/api/try/remove-noise -D response.headers -o response.body
```

The file-expiry periods below apply to Audo's copies. AI processing providers may retain data under their own terms; see the [privacy policy](https://audo.ai/privacy). Audo expiry does not establish deletion timing at a downstream provider.

### Inputs

- `file`, or `url` for a public HTTPS link, for tools that take one file. Send the body as `multipart/form-data`, or as JSON when every input is a `url`.
- Join takes `file` (or `url`) 2 to 20 times, in order.
- Swap audio takes `video` and `audio` (or `video_url` and `audio_url`).
- Align a script takes `file`, plus a `text` field or a `script` file (TXT, SRT, or VTT, up to 2 MB).
- Options are the tool's inputs, with the same names and defaults as the MCP tools. Send numbers and booleans as text, and arrays, such as cut's `ranges`, as JSON. IDs and `idempotency_key` aren't accepted, and `free` and `max_credits` need an API key.
- Transcription returns `json`, `srt`, `vtt`, or `txt`. Alignment returns `json`, `srt`, or `vtt` (no TXT output).
- A request takes up to 100 MB of files, with or without a key; without one, what its links fetch counts too. Links are fetched one at a time. Please send a User-Agent that names your agent.

### Responses
The curl examples save `response.headers` and `response.body`, which are not yet media. Read the HTTP status and Content-Type before naming the result. A 200 JSON response can be a transcript, media report, or a job manifest with multiple output links. A 202 response is unfinished work. A 4xx or 5xx response is an error, even when a file was written.

For a complete single-file recipe, [download and inspect the Python client](https://audo.ai/examples/free-api.py). It uses Python 3 and ffprobe, handles synchronous media and text/JSON, polls only the returned HTTPS Audo job link, detects terminal failure, validates every downloaded output, and saves to a new directory. It refuses redirects and unrelated origins, and never executes returned commands or prints capability links. The in-memory example handles files below 100 MB; join, mix, replacement and script uploads need their documented multi-input layouts.

```bash
python3 free-api.py remove-noise interview.wav noise-results
# Keep only seconds 1 through 4 (mode=remove would delete them):
python3 free-api.py cut interview.wav cut-results '{"mode":"keep","ranges":[[1,4]]}'
```

Anonymous filler previews are review-only: to make automatic cuts, send the same original to remove-fillers with dry_run=false, without transcript_file_id or cut_ids. This transcribes again and uses another free use. To select preview cuts or reuse a transcript, sign in, upload the original on that account, and create a new preview through REST /api/v1/jobs or MCP remove_fillers. Anonymous IDs cannot be reused in that account workflow.


- The request waits up to about 30 seconds and returns the result itself: the audio or video file, the transcript, or the media info JSON.
- Send `Prefer: respond-async` to get `202` as soon as the upload is checked.
- A job that's still running returns `202` with a link to check on it: `/api/try/jobs/<job_id>?t=<token>`. The link works for 1 hour.
- `RateLimit-Policy` and `RateLimit` say how many free uses are left. `Audo-Processed-Seconds` says how much of the file was processed, and `Audo-Full-Price-Credits` gives the price of the whole file.
- When the free uses run out, the API returns `429` with JSON that says which limit and when it resets, and still takes the file: it waits for payment, and `details` has `finishUrl` (the person pays there, from $5, no sign-up, and the whole file runs), `finishPriceCredits`, `jobId`, and `statusUrl`, whose `finishedBy` names the whole-file job once paid. One address can have 3 waiting at once in an hour.
- **A result cut short has a finish link.** JSON results carry `finishUrl` and `finishPriceCredits` with a `nextStep`, and a file returned directly carries `Audo-Finish-Url` and `Audo-Finish-Price-Credits`. Give the link to the person: they pay there (from $5, no sign-up), and the whole file runs. Keep checking the job's status link: once paid, `finishedBy` names the whole-file job with its own status link, which gives the whole result. The link works while the free run's files are kept (an hour).

### Whole files with an API key
Send an API key from the [API page](https://audo.ai/keys), and the same request runs on your account: omitting `free` processes the whole file with credits when the available balance covers it, otherwise the free portion if a free use is available. Credits are charged only if the job succeeds. Add an `Idempotency-Key` header (a new UUID for each request) to retry safely: the same key returns the same job. Uploads are still up to 100 MB; send a larger file with the [REST API](https://audo.ai/docs/api#rest-api), which uploads in parts.

```bash
curl -H "Authorization: Bearer $AUDO_API_KEY" -F file=@interview.wav -F free=false -F max_credits="$APPROVED_MAX_CREDITS" https://audo.ai/api/try/remove-noise -D response.headers -o response.body
```

Set APPROVED_MAX_CREDITS to a positive integer explicitly approved by the person before using this paid example.

| Keyed request | Balance covers whole file | Insufficient balance |
|---|---|---|
| free omitted | Paid whole file, even if free quota is exhausted | Free portion if quota available; otherwise 402 |
| free=true | Free portion; 429 if quota exhausted | Free portion; 429 if quota exhausted |
| free=false | Paid whole file | 402; no fallback |

A disabled free tier is also unavailable. A positive `max_credits` caps paid cost (402 if exceeded); it does not prohibit spending. Use `free=true` to prohibit spending. Media info always runs free and counts against its daily quota. Report `processedSeconds` and `free` from the job, or Audo-Processed-Seconds for synchronous results, so the person knows if only a portion was processed.

### Free limits

| Tool | Free API | Free covers | Free uses a day |
|---|---|---|---|
| Remove noise | `remove-noise` | First minute | 3 |
| Enhance voice | `enhance-voice` | First minute | 3 |
| Transcribe | `transcribe` | First 2 minutes | 3 |
| Align a script | `align-script` | First 2 minutes | 3 |
| Cut | `cut` | First hour | 10 |
| Remove silence | `remove-silence` | First hour | 10 |
| Set loudness | `loudness` | First hour | 10 |
| Convert | `convert` | First hour | 10 |
| Join files | `join` | First hour | 10 |
| Swap a video's audio | `swap-audio` | First hour | 10 |
| Media info | `media-info` | Whole file | 20 |
| Remove filler words | `remove-fillers` | First 2 minutes | 3 |
| Level speakers | `level-speakers` | First 3 minutes | 3 |
| Mix tracks | `mix` | First 3 minutes | 3 |

Uses are counted per tool, per UTC day. The website and the API share one count for each visitor.

Without a key, one address can send 2 uploads at once (an account, 4). Another upload while those are still arriving gets `429` with `Retry-After`; send it again once one finishes.

### Every tool

### Remove noise

```bash
curl -F file=@interview.wav https://audo.ai/api/try/remove-noise -D response.headers -o response.body
```

### Enhance voice

```bash
curl -F file=@voice-note.m4a https://audo.ai/api/try/enhance-voice -D response.headers -o response.body
```

### Transcribe

```bash
curl -F file=@interview.mp3 -F format=srt https://audo.ai/api/try/transcribe -D response.headers -o response.body
```

### Align a script

```bash
curl -F file=@talk.mp3 -F script=@script.txt -F format=srt https://audo.ai/api/try/align-script -D response.headers -o response.body
```

### Cut

```bash
curl -F file=@episode.mp3 -F mode=remove -F 'ranges=[[0,12.5]]' https://audo.ai/api/try/cut -D response.headers -o response.body
```

### Remove silence

```bash
curl -F file=@episode.wav https://audo.ai/api/try/remove-silence -D response.headers -o response.body
```

### Set loudness

```bash
curl -F file=@episode.wav -F target=podcast https://audo.ai/api/try/loudness -D response.headers -o response.body
```

### Convert

```bash
curl -F file=@episode.wav -F format=mp3 https://audo.ai/api/try/convert -D response.headers -o response.body
```

### Join files

```bash
curl -F file=@intro.mp3 -F file=@episode.mp3 -F file=@outro.mp3 https://audo.ai/api/try/join -D response.headers -o response.body
```

### Swap a video's audio
```bash
curl -F video=@talk.mp4 -F audio=@talk-clean.wav https://audo.ai/api/try/swap-audio -D response.headers -o response.body
```

### Media info

```bash
curl -F file=@interview.wav https://audo.ai/api/try/media-info -D response.headers -o response.body
```

### Remove filler words

```bash
curl -F file=@interview.mp3 https://audo.ai/api/try/remove-fillers -D response.headers -o response.body
```

### Level speakers

```bash
curl -F file=@podcast.wav https://audo.ai/api/try/level-speakers -D response.headers -o response.body
```

### Mix tracks

```bash
curl -F file=@voice.wav -F file=@music.mp3 -F 'tracks=[{"role":"voice"},{"role":"music","volume_db":-6}]' -F duck=true https://audo.ai/api/try/mix -D response.headers -o response.body
```

## REST API
The REST API uses the same paid/free choice matrix above: omitted free prefers paid whole files, then falls back to an available free portion. Explicit free=false requires credits for the whole file; use an approved positive max_credits. Its base address is `https://audo.ai/api/v1`. Send an API key from the [API page](https://audo.ai/keys) as a bearer token:

```bash
curl https://audo.ai/api/v1/account -H "Authorization: Bearer $AUDO_API_KEY"
```

### Run a tool

1. `POST /api/v1/files` with `{"filename": "interview.wav", "sizeBytes": 48213000}` returns the file and how to upload it: one link in `upload` (up to 100 MB), or, for files over 16 MB, `multipart` with the part size, a part link, and a complete link, all under one token.
2. Upload the bytes with `PUT` to `upload.url`. For parts, `PUT` part n (counting from 1) to `multipart.partUrl` with `{n}` replaced by n, then `POST` to `multipart.completeUrl`, which joins them or lists the parts to send again.
3. `GET /api/v1/files/<file_id>?wait=30` until its status is `ready`. Its `prices` gives each tool's price for the file, in credits.
4. `POST /api/v1/jobs` with the tool and its inputs, in snake_case: `{"tool": "remove_noise", "file_id": "file_8Kc2QmP4", "idempotency_key": "<a new UUID>", "free": true}`. Add `max_credits` to cap the price, or `"free": true` for the free part.
5. `GET /api/v1/jobs/<job_id>` until its status is `succeeded`, `failed`, or `cancelled`. A job is charged only if it succeeds.
6. `GET /api/v1/files/<output_file_id>/download` returns a link that works for 15 minutes.

### Endpoints

| Method and path | What it does |
|---|---|
| `GET /api/v1/account` | Balance (in credits and in US cents: `balanceCents`, `availableCents`), free uses left today, prices, and limits |
| `GET /api/v1/ledger` | Credit history |
| `GET /api/v1/auto-refill` | Auto-refill, read-only: on or off, and its threshold, amount, and monthly limit in US cents (`thresholdCents`, `amountCents`, `monthlyCapCents`), and the card. Only the person changes it, at https://audo.ai/keys#auto-refill |
| `POST /api/v1/files` | Create a file and get an upload link |
| `GET /api/v1/files` | List recent files |
| `GET /api/v1/files/<id>` | A file's status, length, and prices |
| `GET /api/v1/files/<id>/download` | A download link |
| `DELETE /api/v1/files/<id>` | Delete a file now |
| `POST /api/v1/quote` | The price of a tool for some files, and the free uses left |
| `POST /api/v1/jobs` | Start a tool |
| `GET /api/v1/jobs` | List jobs, or one tool's with `?tool=` |
| `GET /api/v1/jobs/<id>` | A job's status, progress, stage, rough time left (`estimatedSecondsLeft`), and outputs |
| `POST /api/v1/jobs/<id>/cancel` | Cancel a job that hasn't finished |

### Errors

Errors are JSON: `{"error": {"code": "...", "message": "...", "retryable": false, "nextAction": "..."}}`. The message says what went wrong, and `nextAction` says what to do. Codes: `bad_request`, `unauthorized`, `forbidden`, `not_found`, `conflict`, `idempotency_key_reused`, `insufficient_balance`, `over_spending_cap`, `free_limit_reached`, `free_budget_exhausted`, `free_disabled`, `file_not_ready`, `file_invalid`, `file_expired`, `file_too_large`, `file_too_long`, `unsupported_format`, `wrong_file_type`, `text_too_long`, `no_audio`, `ranges_outside`, `fetch_failed`, `limit_exceeded`, `upload_limit_reached`, `rate_limited`, `job_not_cancellable`, `tool_unavailable`, `build_mismatch`, `internal_error`.

### OpenAPI

The full description of the free API and the REST API is at https://audo.ai/api/openapi.json.
