Send an audio file, get an AI-likelihood score and a human-versus-synthetic verdict back as JSON. It is one synchronous POST with an sk_live_ key, it is included from the Amplify voice plan up, and it draws on your voice quota rather than the text API quota — voice is a separate product, so a Starter or Pro text subscription neither grants nor blocks it.
Everything you need to make the first call.
# Check an audio file. Key from /api-keys — needs the `scan` scope.
curl -X POST https://api.textsight.ai/v2/voice/detect \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-F "file=@clip.mp3"
The key also works as x-api-key: sk_live_… if you prefer a plain
header. A successful call returns:
{
"ai_probability": 0.94,
"ai_score": 94,
"human_score": 6,
"verdict": "ai",
"segments": [],
"duration_sec": null,
"detector": "…",
"filename": "clip.mp3",
"file_size": 148213,
"cached": false,
"quota": { "source": "plan", "remaining": 9873 },
"scan_id": "…",
"report_url": "/api/scan/…/report.pdf",
"request_id": "req_…"
}
verdict, not just ai_score.
When the detector has a calibrated uncertainty band it states its own verdict, and
"mixed" means genuinely undecided rather than
“leaning human”. Deriving your own verdict from the score alone will turn an abstain
into a confident answer the detector did not give.
| Field | Value |
|---|---|
| Endpoint | POST https://api.textsight.ai/v2/voice/detect |
| Body | multipart/form-data with the audio in the file field |
| Auth | Authorization: Bearer sk_live_… or x-api-key: sk_live_… |
| Scope | scan — the default on a new key |
| Plan | Amplify, Broadcast or Enterprise. Free, Echo and text plans return 403 VOICE_API_NOT_ENTITLED |
| Formats | MP3, WAV, M4A, OGG, FLAC. Opus is rejected — convert WhatsApp voice notes first |
| Max size | 10 MB, enforced server-side |
| Throughput | 60 requests/minute on Amplify, 120 on Broadcast, negotiated on Enterprise — per account rather than per key |
| Quota | Voice plan month → Soundbites credits → free daily. Never the text API quota |
Every error uses the standard { "error": { "code", "message" } }
envelope shared with the rest of the API.
| Status | Code | Means |
|---|---|---|
| 400 | invalid_request | No file, an unsupported format, or bytes that are not really audio |
| 401 | unauthorized | Key missing, invalid or expired |
| 403 | VOICE_API_NOT_ENTITLED | No Amplify, Broadcast or Enterprise plan on the account |
| 429 | quota_exceeded | Voice quota exhausted for the period |
| 429 | rate_limited | Over 120 requests/minute |
| 502 | detector_error | Our detection engine failed. Retryable |
| 503 | detector_unavailable | Detector temporarily down. Retryable |
Each of these will otherwise surprise you in production.
Results are cached on the file bytes. A repeat upload returns
cached: true and does not consume quota or spend a credit —
but because no scan row is written, it also returns no
scan_id or report_url. If you need a
persisted record for every call, deduplicate on your side rather than relying on ours.
segments is often emptyA per-window timeline is not produced by the engine running today: segments always returns an empty array and duration_sec returns null. We would rather return an honest empty array than fabricate segmentation, so treat both fields as optional in your client.
Nothing in the response names ElevenLabs, OpenAI or any other generator, because source
attribution is a much harder problem than telling synthetic from real and we do not return a guess.
detector identifies our engine, not the one that made the audio.
This runs on a complete recorded file. There is no streaming or real-time endpoint, and we would be sceptical of anyone offering one at consumer scale. For live call protection, the process controls in our fraud-team guide do more than any detector.
Call codecs and voice-note compression strip out much of the detail detection relies on. Expect lower confidence on that material, and treat an uncertain result as genuinely uncertain rather than as a soft yes. What moves reliability is covered here.
API access is included from Amplify up ($59/mo, 2,000 voice checks a month; Broadcast $199 for 10,000; Enterprise $599 for 100,000). Create a key at /api-keys — a voice plan qualifies on its own, so you do not need a paid text plan as well. Keys are shown once; store them somewhere safe.
Voice checks draw on the voice quota and never touch the text API's monthly call allowance. The two products are billed and metered independently.
The same key works against the text endpoints, subject to your text plan and the key's scopes:
POST /v2/detect — AI detection on text, with per-sentence scoresPOST /v2/score — the same engine, scores only, lighter payloadPOST /v2/rewrite — humanize text (scope: humanize)GET /v2 lists the current surface without authentication, and full
parameters live in the API documentation.
segments always returns an empty array and duration_sec returns null. We would rather return an honest empty array than fabricate segmentation, so treat the field as optional.Where retrospective analysis fits, and why process controls beat detection for live calls.
Controls that work →What moves reliability up and down, and why we publish no single accuracy percentage.
See our position →Parameters, error codes, rate limits and authentication across the whole API surface.
Read the docs →Your first check is free with no signup — 3 a day with a free account. Your audio is never stored.