CleanvoiceDocs
REST API

Retrieve an Edit

Poll for the status and result of an edit job.

Endpoint

GET /v2/edits/{edit_id}

Try with your API key

Paste your key — all code examples on this page update instantly. Stays in your browser only, never sent anywhere.

Single request

curl https://api.cleanvoice.ai/v2/edits/edit_abc123 \
  -H "X-API-Key: $CLEANVOICE_API_KEY"
<?php
$editId = 'edit_abc123';

$ch = curl_init("https://api.cleanvoice.ai/v2/edits/$editId");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['X-API-Key: ' . getenv('CLEANVOICE_API_KEY')],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($response);
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

String editId = "edit_abc123";

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.cleanvoice.ai/v2/edits/" + editId))
    .header("X-API-Key", System.getenv("CLEANVOICE_API_KEY"))
    .GET()
    .build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
require 'net/http'
require 'json'

edit_id = 'edit_abc123'
uri     = URI("https://api.cleanvoice.ai/v2/edits/#{edit_id}")
http    = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Get.new(uri)
request['X-API-Key'] = ENV['CLEANVOICE_API_KEY']

response = JSON.parse(http.request(request).body)
puts response

While processing (PENDING, STARTED, or one of the worker phase statuses):

{
  "task_id": "edit_abc123",
  "status": "PENDING"
}

On success:

{
  "task_id": "edit_abc123",
  "status": "SUCCESS",
  "result": {
    "download_url": "https://cdn.cleanvoice.ai/cleaned/edit_abc123.mp3",
    "transcription": null,
    "summarization": null,
    "social_content": []
  }
}

On failure:

{
  "task_id": "edit_abc123",
  "status": "FAILURE"
}

Anonymized live examples

These examples were captured from real GET /v2/edits/{id} responses and then shortened and anonymized for documentation. URLs, transcript text, summaries, and social copy are intentionally trimmed.

Success with summarization

{
  "task_id": "edit_abc123",
  "status": "SUCCESS",
  "result": {
    "video": false,
    "filename": "anonymized_clean.wav",
    "download_url": "https://storage.example.com/uploads/.../anonymized_clean.wav?...",
    "summarization": {
      "title": "[Anonymized title]",
      "summary": "[Anonymized summary text about avoiding sticky foods and protecting braces.]",
      "chapters": [
        {
          "start": 0.11,
          "title": "[Anonymized chapter 1]"
        },
        {
          "start": 17.68,
          "title": "[Anonymized chapter 2]"
        }
      ],
      "summaries": [
        "[Anonymized long-form summary text.]"
      ],
      "key_learnings": "- [Anonymized takeaway 1]\n- [Anonymized takeaway 2]",
      "summary_of_summary": "[Anonymized short summary.]",
      "episode_description": "# [Anonymized episode description]\n\n[Anonymized markdown body.]"
    },
    "transcription": {
      "paragraphs": [
        {
          "id": 0,
          "start": 0.11,
          "end": 25.83,
          "text": "[Anonymized transcript paragraph 1.]"
        },
        {
          "id": 1,
          "start": 26.63,
          "end": 29.89,
          "text": "[Anonymized transcript paragraph 2.]"
        }
      ],
      "transcription": {
        "words": [
          {
            "id": 0,
            "start": 0.11,
            "end": 0.69,
            "text": "[word]"
          }
        ],
        "paragraphs": [
          {
            "id": 0,
            "start": 0.11,
            "end": 30.14,
            "speaker": "SPEAKER_00"
          }
        ]
      }
    },
    "social_content": [],
    "merged_audio_url": [],
    "timestamps_markers_urls": [],
    "waveform_result": []
  }
}

Success with social content

{
  "task_id": "edit_def456",
  "status": "SUCCESS",
  "result": {
    "video": false,
    "filename": "anonymized_clean.wav",
    "download_url": "https://storage.example.com/uploads/.../anonymized_clean.wav?...",
    "summarization": {
      "title": "Protecting Your Braces: Why Hard and Sticky Foods Can Break Your Brackets",
      "summary": "The speaker advises orthodontic patients to avoid hard and sticky foods like caramel and taffy, because they can damage brackets and extend treatment time."
    },
    "transcription": {
      "paragraphs": [
        {
          "id": 0,
          "start": 0.11,
          "end": 25.83,
          "text": "Stuff like caramel, taffy, you're also avoiding... it's not a super emergency... just call us so we know something is broken."
        }
      ]
    },
    "social_content": {
      "newsletter": "# Don't Let Your Braces Break-Here's What You Need to Know\n\nHello [Subscriber's Name],\n\nIn this episode we break down one of orthodontics' most common problems: broken brackets.\n\nHard candies, caramel, and taffy can damage brackets and delay treatment.\n\n...",
      "twitter_thread": "<tweet>\n1/ Your braces can survive more than you think, but one mistake keeps adding months to your treatment.\n</tweet>\n\n<tweet>\n2/ Hard and sticky foods like caramel and taffy are bracket killers. A broken bracket isn't usually an emergency, but your orthodontist still needs to know.\n</tweet>\n\n...",
      "linkedin": "Here's what most people miss about orthodontic care: a broken bracket isn't just about the inconvenience-it's about the ripple effects on your treatment timeline.\n\nOne broken bracket is manageable. Repeated breakage adds months to treatment.\n\n..."
    },
    "merged_audio_url": [],
    "timestamps_markers_urls": [],
    "waveform_result": []
  }
}

Status values

StatusMeaning
PENDINGJob is queued and not yet started
STARTEDJob is actively being processed
PREPROCESSINGInput analysis and setup is in progress
CLASSIFICATIONCleanvoice is classifying events in the media
EDITINGThe main edit pass is running
POSTPROCESSINGFinal cleanup and result assembly is running
EXPORTOutput files are being written
SUCCESSJob completed — result.download_url contains the cleaned audio
FAILUREJob failed — inspect the returned payload for error details
RETRYJob encountered a transient error and is being retried

A recent live social_content=true run stayed in POSTPROCESSING across multiple polls before finally returning SUCCESS. Do not treat POSTPROCESSING as a completed state.

Polling loop

#!/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"
    echo "$RESPONSE"
    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

Final response when the loop exits successfully:

{
  "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 your cleaned audio file. Download it promptly — the link expires after a short period.

If you enabled transcription, summarize, or social_content, the raw response uses result.transcription, result.summarization, and result.social_content.

Wait at least 30 seconds after submitting before your first poll. After that, poll every 10 seconds. Polling more frequently than every 5 seconds is not recommended.