Credits and billing
The API is billed in credits. The same rules apply to the REST API, the dashboard and the MCP server; there is one billing implementation behind all three.
The signup grant
Each account receives 100,000 credits once, the first time it signs in with a verified email address. Signing in again, creating API keys or rotating keys never grants more credits.
If your account shows no credits, verify your email address and sign in again.
Prices
| Operation | Endpoint | Credits |
|---|---|---|
| Deepfake detection | POST /v1/deepfake/detect |
50 |
| Speaker enrollment (one sample) | POST /v1/speakers/enroll |
50 |
| Speaker verification | POST /v1/speakers/verify |
50 |
Speaker verification with detect_deepfake=true |
POST /v1/speakers/verify |
100 (two operations of 50) |
A charge happens only when a model operation succeeds. A successful operation includes every valid result: synthetic and genuine, match and no_match.
What is free
- Signing in, creating, rotating and revoking API keys.
- Listing, reading and deleting speakers.
- Reading your balance (
GET /v1/credits) and usage (GET /v1/usage). - Fetching stored results (
GET /v1/operations/{operation_id}). - Idempotent replays: repeating a request with the same
Idempotency-Keyreturns the stored result withcredits.charged: 0. GET /v1/health, the documentation and the OpenAPI description.- Every failed request (see below).
How a charge happens
Each model operation follows the same steps:
- Validate (free). The server checks the key, scopes, request fields and audio (format, size, duration, speech). A request that fails here is rejected before any credits are touched.
- Reserve. 50 credits move from
availabletoreservedin one atomic step. If fewer than 50 credits are available, the request fails with402 insufficient_credits. - Run the model.
- Settle or release. On a valid result, the reservation is settled: the 50 credits are charged, exactly once. If the model fails, times out or is overloaded, the reservation is released and the credits return to
available.
Reservations carry a lease of a few minutes. If a request is interrupted in a way that leaves a reservation without a result, a background job releases it after the lease expires. Settlement and release are each recorded at most once per operation.
You are not charged for failures
Nothing is charged when a request fails, including:
- authentication and permission errors (
401,403); - invalid requests and invalid audio (
400,413,415,422); 402 insufficient_credits;- rate limits (
429); - model overload, failure or timeout (
503 inference_overloaded,502 inference_failed,504 inference_timeout).
With detect_deepfake=true, each part is accounted independently. If verification succeeds and the deepfake part fails, you are charged 50 credits and the response's deepfake field contains the error.
Disconnects and retries
Once the model has started, the operation runs to completion even if your client disconnects. The result is stored and charged once. To get it without paying again, either:
- repeat the request with the same
Idempotency-Key: you receive the stored result withidempotent_replay: trueand"credits": {"charged": 0, "balance": <current balance>, "original_charge": 50}, or - fetch it with
GET /v1/operations/{operation_id}.
Check your balance
curl https://platform.detectif.ai/v1/credits \
-H "Authorization: Bearer $DETECTIF_API_KEY"import os
import requests
resp = requests.get(
"https://platform.detectif.ai/v1/credits",
headers={"Authorization": f"Bearer {os.environ['DETECTIF_API_KEY']}"},
timeout=30,
)
balance = resp.json()
print(balance["available"], balance["reserved"])const res = await fetch("https://platform.detectif.ai/v1/credits", {
headers: { Authorization: `Bearer ${process.env.DETECTIF_API_KEY}` },
});
const balance = await res.json();
console.log(balance.available, balance.reserved);Example response:
{ "object": "credits", "available": 99850, "reserved": 0, "price_per_operation": 50, "plan": "free" }available is what new requests can reserve. reserved is held by requests that are running now. Each model response also reports credits.balance after that request. Needs the credits:read scope.
Review your usage
GET /v1/usage lists one entry per model operation request, including failed ones that were not charged. Filter with operation (deepfake.detect, speaker.enroll, speaker.verify) and status, and page with limit and cursor. Needs the usage:read scope.
curl "https://platform.detectif.ai/v1/usage?operation=deepfake.detect&limit=20" \
-H "Authorization: Bearer $DETECTIF_API_KEY"import os
import requests
resp = requests.get(
"https://platform.detectif.ai/v1/usage",
headers={"Authorization": f"Bearer {os.environ['DETECTIF_API_KEY']}"},
params={"operation": "deepfake.detect", "limit": 20},
timeout=30,
)
for event in resp.json()["data"]:
print(event)const url = new URL("https://platform.detectif.ai/v1/usage");
url.searchParams.set("operation", "deepfake.detect");
url.searchParams.set("limit", "20");
const res = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.DETECTIF_API_KEY}` },
});
for (const event of (await res.json()).data) console.log(event);When credits run out
Requests that need more credits than are available fail with 402 insufficient_credits and are not charged. Free endpoints keep working. To discuss more credits, contact DetectifAI.