{
"success": false,
"error": "API key has expired"
}
Some responses carry extra fields alongside error — upgradeRequired /
requiredPlan / currentPlan on a plan gate, limitExceeded / currentCount /
maxChats on a quota stop. Always branch on the HTTP status code first; treat
the string fields as human-readable detail, not a stable API.
Common error codes
401
Missing / malformed / disabled / expired API key
All key failures return 401 — "API key required", "Invalid API key format", "API key is disabled", or "API key has expired". Expired keys auto-deactivate on that request.
403
Plan gate (upgradeRequired)
Feature needs a higher plan (e.g. Hosted Models / GPU Tunnels need Pro). Body has upgradeRequired, requiredPlan, currentPlan. Note: Hosted Models / GPU Tunnels are session-only — an API-key caller gets 401 there, not 403.
429
rate_limit_exceeded
Monthly request or token quota reached, or a short-term per-minute burst limit. See Plan Limits. The monthly-quota 429 has no Retry-After — it resets at the month boundary.
402
INSUFFICIENT_CREDITS
Account has run out of credits
503
MODEL_NOT_AVAILABLE
Requested model is temporarily unavailable
400
INVALID_REQUEST
Request body is malformed or missing required fields
400
CONTEXT_LENGTH_EXCEEDED
Message exceeds the model context window
404
CONVERSATION_NOT_FOUND
RAG conversation ID does not exist
500
INTERNAL_ERROR
Internal server error — please retry
Error handling example
Branch on the HTTP status, not on a string. Retry only 429 and 5xx,
with a bounded count and jitter, and honour Retry-After when the server sends it.
JS
const MAX_RETRIES = 4;
async function handleTarqaRequest(apiKey, requestData, attempt = 0) {
const response = await fetch(`${BASE_URL}/v1/chat/completions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(requestData),
});
if (response.ok) return response.json();
const body = await response.json().catch(() => ({}));
const detail = body.error?.message || body.error || `HTTP ${response.status}`;
// Retryable: rate limits and transient server errors.
if ((response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES) {
const retryAfter = Number(response.headers.get('Retry-After'));
const backoff = Number.isFinite(retryAfter) && retryAfter > 0
? retryAfter * 1000
: Math.min(2 ** attempt * 1000, 30000) + Math.random() * 1000; // jitter
await new Promise(r => setTimeout(r, backoff));
return handleTarqaRequest(apiKey, requestData, attempt + 1);
}
// Not retryable — surface it.
if (response.status === 401) throw new Error('Auth failed — check your API key.');
if (response.status === 403) throw new Error(`Plan gate: ${detail}. Upgrade required.`);
throw new Error(`API error ${response.status}: ${detail}`);
}
Safe retries — Idempotency-Key
Send an Idempotency-Key header (any unique string, e.g. a UUID) on
POST /v1/chat/completions and POST /v1/ask. A retry that carries the same key
and the same request body returns the stored original response instead of
running again — the replay carries Idempotency-Replayed: true.
200Replayed — identical to the first response, plus header Idempotency-Replayed: true
409The first request with this key is still in flight — retry after a short wait
422This key was already used with a different request body — use a new key
Keys are remembered for 24 hours, then a repeat re-executes.
Streaming requests (stream: true) are not covered — the header is
ignored, so a retried stream re-runs tool calls and re-counts usage, and tokens from a
stream that dropped mid-way are still billed. Treat a dropped stream as possibly-billed.
Errors inside a stream
Once a streaming response has started (HTTP 200), a failure arrives as a frame,
not a status code — and the shape differs by endpoint:
Your stream consumer must check for these before treating a frame as content, and
must handle the connection simply dropping (no [DONE], no error frame). There is
no resumption — no Last-Event-ID; retry the whole request (see the caveat above).
Debugging tips
Always log the requestId for support · check response status codes first · validate request format before sending · monitor error rates in analytics
Production best practices
Branch on HTTP status, not error strings · bound your retries and add jitter · set up error monitoring · give users friendly error messages