Documentation Index
Fetch the complete documentation index at: https://docs.ninjachat.ai/llms.txt
Use this file to discover all available pages before exploring further.
All errors return JSON with an error code and message:
{
"error": "insufficient_credits",
"message": "Insufficient credits. Add more at Developers > Billing."
}
Error reference
| Status | Code | What’s wrong | What to do |
|---|
| 400 | invalid_json | Body isn’t valid JSON | Fix your JSON syntax |
| 400 | validation_error | Bad parameter(s) | Check the details array (see below) |
| 401 | missing_api_key | No auth header | Add Authorization: Bearer nj_sk_... |
| 401 | invalid_api_key | Bad or revoked key | Check key starts with nj_sk_ |
| 402 | insufficient_credits | Out of credits | Add credits |
| 429 | rate_limit_exceeded | Too many requests | Wait Retry-After seconds |
| 502 | generation_failed | Upstream failed | Retry — not charged |
| 500 | internal_error | Server error | Retry |
Validation errors
400 errors include a details array telling you exactly which fields are wrong:
{
"error": "validation_error",
"message": "Invalid parameters",
"details": [
{ "field": "messages", "message": "Required" },
{ "field": "model", "message": "Invalid model ID" }
]
}
Error handling code
import os, time, requests
HEADERS = {"Authorization": f"Bearer {os.environ['NINJACHAT_API_KEY']}"}
def chat(model, message, retries=3):
for attempt in range(retries):
r = requests.post("https://ninjachat.ai/api/v1/chat",
headers=HEADERS,
json={"model": model, "messages": [{"role": "user", "content": message}]}
)
if r.status_code == 200:
return r.json()["message"]["content"]
if r.status_code == 429: # Rate limited — retry
time.sleep(int(r.headers.get("Retry-After", 2 ** attempt)))
continue
if r.status_code >= 500: # Server error — retry
time.sleep(2 ** attempt)
continue
# Client errors — don't retry
err = r.json()
raise Exception(f"{r.status_code} {err['error']}: {err['message']}")
raise Exception("Max retries exceeded")
Common mistakes
| Symptom | Cause | Fix |
|---|
validation_error on model | Typo in model ID (e.g. gpt5 instead of gpt-5) | Check available models for exact IDs |
validation_error on messages | Missing role or content field | Each message needs {"role": "user", "content": "..."} |
insufficient_credits | Balance is $0.00 | Add credits |
invalid_api_key | Key revoked or copied incorrectly | Keys start with nj_sk_. Create a new one in Developers |
| Empty streaming response | Not reading SSE stream correctly | See streaming guide |
Proactive warnings
You don’t have to wait for a 402 error. Every response includes a balance_warning when your balance drops below $5.00:
{
"balance_warning": {
"warning": "low_balance",
"threshold": "Balance is $3.50. Consider adding credits soon."
}
}
Check for balance_warning in responses and alert yourself before running out. See Pricing for details.
Quick debug
# Test if your key works (should print 200)
curl -s -o /dev/null -w "%{http_code}" \
-X POST https://ninjachat.ai/api/v1/chat \
-H "Authorization: Bearer nj_sk_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-5-mini","messages":[{"role":"user","content":"hi"}]}'
| Output | Meaning |
|---|
200 | Working |
401 | Bad API key |
402 | No credits |