Quick Start
Make your first API call — examples in Shell, PHP, Java, and Ruby.
Use this pre-built prompt to get started faster.
Get your API key
Go to app.cleanvoice.ai/developer/api-keys and create a new key.
export CLEANVOICE_API_KEY="your_api_key_here"Try it with your key
Try with your API key
Paste your key — all code examples on this page update instantly. Stays in your browser only, never sent anywhere.
The Cleanvoice REST API works with any HTTP client. Choose your language below — all examples use only standard library or widely-available HTTP clients.
Submit an edit
If your file is already online, pass the URL directly:
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://example.com/episode.mp3"],
"config": {
"fillers": true,
"long_silences": true,
"normalize": true
}
}
}'<?php
$payload = json_encode([
'input' => [
'files' => ['https://example.com/episode.mp3'],
'config' => ['fillers' => true, 'long_silences' => true, 'normalize' => true],
],
]);
$ch = curl_init('https://api.cleanvoice.ai/v2/edits');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . getenv('CLEANVOICE_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => $payload,
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $response['id']; // edit_abc123import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
String body = """
{
"input": {
"files": ["https://example.com/episode.mp3"],
"config": {"fillers": true, "long_silences": true, "normalize": true}
}
}""";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.cleanvoice.ai/v2/edits"))
.header("X-API-Key", System.getenv("CLEANVOICE_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); // {"id":"edit_abc123"}require 'net/http'
require 'json'
uri = URI('https://api.cleanvoice.ai/v2/edits')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['X-API-Key'] = ENV['CLEANVOICE_API_KEY']
request['Content-Type'] = 'application/json'
request.body = JSON.generate({
input: {
files: ['https://example.com/episode.mp3'],
config: { fillers: true, long_silences: true, normalize: true },
},
})
response = JSON.parse(http.request(request).body)
puts response['id'] # edit_abc123{ "id": "edit_abc123" }Save the id — you'll use it to poll for the result in the next step.
If your file is local, upload it first — see Uploads for full language examples. The three-step flow is:
POST /v2/upload?filename=...→ getsignedUrlPUTyour file tosignedUrl- Strip the query string from
signedUrl, then submit the edit using that file URL
Poll for the result
#!/bin/bash
EDIT_ID="edit_abc123"
sleep 30 # wait before first poll
while true; do
RESPONSE=$(curl -s "https://api.cleanvoice.ai/v2/edits/$EDIT_ID" \
-H "X-API-Key: $CLEANVOICE_API_KEY")
STATUS=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
echo "Status: $STATUS"
if [ "$STATUS" = "SUCCESS" ]; then
URL=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['result']['download_url'])")
echo "Download URL: $URL"
break
elif [ "$STATUS" = "FAILURE" ]; then
echo "Edit failed"
break
fi
sleep 10
done<?php
$editId = 'edit_abc123';
$apiKey = getenv('CLEANVOICE_API_KEY');
sleep(30); // wait before first poll
while (true) {
$ch = curl_init("https://api.cleanvoice.ai/v2/edits/$editId");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: $apiKey"],
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
echo 'Status: ' . $data['status'] . PHP_EOL;
if ($data['status'] === 'SUCCESS') {
echo 'Download URL: ' . $data['result']['download_url'] . PHP_EOL;
break;
} elseif ($data['status'] === 'FAILURE') {
echo 'Edit failed' . PHP_EOL;
break;
}
sleep(10);
}import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
String apiKey = System.getenv("CLEANVOICE_API_KEY");
String editId = "edit_abc123";
HttpClient client = HttpClient.newHttpClient();
Thread.sleep(30_000); // wait before first poll
while (true) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.cleanvoice.ai/v2/edits/" + editId))
.header("X-API-Key", apiKey)
.GET()
.build();
String body = client.send(request, HttpResponse.BodyHandlers.ofString()).body();
System.out.println(body);
if (body.contains("\"SUCCESS\"")) {
// extract download_url with your preferred JSON library
break;
} else if (body.contains("\"FAILURE\"")) {
break;
}
Thread.sleep(10_000);
}require 'net/http'
require 'json'
edit_id = 'edit_abc123'
api_key = ENV['CLEANVOICE_API_KEY']
uri = URI("https://api.cleanvoice.ai/v2/edits/#{edit_id}")
sleep 30 # wait before first poll
loop do
request = Net::HTTP::Get.new(uri)
request['X-API-Key'] = api_key
data = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
JSON.parse(http.request(request).body)
end
puts "Status: #{data['status']}"
if data['status'] == 'SUCCESS'
puts "Download URL: #{data.dig('result', 'download_url')}"
break
elsif data['status'] == 'FAILURE'
puts 'Edit failed'
break
end
sleep 10
end{
"task_id": "edit_abc123",
"status": "SUCCESS",
"result": {
"download_url": "https://cdn.cleanvoice.ai/cleaned/edit_abc123.mp3",
"transcription": null,
"summarization": null,
"social_content": []
}
}result.download_url is a time-limited link to the cleaned audio file. Download it promptly after the job succeeds.
If you enable text outputs, the raw polling payload also uses result.transcription, result.summarization, and result.social_content. A recent live social_content=true run returned social_content as an object with newsletter, twitter_thread, and linkedin, and remained in POSTPROCESSING for several polls before SUCCESS.
Processing typically takes ~30 seconds for a 2–3 minute clip and 5–10 minutes for a 1-hour file. Wait at least 30 seconds before your first poll, then poll every 10 seconds.
Common presets
Noise reduction and loudness normalization — language-agnostic.
{
"input": {
"files": ["https://example.com/episode.mp3"],
"config": {
"remove_noise": true,
"studio_sound": true,
"normalize": true
}
}
}Full spoken-word cleanup: fillers, silences, mouth sounds, breathing, stutters.
{
"input": {
"files": ["https://example.com/episode.mp3"],
"config": {
"fillers": true,
"long_silences": true,
"mouth_sounds": true,
"breath": true,
"stutters": true,
"remove_noise": true,
"normalize": true
}
}
}Pass a video URL and set video to true — otherwise the file is treated as audio-only.
{
"input": {
"files": ["https://example.com/video.mp4"],
"config": {
"video": true,
"remove_noise": true,
"studio_sound": true,
"normalize": true
}
}
}Minimal processing — normalize loudness and trim excessive silences.
{
"input": {
"files": ["https://example.com/episode.mp3"],
"config": {
"long_silences": true,
"normalize": true
}
}
}Deliver results to your storage
Pass a pre-signed PUT URL in signed_url and Cleanvoice will upload the cleaned file directly to your bucket.
{
"input": {
"files": ["https://example.com/episode.mp3"],
"config": {
"signed_url": "https://your-bucket.s3.amazonaws.com/cleaned.mp3?X-Amz-Signature=...",
"fillers": true,
"normalize": true
}
}
}