CleanvoiceDocs
REST API

Recommendations

Recommended patterns for high-volume and production audio processing with the REST API.

Choosing the right breath mode

ValueWhen to use
trueRecommended for most recordings. Best default for noisy or challenging audio.
"legacy"Source audio is already clean and well-recorded. Conservative, predictable removal.
"natural"You want a lighter touch that preserves more of the speaker's natural breathing.
falseLeave breathing untouched (default).

Choosing the right studio_sound mode

ValueWhen to use
trueRecommended. Aggressive enhancement for studio-quality output.
"nightly"Advanced/experimental variant. Currently behaves similarly to true.
falseDisabled (default). Use remove_noise alone for lighter cleanup.

autoeq is legacy and will be removed in a future release. Use studio_sound instead.


Preserve original timing with muted

If you need the output to keep the exact same duration as the input — for example, to stay in sync with a video timeline or subtitle file — set "muted": true. Edits are silenced instead of cut, so no content shifts in time.


Video: send audio only when you're enhancing, not editing

When you want to edit a video — remove filler words, cut silences, trim stutters — you must send the video file directly. Cleanvoice applies cuts to the video timeline and returns a shortened video with the edits applied.

For REST requests, you must set "video": true inside config for actual video editing. Without it, the file is treated as audio-only and you will not get an edited video back.

When you only want to enhance audio (noise reduction, studio sound, normalization) without any cuts, the video stream is irrelevant. Extract the audio, process the small file, then mux the cleaned audio back. The upload can be 10–50× smaller and you get the same result.

Job typeWhat to send
fillers, long_silences, stutters, mouth_sounds, breathSend the video file directly
remove_noise, studio_sound, normalize onlyExtract audio → process → mux back

Enhancement-only pattern:

# 1. Extract audio (no re-encode)
ffmpeg -i input.mp4 -vn -acodec copy audio.aac

# 2. Upload audio.aac to Cleanvoice
curl -X POST https://api.cleanvoice.ai/v2/uploads \
  -H "X-API-Key: $CLEANVOICE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename": "audio.aac", "content_type": "audio/aac"}'
# → returns { "upload_url": "...", "file_url": "..." }

curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: audio/aac" \
  --data-binary @audio.aac

# 3. Submit the edit using file_url
curl -X POST https://api.cleanvoice.ai/v2/edits \
  -H "X-API-Key: $CLEANVOICE_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"input\": {
      \"files\": [\"$FILE_URL\"],
      \"config\": {
        \"video\": true,
        \"remove_noise\": { \"enabled\": true },
        \"normalize\": { \"enabled\": true }
      }
    }
  }"

# 4. Poll until SUCCESS, download result.url as cleaned.aac

# 5. Mux cleaned audio back into original video
ffmpeg -i input.mp4 -i cleaned.aac -c:v copy -c:a copy -map 0:v:0 -map 1:a:0 output.mp4

-acodec copy extracts the audio stream without re-encoding it. The original audio quality is preserved. Same for the mux step — -c:v copy -c:a copy just remuxes streams without transcoding.


Deliver results directly to your storage

By default, Cleanvoice hosts the cleaned file and you download it from our servers. At scale, this means every file travels the full round trip: your servers → Cleanvoice → your servers.

Use signed_url to eliminate the return leg. Generate a pre-signed PUT URL for your S3/GCS/Azure bucket before submitting the job. Cleanvoice will PUT the cleaned file directly into your storage — you never need to download it.

curl -X POST https://api.cleanvoice.ai/v2/edits \
  -H "X-API-Key: $CLEANVOICE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "files": ["https://storage.example.com/audio.aac"],
      "signed_url": "https://my-bucket.s3.amazonaws.com/cleaned/episode.mp3?X-Amz-Signature=...",
      "config": {
        "fillers": { "enabled": true },
        "normalize": { "enabled": true }
      }
    }
  }'

When signed_url is set, Cleanvoice PUTs the cleaned file directly to your bucket. result.url in the response will point to your own storage URL.

The signed_url must be a PUT URL, not a GET URL. Most S3-compatible storage providers support pre-signed PUT URLs. The URL must remain valid until Cleanvoice finishes processing — use at least a 1-hour expiry for long files.


Upload once, reuse across requests

If you need to process the same file multiple times with different settings (or retry a failed job), upload it once and reuse the URL. The uploaded URL is not public — it's a private Cleanvoice storage URL only accessible by your API key.

# Upload once
curl -X POST https://api.cleanvoice.ai/v2/uploads \
  -H "X-API-Key: $CLEANVOICE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename": "episode.mp3", "content_type": "audio/mpeg"}'
# → { "upload_url": "...", "file_url": "https://storage.cleanvoice.ai/uploads/..." }

curl -X PUT "$UPLOAD_URL" -H "Content-Type: audio/mpeg" --data-binary @episode.mp3

# Reuse FILE_URL for multiple jobs — no re-upload needed
curl -X POST https://api.cleanvoice.ai/v2/edits \
  -H "X-API-Key: $CLEANVOICE_API_KEY" -H "Content-Type: application/json" \
  -d "{\"input\": {\"files\": [\"$FILE_URL\"], \"config\": {\"fillers\": {\"enabled\": true}}}}"

curl -X POST https://api.cleanvoice.ai/v2/edits \
  -H "X-API-Key: $CLEANVOICE_API_KEY" -H "Content-Type: application/json" \
  -d "{\"input\": {\"files\": [\"$FILE_URL\"], \"config\": {\"transcription\": {\"enabled\": true}}}}"

The file_url returned by /v2/uploads is not a public URL — it's a private Cleanvoice storage URL. It can only be used in POST /v2/edits requests authenticated with your API key. Uploaded files are retained for 7 days.


Batch processing

Submit all jobs first, then poll in a loop — never wait serially between files.

# Submit all jobs
ID1=$(curl -s -X POST https://api.cleanvoice.ai/v2/edits \
  -H "X-API-Key: $CLEANVOICE_API_KEY" -H "Content-Type: application/json" \
  -d '{"input": {"files": ["https://example.com/ep1.mp3"], "config": {"normalize": {"enabled": true}}}}' \
  | jq -r '.id')

ID2=$(curl -s -X POST https://api.cleanvoice.ai/v2/edits \
  -H "X-API-Key: $CLEANVOICE_API_KEY" -H "Content-Type: application/json" \
  -d '{"input": {"files": ["https://example.com/ep2.mp3"], "config": {"normalize": {"enabled": true}}}}' \
  | jq -r '.id')

# Poll both
for ID in $ID1 $ID2; do
  while true; do
    STATUS=$(curl -s "https://api.cleanvoice.ai/v2/edits/$ID" \
      -H "X-API-Key: $CLEANVOICE_API_KEY" | jq -r '.status')
    [ "$STATUS" = "SUCCESS" ] && break
    [ "$STATUS" = "FAILURE" ] && echo "Failed: $ID" && break
    sleep 10
  done
done

Do not pass multiple unrelated files in one POST /v2/edits request to simulate batching — that activates multi-track mode (for interviews recorded on separate mics). Submit one request per file.