Skip to main content
All errors return JSON with an error code and message:
{
  "error": "insufficient_credits",
  "message": "Insufficient credits. Add more at Developers > Billing."
}

Error reference

StatusCodeWhat’s wrongWhat to do
400invalid_jsonBody isn’t valid JSONFix your JSON syntax
400validation_errorBad parameter(s)Check the details array (see below)
401missing_api_keyNo auth headerAdd Authorization: Bearer nj_sk_...
401invalid_api_keyBad or revoked keyCheck key starts with nj_sk_
402insufficient_creditsOut of creditsAdd credits
429rate_limit_exceededToo many requestsWait Retry-After seconds
502generation_failedUpstream failedRetry — not charged
500internal_errorServer errorRetry

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

SymptomCauseFix
validation_error on modelTypo in model ID (e.g. gpt5 instead of gpt-5)Check available models for exact IDs
validation_error on messagesMissing role or content fieldEach message needs {"role": "user", "content": "..."}
insufficient_creditsBalance is $0.00Add credits
invalid_api_keyKey revoked or copied incorrectlyKeys start with nj_sk_. Create a new one in Developers
Empty streaming responseNot reading SSE stream correctlySee streaming example

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"}]}'
OutputMeaning
200Working
401Bad API key
402No credits