API Reference / Speakers
Verify a speaker
POST/v1/speakers/verify
Checks whether a recording is the voice of an enrolled speaker. Send a recording of 3-60 seconds with at least 2 seconds of speech. The result has a decision (match or no_match), a similarity from -1 to 1 (higher means more alike) and the threshold used for the decision. Main failures: unknown speaker (404), too short or too little speech (422), not enough credits (402). Requires scope speakers:verify.
With detect_deepfake=true the same recording is also checked for AI-generated speech (needs scope deepfake:detect; that check needs a recording longer than 4 seconds, otherwise only it fails). The two checks are counted separately, and only completed checks use credits:
- if the synthetic-speech check fails, you still get the verification, with
deepfake.error; - if the verification fails but the synthetic-speech check completed, you get the verification's error together with
partial(the synthetic-speech result); - if both fail, you get the error and no credits are used.
A retry with the same Idempotency-Key returns the same outcome again without running or counting the checks twice.
Authentication
Send your API key as Authorization: Bearer <your API key>.
Headers
| Name | Type | Details |
|---|---|---|
Authorizationrequired | string |
|
Idempotency-Keyoptional | string or null | Optional (recommended) key that makes retries safe for 24 h: a retry with the same key and the same request returns the stored response without charging again. 1 to 128 characters |
Request body
Send the request as multipart/form-data. Upload the audio file in the file field (up to 100 MB) and send the other fields as form fields. See Audio and limits.
filefilerequiredThe audio file: WAV, MP3, FLAC, OGG/Opus, WebM, M4A/AAC or AIFF, at most 100 MB.
speaker_idstringrequiredYour identifier for the speaker (1-64 characters: letters, digits, _ . : -).
1 to 64 characterspattern
^[A-Za-z0-9_.:-]{1,64}$detect_deepfakebooleanAlso check the same recording for AI-generated speech (needs scope deepfake:detect).
default
false
Response
200 The check completed.
request_idstringrequiredoperation_idstringrequiredobjectstringrequiredalways
"speaker.verification"speaker_idstringrequiredresultobjectrequired5 fields in
resultdecisionstringrequiredmatchwhensimilarityis at or abovethreshold.One of:
match,no_matchsimilaritynumberrequiredHow alike the two voices are, from -1 to 1 (higher is more alike).
-1 to 1
thresholdnumberrequiredThe similarity from which the decision is
match.similarity_semanticsstringrequiredA short reminder of what
similaritymeans.duration_secondsnumberrequiredLength of the checked audio, in seconds.
deepfakeobject or nullrequirednull unless detect_deepfake=true; then either the synthetic-speech result or its error.
2 fields in
deepfakeoption 1 (EmbeddedDeepfake)object11 fields in this option
verdictstringrequiredThe decision:
real,fake(AI-generated), ornot_sure(try a longer or cleaner recording).One of:
real,fake,not_surelabelstringrequiredThe same decision in the older wording:
synthetic(fake),genuine(real),uncertain(not sure).One of:
synthetic,genuine,uncertainis_syntheticbooleanrequiredtrue only when the verdict is
fake.scorenumberrequiredHow likely the recording is AI-generated, from 0 to 1.
0 to 1
thresholdnumberrequiredThe main model's threshold (0.62). Informational: decide with
verdict.score_semanticsstringrequiredA short reminder of what
scoremeans.duration_secondsnumberrequiredLength of the checked audio, in seconds.
segmentsarray of objectrequiredScores for consecutive, overlapping parts of the recording.
3 fields in
segmentsstartnumberrequiredStart of this part of the recording, in seconds.
endnumberrequiredEnd of this part of the recording, in seconds.
scorenumberrequiredHow likely this part is synthetic (0 to 1).
0 to 1
operation_idstringrequiredcredits_chargedintegerrequiredmodelobjectrequired2 fields in
modelnamestringrequiredModel family:
aletheia(deepfake detection) oreidos(speaker verification).versionstringrequiredOpaque model version; it changes whenever the model is updated.
option 2 (EmbeddedDeepfakeError)object3 fields in this option
errorobjectrequired4 fields in
errortypestringrequiredOne of:
invalid_request,authentication,permission,not_found,conflict,rate_limited,insufficient_credits,payload_too_large,unsupported_media,unprocessable_audio,overloaded,internalcodestringrequiredStable snake_case error code.
messagestringrequiredHuman-readable explanation. Do not parse.
paramstring or nullThe request field the error refers to, if any.
operation_idstringrequiredcredits_chargedintegerdefault
0
modelobjectrequired2 fields in
modelnamestringrequiredModel family:
aletheia(deepfake detection) oreidos(speaker verification).versionstringrequiredOpaque model version; it changes whenever the model is updated.
creditsobjectrequired4 fields in
creditschargedintegerrequiredCredits used by this request. A repeated request with the same Idempotency-Key uses none: it reports 0 here and what the original request used in
original_charge.balanceinteger or nullrequiredYour available credits after this request; null on the unlimited plan.
original_chargeinteger or nullOnly on repeated requests (
chargedis then 0): the credits the original request used.unlimitedboolean or nullPresent (true) only for accounts on the unlimited plan, where checks use no credits.
latency_msnumberrequiredidempotent_replaybooleanrequired
Example response
{
"request_id": "req_8f14e45fceea167a5a36dedd",
"operation_id": "op_6f1ed002ab5595859014ebf0951522d9",
"object": "speaker.verification",
"speaker_id": "customer-4711",
"result": {
"decision": "match",
"similarity": 0.7412,
"threshold": 0.53,
"similarity_semantics": "Higher means the two voices are more alike. Not a percentage.",
"duration_seconds": 4.1
},
"idempotent_replay": false
}Errors
| Status | Meaning |
|---|---|
| 400 | The request is invalid, for example a missing or malformed field. |
| 401 | The API key is missing, invalid or revoked. |
| 402 | Not enough credits are available for this operation. |
| 403 | The key lacks the required scope, the email address is not verified, or the account is suspended. |
| 404 | Not found. Also returned for resources that belong to another account. |
| 408 | The upload did not arrive in time (the whole file within 120 seconds, at least 64 KB per second). |
| 409 | Conflict, for example a request with the same Idempotency-Key is still running, or the speaker already has the maximum number of samples. |
| 413 | The upload is larger than 100 MB. |
| 415 | Not a supported audio format, or the request is not multipart/form-data. |
| 422 | The audio cannot be used (damaged, too short, too long, silent or too little speech; the message says what is needed), or the Idempotency-Key was already used for a different request. |
| 429 | Too many requests or too many operations at the same time. Wait for the number of seconds in Retry-After. |
| 500 | Something went wrong on our side. No credits were used. |
| 502 | The check failed on our side. No credits were used; retry later. |
| 503 | The service is busy, for example with many large uploads. Retry after the Retry-After seconds. |
| 504 | The check took too long. No credits were used; retry later. |
Error bodies share one shape with a stable error.code; see Errors for every code and how to fix it.
Code samples
curl "https://platform.detectif.ai/v1/speakers/verify" \
-H "Authorization: Bearer $DETECTIF_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-F "speaker_id=alice" \
-F "file=@clip.wav"import os
import uuid
import requests
with open("clip.wav", "rb") as f:
resp = requests.post(
"https://platform.detectif.ai/v1/speakers/verify",
headers={"Authorization": f"Bearer {os.environ['DETECTIF_API_KEY']}", "Idempotency-Key": str(uuid.uuid4())},
data={"speaker_id": "alice"},
files={"file": f},
timeout=300,
)
print(resp.status_code, resp.json())import { readFile } from "node:fs/promises";
import { randomUUID } from "node:crypto";
const form = new FormData();
form.append("speaker_id", "alice");
form.append("file", new Blob([await readFile("clip.wav")]), "clip.wav");
const res = await fetch("https://platform.detectif.ai/v1/speakers/verify", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.DETECTIF_API_KEY}`,
"Idempotency-Key": randomUUID(),
},
body: form,
});
console.log(res.status, await res.json());