Troubleshooting
The five issues new integrators hit most often, with the actual error payloads and the fixes that resolve them. If none of these match what you're seeing, email [email protected] with the request id from the response.
x-request-id header. Capturing and forwarding it cuts support resolution time by roughly 10x.401 UNAUTHORIZED or INVALID_API_KEY
The API didn't accept the key you sent. Walk this checklist top to bottom — it accounts for ~95% of these errors:
- Are you sending the header as
x-api-key(lowercase, with hyphens)?Authorization: Beareris not supported. - Is the key trimmed? Stray newlines from .env files cause silent failures — log
key.lengthand confirm it matches the dashboard. - Was the key revoked? Check /dashboard/api-keys. A revoked key is permanently inactive — issue a new one.
- Are you mixing live and test keys against the same environment? They are not interchangeable.
{ "error": { "code": "UNAUTHORIZED", "message": "Missing API key. Pass it as the x-api-key header or api_key query parameter." }}{ "error": { "code": "INVALID_API_KEY", "message": "API key is not valid or has been revoked." }}429 RATE_LIMIT_EXCEEDED
You've hit your plan's per-minute or per-month limit. The response always includes a Retry-After header (in seconds) — respect it instead of hammering. The standard X-RateLimit-* headers tell you exactly where you stand.
HTTP/1.1 429 Too Many RequestsRetry-After: 12X-RateLimit-Limit: 60X-RateLimit-Remaining: 0X-RateLimit-Reset: 1714324800 { "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Rate limit exceeded. Retry after 12 seconds." }}Production-grade clients should retry with exponential backoff and a cap. The official SDKs do this automatically:
// Exponential backoff with respect for Retry-Afterasync function callWithRetry(url, init, attempt = 0) { const res = await fetch(url, init) if (res.status !== 429 || attempt >= 4) return res const retryAfter = Number(res.headers.get("retry-after")) || 2 ** attempt await new Promise(r => setTimeout(r, retryAfter * 1000)) return callWithRetry(url, init, attempt + 1)}See Rate Limits for plan-by-plan numbers.
CORS error in the browser
KhaleejiAPI is a server-to-server API. Calling it directly from a browser is intentionally blocked by CORS — and even if it weren't, doing so would expose your API key to every site visitor.
The fix is the same in every framework: put a thin proxy on your backend that attaches the key, and let the browser call your proxy.
// ❌ Don't do this in the browser — your key will leak.fetch("https://khaleejiapi.dev/api/v1/ip/lookup?ip=8.8.8.8", { headers: { "x-api-key": "khj_live_..." },}) // ✅ Do this instead: call your own backend, which calls KhaleejiAPI.fetch("/api/proxy/ip-lookup?ip=8.8.8.8")Timeouts on AI endpoints
Standard endpoints (IP lookup, exchange rates, weather) reply in well under a second. AI-backed endpoints — translation, summarization, image processing — can take 3–8 seconds on a cold cache because they call upstream models. Default fetch timeouts of 5s will trip on those.
// Most KhaleejiAPI endpoints respond in <500ms.// AI endpoints (translate, summarize) can take 3–8s on cold cache.// Set a generous timeout for those.const controller = new AbortController()const timer = setTimeout(() => controller.abort(), 15_000)try { const res = await fetch(url, { signal: controller.signal, ...init })} finally { clearTimeout(timer)}If you consistently hit timeouts on cached endpoints, that's a problem on our side — file a ticket with the request id (see below).
Sporadic 5xx errors / “it worked yesterday”
Before assuming a regression, check status.khaleejiapi.dev and the upstream provider for the endpoint you called (we publish the upstream dependency for each API in its docs page).
When you contact support, include the x-request-id from the failing response. We index logs by that id and can usually pinpoint the cause within minutes.
// Every response includes an x-request-id header.// Capture it and include it when you contact support.const res = await fetch("https://khaleejiapi.dev/api/v1/health")console.log("request id:", res.headers.get("x-request-id"))