> For the full page index, fetch https://echonova.ai/llms.txt

# Sound Cleaning quickstart

Clean a recording of up to 10 minutes in one call, read the cleaned file link and its cost, tune the cleaner, then list your past jobs.

Sound Cleaning removes low rumble, compresses the dynamics, evens out the loudness and caps the peaks
of a recording. This tutorial cleans a short file with one synchronous call,
reads the link to the cleaned file, then tunes the cleaner and lists your
past jobs.

## What you need

- An API key. See [Get an API key](/docs/developers/get-an-api-key). If the
  key restricts services, it needs **Access** on **Sound Cleaning**.
- The base URL. See
  [Base URL](/docs/developers/base-url).
- Credits on the workspace. The charge is taken before cleaning starts. See
  [Credits](/docs/developers/credits).
- An audio file of 10 minutes or less and 150 MB or less, in MP3, WAV, FLAC or
  OGG/Opus.

## Steps

### Step 1: Clean a file

Send the file as the `file` part of a `multipart/form-data` request to
`POST https://api.echonova.ai/v1/sound-cleaning`. The call waits for the cleaner and
returns the result in the response. There is no asynchronous endpoint.

:::tabs

```bash title="cURL"
curl -X POST "https://api.echonova.ai/v1/sound-cleaning" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@noisy.wav;type=audio/wav"
```

```python
import requests

with open("noisy.wav", "rb") as audio:
    response = requests.post(
        "https://api.echonova.ai/v1/sound-cleaning",
        headers={"Authorization": "Bearer YOUR_API_KEY"},
        files={"file": ("noisy.wav", audio, "audio/wav")},
    )
print(response.json())
```

```javascript
import { readFile } from 'node:fs/promises';

const formData = new FormData();
formData.append('file', new Blob([await readFile('noisy.wav')], { type: 'audio/wav' }), 'noisy.wav');

const response = await fetch('https://api.echonova.ai/v1/sound-cleaning', {
  method: 'POST',
  headers: { Authorization: 'Bearer YOUR_API_KEY' },
  body: formData,
});
console.log(await response.json());
```

:::

> [!NOTE]
> The API checks the `Content-Type` of the `file` part and the file extension,
> and the two must agree. Use `audio/mpeg` with `.mp3`, `audio/wav` with
> `.wav`, `audio/flac` with `.flac`, and `audio/ogg` or `audio/opus` with
> `.ogg` or `.opus`. Set the type explicitly as the samples do.

### Step 2: Read the result

A successful call returns HTTP `200` and the standard envelope. The result is
in `data`:

```json
{
  "success": true,
  "statusCode": 200,
  "errorMessage": null,
  "data": {
    "id": "66e3f2a1c9d4b8a1f0e12345",
    "status": "completed",
    "fileName": "noisy.wav",
    "outputUrl": "https://storage.example.com/cleaned/66e3f2a1c9d4b8a1f0e12345.wav?signature=abc",
    "durationSeconds": 12,
    "creditsConsumed": 207,
    "metrics": {
      "sampleRate": 48000,
      "integratedLufs": -16.1,
      "truePeakDbfs": -1
    },
    "createdAt": "2026-10-11T10:21:04.512Z"
  }
}
```

| Field             | Type             | What it holds                                                                                                |
| ----------------- | ---------------- | ------------------------------------------------------------------------------------------------------------ |
| `id`              | string           | The job id. Read it again from `GET /v1/sound-cleaning/jobs/{jobId}`.                                        |
| `status`          | string           | `completed` on a successful call.                                                                            |
| `fileName`        | string           | The name of the file you uploaded.                                                                           |
| `outputUrl`       | string           | A time-limited link to the cleaned WAV. Download it, or read the job again for a fresh link.                 |
| `durationSeconds` | number or absent | The length of the audio, rounded to whole seconds. The charge uses the unrounded length: 12.4 s costs 207.   |
| `creditsConsumed` | number           | The credits charged, a whole number.                                                                         |
| `options`         | object or absent | The tuning values you sent. Absent when you sent none.                                                       |
| `metrics`         | object or absent | What the cleaner measured on the result: `sampleRate`, `integratedLufs` and `truePeakDbfs`, when it reports. |
| `error`           | string or absent | The reason, on a job that failed.                                                                            |
| `createdAt`       | string           | When the job was created, in ISO 8601.                                                                       |

The cleaned file is always a WAV. The link expires, so download the file
when you get it, or fetch the job again later for a new link.

### Step 3: Tune the cleaner

Eight optional form fields change how the cleaner works. Leave them out and
the cleaner uses its defaults. A value outside its range is rejected with
`400`.

| Field           | Meaning                            | Range      | Default |
| --------------- | ---------------------------------- | ---------- | ------- |
| `cutoffFreq`    | High-pass cutoff in Hz             | 20 to 300  | 90      |
| `qFactor`       | High-pass smoothness               | 0.1 to 5   | 0.707   |
| `targetLufs`    | Target loudness in LUFS            | -30 to -6  | -16     |
| `truePeakDbfs`  | Peak ceiling in dBFS               | -6 to 0    | -1      |
| `compThreshDb`  | Compressor threshold in dBFS       | -30 to 0   | -3.8    |
| `compRatio`     | Compressor ratio                   | 1 to 20    | 4       |
| `compAttackMs`  | Compressor attack in milliseconds  | 0.1 to 100 | 5       |
| `compReleaseMs` | Compressor release in milliseconds | 1 to 1000  | 50      |

```bash
curl -X POST "https://api.echonova.ai/v1/sound-cleaning" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@noisy.wav;type=audio/wav" \
  -F "targetLufs=-18" \
  -F "cutoffFreq=120"
```

### Step 4: List your jobs

`GET https://api.echonova.ai/v1/sound-cleaning/jobs` returns the API jobs of your
workspace, newest first, 20 per page. Add `offset` and `limit` (1 to 100) to page.
`GET https://api.echonova.ai/v1/sound-cleaning/jobs/{jobId}` returns one job with a
fresh `outputUrl`.

```bash
curl "https://api.echonova.ai/v1/sound-cleaning/jobs?offset=0&limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Any key in the workspace with Sound Cleaning access sees these jobs, whichever
key created them. Jobs made in the portal are not listed here.

## What happens next

- The price is 1,000 credits per minute of audio, charged per second, with a
  minimum of 1 second. The API measures the decoded audio, not the file
  header, so a file of 12.4 seconds costs 207 credits (206.67, rounded to a whole credit). See
  [Credits](/docs/developers/credits).
- The workspace is charged before cleaning starts and refunded if cleaning
  fails. The key's own usage counter is checked against its **Credit Limit**
  first; a call that would pass the limit is rejected with `429` and nothing
  is charged.
- Every call creates a job, so the work stays listed under your key.

## Limits

| Limit                    | Value                                           | Applies to                    |
| ------------------------ | ----------------------------------------------- | ----------------------------- |
| Audio duration           | 10 minutes                                      | `POST /v1/sound-cleaning`     |
| File size                | 150 MB                                          | `POST /v1/sound-cleaning`     |
| Formats                  | MP3, WAV, FLAC, OGG/Opus                        | `POST /v1/sound-cleaning`     |
| `limit` query parameter  | 1 to 100, default 20                            | `GET /v1/sound-cleaning/jobs` |
| `offset` query parameter | 0 or more, default 0                            | `GET /v1/sound-cleaning/jobs` |
| Requests per key         | See [Rate limits](/docs/developers/rate-limits) | Every endpoint                |

## Troubleshooting

### 400 File is required

The request had no part named `file`. Check the field name and that the
request is `multipart/form-data`.

### 400 Unsupported audio format. Sound cleaning accepts MP3, WAV, FLAC or OGG/Opus.

The file extension or its `Content-Type` is not one of the four formats, or
the two do not match. A `.wav` file sent as `audio/mpeg` is refused.

### 400 Audio exceeds the 10-minute limit.

The audio is longer than 10 minutes. Cut it into pieces of 10 minutes or
less and send each one.

### 400 targetLufs must not be greater than -6

A tuning field is outside its range in the table in Step 3; the message names
the field and the bound. A form field that is not one of the eight, such as
`volume`, is refused with `property volume should not exist`.

### 413 File too large

The upload is over 150 MB. The request is cut off before the file is read.

### 503 Sound cleaning is temporarily unavailable. Please try again.

The cleaner could not be reached. The charge is refunded. Retry once.

### 404 Clean Job not found

The `jobId` does not exist, or it belongs to another workspace or was made in the portal.

### 403 This API key does not have access to Sound Cleaning services

The key restricts services and Sound Cleaning is not among them. **Edit** the
key in the portal and turn **Sound Cleaning** on. Keys that have no service
restriction can use it already.

### 402 Insufficient subscription balance

The workspace cannot cover the charge. Nothing ran.

## FAQ

### Can I clean a file longer than 10 minutes?

Not through the API today. Cut it into pieces of 10 minutes or less and send
each one.

### Is there an asynchronous endpoint?

No. The cleaner answers in the same call, so the endpoint is synchronous
only.

## Related

- [Clean audio](/docs/reference/clean-create)
- [List Sound Cleaning jobs](/docs/reference/clean-list-jobs)
- [Get Sound Cleaning job details](/docs/reference/clean-get-job)
- [Credits](/docs/developers/credits)
- [Rate limits](/docs/developers/rate-limits)
- [Clean a recording](/docs/playground/clean-a-recording)
