API Reference
Send a question to multiple independent AI models in one request and receive a synthesized response with per-sentence corroboration scores showing exactly which models agreed.
Evaluating a software investment or acquisition instead? See AI-audited technical due diligence reports →
Authentication
Bearer token
Include your API key as a Bearer token in every request's
Authorization header.
Authorization: Bearer YOUR_API_KEY
Unauthenticated requests are served as free tier (25 queries/month, 2-provider panel). Paid tiers require a bearer token. Get an API key →
| Tier | Queries/month | Panel | API access |
|---|---|---|---|
| Free (no key) | 25 | 2 providers | — |
| Personal ($8.99/mo) | 200 | 3 providers | ✓ |
| Plus ($17.99/mo) | 1,000 | Every available provider + export | ✓ |
| Business ($149/mo) | 5,000 | Every available provider + provenance reports | ✓ |
Quick start
Your first call
A minimal request with three providers and the default synthesizer:
# Ask three models, get a synthesized answer with consensus annotation curl -X POST https://askarei.com/api/ask \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "question": "What are the key risks of deploying LLMs in production?", "providers": ["gemini", "openrouter", "cohere"] }'
Plus and Business plans can use every available provider at once — no fixed panel size:
-d '{
"question": "...",
"providers": ["gemini", "groq", "openrouter", "cohere", "cerebras", "huggingface", "anthropic", "deepseek"]
}'
Endpoints
Available routes
Request body
| Field | Type | Description |
|---|---|---|
| questionrequired | string | The question to send to all providers. |
| providersrequired | string[] | Provider keys to query. See Providers below. At least one required. |
| synthesizeroptional | string | Provider key to use as synthesizer. Defaults to a provider not in the panel. Must differ from all queried providers. |
| modeloptional | string | Override the default model for all providers. |
| optimize_promptoptional | boolean | Rewrite the question before fan-out for clarity and specificity. Default: false. |
| optimizer_modeloptional | string | Provider key to use for prompt optimization when optimize_prompt is true. |
| fast_modeoptional | boolean | Use only the 2 lowest-latency providers from recent history instead of the full requested panel. Default: false. Overridden by panel_size if both are set. |
| panel_sizeoptional | integer | Explicit cap on how many of the requested providers actually run (must be ≥ 1). Your plan's own provider-count limit still applies on top of this. |
Response
| Field | Type | Description |
|---|---|---|
| question | string | Original question as submitted. |
| optimized_question | string | null | Rewritten question, if optimize_prompt was true and succeeded. |
| answers | object[] | One entry per provider. Each has label, model, text (the raw answer), error (null on success), and cost_usd (real per-call cost in USD, or null when the provider doesn't expose token-usage data). |
| consensus_available | boolean | True if two or more providers responded successfully. |
| provider_count | integer | Number of providers that returned a successful answer. |
| final_answer | string | Synthesized answer combining the strongest, most corroborated content from all providers. |
| synthesizer | string | Provider label that performed the synthesis step. |
| source_count | integer | Number of provider answers the synthesizer used as sources. |
| notes | string | Synthesizer commentary on agreement level and notable divergences. |
| init_errors | object | Map of provider key → error message for any providers that failed to initialize. |
| conversation_id | integer | null | Persisted conversation ID for history and rating. An integer for authenticated requests like the ones documented here. null when no history row was recorded — anonymous, unauthenticated requests are answered normally but are never persisted, so they have no conversation to rate or retrieve. |
| consensus_sentences | object[] | Per-sentence breakdown of the final_answer. Each entry: text (sentence) and corroborated_by (count of raw answers that contain similar content). |
Example response
{
"question": "What are the key risks of deploying LLMs in production?",
"optimized_question": null,
"answers": [
{ "label": "gemini", "model": "gemini-2.5-flash", "text": "...", "error": null },
{ "label": "openrouter", "model": "openai/gpt-oss-120b:free", "text": "...", "error": null },
{ "label": "cohere", "model": "command-a-03-2025", "text": "...", "error": null }
],
"consensus_available": true,
"provider_count": 3,
"final_answer": "Deploying LLMs in production carries several key risks...",
"synthesizer": "gemini",
"source_count": 3,
"notes": "All three providers agreed on the core risk categories...",
"init_errors": {},
"conversation_id": 42,
"consensus_sentences": [
{ "text": "Deploying LLMs in production carries several key risks.", "corroborated_by": 3 },
{ "text": "Hallucination is the most cited failure mode.", "corroborated_by": 2 }
]
}
Identical request body, auth, tier limits, and rate limiting to /api/ask above —
the only difference is the response shape: a text/event-stream of Server-Sent Events
instead of one blocking JSON payload, so a client can render each provider's answer as it
arrives instead of waiting for the full panel plus synthesis. Event types (each a JSON payload
under data:): provider_result (one per provider, as it completes),
synthesis_result (the final combined answer, same shape as /api/ask's
response body), error, and done (stream end).
Thumbs up or down on a past call. Use the conversation_id from any /api/ask response.
curl -X POST https://askarei.com/api/conversations/42/rating \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"rating": 1}' # 1 = thumbs up, -1 = thumbs down
Returns a structured, provenance-complete report for a single past call. Includes: original question, every per-provider raw answer with lexical overlap scores, the synthesized answer, and per-sentence corroboration rates computed at report-generation time.
curl https://askarei.com/api/conversations/42/report \
-H "Authorization: Bearer YOUR_API_KEY"
Requires either the caller to be the conversation's own owner (the key/session that asked it), or a valid share_token query parameter from POST .../share below. Returns 404 for unknown IDs and for a real ID that isn't yours — the id itself is not a capability token; a paying-tier key or free-tier session only ever sees its own conversations.
Owner-only. Mints a single active, unguessable share_token for this conversation and returns a ready-to-send report URL — the explicit, revocable way to hand a report to someone else (a client, an auditor) without giving them your key. Calling it again replaces the previous link.
curl -X POST https://askarei.com/api/conversations/42/share \
-H "Authorization: Bearer YOUR_API_KEY"
Owner-only. Immediately invalidates any previously-issued share_token for this conversation — the link stops working right away.
curl -X POST https://askarei.com/api/conversations/42/unshare \
-H "Authorization: Bearer YOUR_API_KEY"
Owner-only. Permanently removes one of your own past questions and its answers from your history — cannot be undone. Same 404-for-unauthorized reasoning as .../report above: a real id that isn't yours 404s, it never reveals whether the id exists.
curl -X DELETE https://askarei.com/api/conversations/42 \
-H "Authorization: Bearer YOUR_API_KEY"
Owner-only. Returns a downloadable JSON file with the full per-provider raw answers, synthesis, and metadata for one past call — Business tier additionally includes a human-readable provenance_text field suitable for pasting directly into a client report. Requires Plus or Business tier; a Free or Personal key gets 403 export_requires_professional. Same 404-for-unauthorized reasoning as .../report above, but with no share_token variant — export is a "get my own data" feature, not a sharing mechanism.
curl https://askarei.com/api/conversations/42/export \
-H "Authorization: Bearer YOUR_API_KEY"
Returns up to 50 of your own past questions and their synthesis summaries, plus aggregate stats — scoped to the caller's identity, never another user's.
curl https://askarei.com/api/conversations \
-H "Authorization: Bearer YOUR_API_KEY"
curl https://askarei.com/api/health
# → {"status": "ok"}
Providers
Available provider keys
Pass any combination of these keys in the providers array. Each runs independently against its default model.
anthropic or deepseek return a clear per-provider error rather than a real answer — see pricing for paid plans. Live per-provider success rates are public at reliability.html.
Consensus layer
How corroboration scores work
Each sentence in final_answer appears in consensus_sentences
with a corroborated_by count. A count of 3 means three independent
raw provider answers contained semantically similar content — the higher the count,
the more confident you can be that sentence reflects genuine model consensus rather
than a single model's opinion.
Sentences with corroborated_by: 1 are worth extra scrutiny — only one
model said it, and it may reflect that model's individual bias or a hallucination the
others didn't share.
answers[], even when synthesis fails. You can always read exactly what each
model said, independent of Arei's synthesis layer.
Errors
HTTP status codes
| Code | Meaning | Details |
|---|---|---|
| 200 | OK | Successful call. Check init_errors for any providers that failed to initialize within a successful overall request. |
| 400 | Bad request | No providers could be initialized, or invalid request body. |
| 401 | Unauthorized | Missing or invalid Authorization header. |
| 403 | Forbidden | The endpoint you're calling requires a higher tier than your account has — e.g. conversation export requires Plus or Business. |
| 404 | Not found | The resource id in the URL doesn't exist, or exists but belongs to a different account (returned identically either way, so a request can't be used to probe which conversation ids are real). |
| 429 | Too many requests | You've hit your plan's monthly query limit, or a short-term rate limit. See pricing for plan limits. |
| 502 | Bad gateway | The synthesizer call failed or returned a malformed response. Raw per-provider answers are still included in the response body. |
| 503 | Service unavailable | A downstream integration (e.g. the billing portal) is temporarily unreachable. Safe to retry; not a sign anything else on your account is broken. |