API
One POST, one probability score. No infrastructure required.
Scope. The API is built for English natural-language LLM responses. Source code, structured output (JSON, XML), and non-English text are outside the trained range and may produce unreliable scores. See Performance for the regimes covered.
How to read the result
p_hallucination(0–1) is how likely the text contains a made-up or wrong “fact” — higher means more suspect. Use flag for a ready-made yes/no, or threshold the score yourself. top_regime is the kind of error: FABRICATED (invented facts), NEAR_FALSE (misleading but technically true), CF_AUTH (fake/misattributed citations), FALSE_REFUSAL (refusing a reasonable request), NORMAL (clean), or Other.
Base URL
https://api.komplexai.io/apiSend your API key as an Authorization: Bearer <key> header. Public sign-ups open at launch.
POST /detect
Runs hallucination detection on a single LLM response.
Request
{
"response": "The Eiffel Tower was built in 1887 by Gustav Eiffel..."
}| Field | Type | Required | Description |
|---|---|---|---|
| response | string | yes | LLM response text to evaluate. Example: "The Eiffel Tower is in Paris." |
| prompt | string | no | Optional prompt context. Including it enables the prompt+response (pr) head, which is more accurate when context matters. Example: "Where is the Eiffel Tower?" |
| task | string | no | binary or multiclass. Default multiclass (per-regime breakdown). Pass binary for the headline-score-only path. |
| input_mode | string | no | Override the auto-detect: pr forces the prompt+response head;ro forces response-only. Default: auto (pr if prompt non-empty, else ro). |
| top_k_regimes | int | no | Number of regime scores to return, 1–10 (default: 6 —NORMAL + the 5 advertised hallucination regimes (FABRICATED, NEAR_FALSE,CF_AUTH, FALSE_REFUSAL) and Other, after the v1 server-side collapse policy). |
Response
{
"p_hallucination": 0.73,
"flag": true,
"top_regime": "FABRICATED",
"regime_scores": [
{"regime": "FABRICATED", "p": 0.61},
{"regime": "CF_AUTH", "p": 0.18},
{"regime": "NEAR_FALSE", "p": 0.09},
{"regime": "NORMAL", "p": 0.06},
{"regime": "Other", "p": 0.05},
{"regime": "FALSE_REFUSAL", "p": 0.01}
],
"request_id": "req_a1b2c3d4e5f6a7b8",
"detections_billed": 1,
"mode": "short",
"latency_ms": 210,
"model_version": "nl-v1",
"calibrator_version": "platt_v1:<cell>",
"input_mode_used": "ro",
"task_used": "multiclass",
"warnings": []
}Response fields
| Field | Type | Description |
|---|---|---|
| p_hallucination | float 0–1 | Calibrated hallucination probability |
| flag | bool | Convenience flag — true when p_hallucination crosses the server-side decision threshold for the active (task_used, input_mode_used) cell. The threshold is model-dependent and is NOT a fixed 0.5; the active build's threshold is reflected via calibrator_version. Clients that need a different operating point should re-threshold from p_hallucination directly. |
| top_regime | string | Highest-probability regime code (e.g. FABRICATED, NORMAL). |
| regime_scores | array | Top-k regimes with individual scores, sorted descending. Items are {regime, p}. Multiclass sums to ≈1.0. |
| request_id | string | Server-minted ID. Also returned on the X-Request-Id header. |
| detections_billed | int | Detections consumed by this call. At v1 each short call is 1. |
| mode | string | short (single-pass) or long (sliding-window; paid-tier, not at v1). |
| latency_ms | int | Server-side inference time in ms (excludes network). |
| model_version | string | Detector build identifier. Stable per Modal deployment. |
| calibrator_version | string | Probability calibrator identifier (Platt / isotonic build). |
| input_mode_used | string | pr (prompt+response) or ro (response-only). Auto-detected from whether prompt was supplied. |
| task_used | string | Echoes the task that actually produced p_hallucination. Equals the request's task when explicitly set; otherwise the public /api/detect proxy supplies multiclass (its own default), so omitting task yields multiclass through the public API. |
| warnings | string[] | Advisory codes for this call. Always present; usually []. See the Warnings section for the closed enum and forward-compat rules. |
Warnings
The response body always carries a warnings: string[] field (usually []) with advisory codes the proxy attaches based on quota, subscription, and calibration state. Useful for surfacing "you're near your limit" in your app without polling /api/usage/summary.
| Code | When emitted |
|---|---|
| quota_80pct | User has used ≥80% of the monthly quota (free + paid tiers both). |
| quota_95pct | ≥95%. Last warning before hard-stop on free, or before overage on paid. |
| overage_active | Paid tier with usage above the included limit; this call is billed at the overage rate. |
| calibration_uncalibrated | Active calibrator is an uncalibrated:* placeholder. Rare; should only appear during a model rollout. |
| input_truncated | Input was truncated to fit the per-call character cap. Reserved — not currently emitted at v1. |
| subscription_inactive | User has subscription_status != "active" but is still calling the API. |
Forward-compatibility: clients MUST ignore unknown codes. Future model / billing revisions may add codes here without bumping the API version.
Response headers
Every /detect response carries usage and rate-limit headers. Use them to track spend without polling /api/usage/summary.
| Header | Type | Description |
|---|---|---|
| X-Detections-Billed | int | Detections consumed by this call. At v1 every short call (≤ 2,048 characters) is 1 detection. Long-document detection is planned for a later release and will return values > 1. |
| X-Quota-Remaining | int | Units left in your current billing period. |
| X-Quota-Period | string | Current billing period in YYYY-MM form (e.g. 2026-05). |
| X-Request-Id | string | Server-minted ID for this request. Quote it in support tickets and dispute audits. |
Suggested thresholds
The wire response carries the raw p_hallucination and a convenience flag (server-side threshold; model-dependent, not a fixed 0.5). The bucket cuts below are demo-UI buckets only, not the server flag cut. Clients are free to pick their own thresholds.
| Bucket | p_hallucination |
|---|---|
| LOW | ≤ 0.40 |
| MODERATE | 0.40 – 0.70 |
| HIGH | > 0.70 |
Example — curl
curl -X POST https://api.komplexai.io/api/detect \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $HALU_API_KEY" \
-d '{
"response": "The Eiffel Tower is in Paris, France.",
"prompt": "Where is the Eiffel Tower?",
"task": "multiclass"
}'Example — Python
import halu
result = halu.detect(
response="The Eiffel Tower is in Paris, France.",
prompt="Where is the Eiffel Tower?",
task="multiclass",
)
print(result.p_hallucination, result.flag, result.top_regime)For higher-level integration patterns — detect_or_raise (gate output), detect_or_warn (annotate), and regenerate_until_clean (LLM-informed retry) — see the Guide.
Example — JavaScript / TypeScript
const response = await fetch("https://api.komplexai.io/api/detect", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${process.env.HALU_API_KEY}`,
},
body: JSON.stringify({
response: "The Eiffel Tower is in Paris, France.",
prompt: "Where is the Eiffel Tower?",
task: "multiclass",
}),
});
if (!response.ok) {
const err = await response.json();
throw new Error(`Detect failed: ${err.error || err.detail}`);
}
const result = await response.json();
console.log(`p_hallucination: ${result.p_hallucination}`);
console.log(`flag: ${result.flag}`);
console.log(`top_regime: ${result.top_regime}`);Use fetch in any JS/TS environment (Node, browser, Vercel, Cloudflare Workers, etc.). An official @komplexai/halu Node package is planned post-launch.
Caveats
- Input mode is auto-detected. The detector runs in prompt+response (
pr) mode whenpromptis non-empty, otherwise response-only (ro). Inromode the detector never sees the prompt, so factual hallucinations that require prompt context may be missed — include the prompt for best accuracy when context matters. - Probabilistic output. Scores are calibrated probabilities, not ground truth. See Performance for per-regime accuracy.
- Max 2,048 characters per field. Longer inputs are rejected with an
input_too_longerror (HTTP 400) — not truncated. - English natural-language only. Code, structured output (JSON, XML), and non-English text are outside the trained range — see Scope above.
Rate limits
Free tier requires a key — sign up with Google to get one instantly. Free tier: 3,700 API requests/month plus 300 web-app detections/month. Higher limits via paid plan — see Pricing.
Have questions? See the FAQ →