One prompt. A finished book.

Open Nellie Sign in

Error Reference

Complete guide to Nellie API error codes and how to handle them.

Error Response Format

All API errors follow a consistent JSON format:

{
  "success": false,
  "error": "Human-readable error message",
  "details": "Additional context (optional)",
  "errorCode": "MACHINE_READABLE_CODE"
}
FieldTypeDescription
successbooleanAlways false for errors
errorstringHuman-readable error message
detailsstringAdditional context (optional)
errorCodestringMachine-readable code for programmatic handling

HTTP Status Codes

400 Bad Request

Invalid request parameters or malformed request body.

INVALID_PARAMETER

{
  "success": false,
  "error": "Field 'style' must be one of: action, adventure, automatic, ...",
  "errorCode": "INVALID_PARAMETER"
}

Causes:

  • Invalid value for style, type, model, or output_format
  • Invalid data type (e.g., string where boolean expected)

Solution:

# Check valid values before submitting
config = client.get_configuration()
if style not in config.styles:
    raise ValueError(f"Invalid style. Valid options: {config.styles}")

INVALID_WEBHOOK_URL

{
  "success": false,
  "error": "Invalid webhook URL: must be a valid HTTPS URL",
  "errorCode": "INVALID_WEBHOOK_URL"
}

Causes:

  • URL is not HTTPS
  • URL is localhost or internal IP
  • URL is malformed

Solution:

# Ensure webhook URL is valid
import re

def is_valid_webhook_url(url):
    if not url.startswith("https://"):
        return False
    if "localhost" in url or "127.0.0.1" in url:
        return False
    return True

MISSING_PARAMETER

{
  "success": false,
  "error": "Missing required field: prompt",
  "errorCode": "MISSING_PARAMETER"
}

Note: Currently all parameters are optional, but this error may appear in future API versions.


401 Unauthorized

Authentication failed.

AUTH_REQUIRED

{
  "success": false,
  "error": "Unauthorized",
  "details": "Valid API key required",
  "errorCode": "AUTH_REQUIRED"
}

Causes:

  • Missing X-API-Key header
  • Empty API key value

Solution:

# Ensure header is present
headers = {
    "X-API-Key": os.environ.get("NELLIE_API_KEY"),
    "Content-Type": "application/json"
}

if not headers["X-API-Key"]:
    raise ValueError("NELLIE_API_KEY environment variable not set")

AUTH_FAILED

{
  "success": false,
  "error": "Invalid API key",
  "errorCode": "AUTH_FAILED"
}

Causes:

  • API key is invalid
  • API key has been revoked
  • API key is malformed

Solution:

# Verify key format
if not api_key.startswith("nel_"):
    raise ValueError("Invalid API key format")

# Test authentication
try:
    client.get_usage()
except AuthenticationError:
    print("API key is invalid or revoked. Create a new key in the dashboard.")

403 Forbidden

Permission denied.

FORBIDDEN

{
  "success": false,
  "error": "Access denied",
  "errorCode": "FORBIDDEN"
}

Causes:

  • Attempting to access another user’s resource
  • API key lacks required permissions

Solution:

  • Verify you’re using the correct API key
  • Check that the request ID belongs to your account

404 Not Found

Resource not found.

NOT_FOUND

{
  "success": false,
  "error": "Request not found",
  "errorCode": "NOT_FOUND"
}

Causes:

  • Invalid requestId in status endpoint
  • Request was deleted or never existed

Solution:

# Validate request ID format (UUID)
import uuid

def is_valid_request_id(request_id):
    try:
        uuid.UUID(request_id)
        return True
    except ValueError:
        return False

if not is_valid_request_id(request_id):
    raise ValueError("Invalid request ID format")

405 Method Not Allowed

Wrong HTTP method for endpoint.

METHOD_NOT_ALLOWED

{
  "success": false,
  "error": "Method not allowed",
  "errorCode": "METHOD_NOT_ALLOWED"
}

Causes:

  • Using GET instead of POST for /v1/book
  • Using POST instead of GET for /v1/status

Solution:

# Correct methods:
# POST /v1/book - Create book
# GET  /v1/status/{id} - Check status
# GET  /v1/configuration - Get config
# GET  /v1/models - Get models
# GET  /v1/usage - Get usage (requires auth)

429 Too Many Requests

Rate limit exceeded.

RATE_LIMIT_EXCEEDED (Burst)

{
  "success": false,
  "error": "Rate limit exceeded: Please wait a few seconds between requests",
  "errorCode": "RATE_LIMIT_EXCEEDED"
}

Causes:

  • Requests sent faster than 1 per 6 seconds

Solution:

import time
from nellie_api import RateLimitError

def create_with_backoff(prompt, max_retries=3):
    for attempt in range(max_retries):
        try:
            return client.books.create(prompt=prompt)
        except RateLimitError:
            wait_time = 6 * (attempt + 1)
            print(f"Rate limited. Waiting {wait_time}s...")
            time.sleep(wait_time)
    raise Exception("Max retries exceeded")

RATE_LIMIT_EXCEEDED (Daily)

{
  "success": false,
  "error": "Daily rate limit exceeded (15 requests/day)",
  "errorCode": "RATE_LIMIT_EXCEEDED"
}

Causes:

  • Exceeded 15 requests in 24 hours

Solution:

# Check remaining daily quota
usage = client.get_usage()
if usage.total_requests >= 15:
    print("Daily limit reached. Try again tomorrow.")

500 Internal Server Error

Server-side error.

INTERNAL_ERROR

{
  "success": false,
  "error": "Internal server error",
  "errorCode": "INTERNAL_ERROR"
}

Causes:

  • Temporary server issue
  • Unexpected error during processing

Solution:

# Implement retry with exponential backoff
import time

def retry_on_server_error(func, max_retries=3):
    for attempt in range(max_retries):
        try:
            return func()
        except APIError as e:
            if e.status_code >= 500:
                wait_time = 2 ** attempt * 10
                print(f"Server error. Retrying in {wait_time}s...")
                time.sleep(wait_time)
            else:
                raise
    raise Exception("Max retries exceeded")

JOB_START_FAILED

{
  "success": false,
  "error": "Failed to start generation job",
  "errorCode": "JOB_START_FAILED"
}

Causes:

  • Backend queue unavailable
  • Resource constraints

Solution:

  • Wait a few minutes and retry
  • Contact support if persistent

503 Service Unavailable

Service temporarily unavailable.

{
  "success": false,
  "error": "Service temporarily unavailable",
  "errorCode": "SERVICE_UNAVAILABLE"
}

Causes:

  • Planned maintenance
  • High load
  • Infrastructure issues

Solution:

  • Wait and retry with exponential backoff
  • Check status page for announcements

Job Status Errors

When checking status, errors appear in the response body:

Insufficient Credits

{
  "requestId": "uuid",
  "status": "failed",
  "progress": 0,
  "error": "Insufficient credits",
  "errorMessage": "Insufficient credits"
}

Solution:

# Pre-check credits before starting
usage = client.get_usage()
models = client.get_models()

required = next(m.cost_per_book for m in models if m.id == "3.0")
available = CREDIT_LIMIT - usage.total_credits_used

if available < required:
    print(f"Need {required} credits, have {available}")

Generation Error

{
  "requestId": "uuid",
  "status": "failed",
  "progress": 45,
  "error": "Generation failed: content policy violation"
}

Causes:

  • Prompt violated content policy
  • Generation encountered unexpected error

Solution:

  • Review and modify your prompt
  • Contact support with the request ID

SDK Exceptions

The Python SDK raises typed exceptions:

Exception Hierarchy

NellieError (base)
├── AuthenticationError (401)
├── RateLimitError (429)
└── APIError (other errors)

Handling Exceptions

from nellie_api import (
    Nellie,
    NellieError,
    AuthenticationError,
    RateLimitError,
    APIError
)

try:
    client = Nellie()
    book = client.books.create(prompt="...")
    
except AuthenticationError as e:
    # Invalid or missing API key
    print(f"Auth error: {e}")
    print(f"Status code: {e.status_code}")  # 401
    
except RateLimitError as e:
    # Too many requests
    print(f"Rate limited: {e}")
    print(f"Status code: {e.status_code}")  # 429
    
except APIError as e:
    # Other API errors
    print(f"API error: {e}")
    print(f"Status code: {e.status_code}")
    print(f"Response body: {e.body}")
    
except NellieError as e:
    # Catch-all for SDK errors
    print(f"Nellie error: {e}")

WebhookSignatureError

from nellie_api import Webhook, WebhookSignatureError

try:
    event = Webhook.construct_event(payload, sig_header, secret)
except WebhookSignatureError:
    # Invalid signature
    return "Invalid signature", 400

Error Handling Patterns

Comprehensive Handler

from nellie_api import (
    Nellie,
    AuthenticationError,
    RateLimitError,
    APIError,
    NellieError
)
import time
import logging

logger = logging.getLogger(__name__)

def safe_generate(prompt: str, max_retries: int = 3) -> dict:
    """Generate a book with comprehensive error handling."""
    
    for attempt in range(max_retries):
        try:
            client = Nellie()
            book = client.books.create(prompt=prompt)
            
            return {
                "success": True,
                "request_id": book.request_id
            }
            
        except AuthenticationError as e:
            logger.error(f"Authentication failed: {e}")
            return {
                "success": False,
                "error_code": "AUTH_ERROR",
                "message": "Invalid API key. Please check your configuration.",
                "retryable": False
            }
            
        except RateLimitError as e:
            wait_time = 10 * (attempt + 1)
            logger.warning(f"Rate limited. Waiting {wait_time}s...")
            time.sleep(wait_time)
            continue  # Retry
            
        except APIError as e:
            if e.status_code >= 500:
                # Server error - retry
                wait_time = 5 * (attempt + 1)
                logger.warning(f"Server error ({e.status_code}). Retrying in {wait_time}s...")
                time.sleep(wait_time)
                continue
            else:
                # Client error - don't retry
                logger.error(f"API error: {e}")
                return {
                    "success": False,
                    "error_code": "API_ERROR",
                    "message": str(e),
                    "status_code": e.status_code,
                    "retryable": False
                }
                
        except NellieError as e:
            logger.exception(f"Unexpected error: {e}")
            return {
                "success": False,
                "error_code": "UNKNOWN_ERROR",
                "message": str(e),
                "retryable": False
            }
    
    # Max retries exceeded
    return {
        "success": False,
        "error_code": "MAX_RETRIES",
        "message": "Request failed after multiple attempts",
        "retryable": True
    }

User-Friendly Error Messages

ERROR_MESSAGES = {
    "AUTH_REQUIRED": "Please log in to continue.",
    "AUTH_FAILED": "Your session has expired. Please log in again.",
    "RATE_LIMIT_EXCEEDED": "You're making requests too quickly. Please wait a moment.",
    "INVALID_PARAMETER": "Some of your inputs are invalid. Please check and try again.",
    "INSUFFICIENT_CREDITS": "You don't have enough credits. Please purchase more.",
    "NOT_FOUND": "The requested book could not be found.",
    "INTERNAL_ERROR": "Something went wrong on our end. Please try again later.",
}

def get_user_message(error_code: str, default: str = "An error occurred.") -> str:
    return ERROR_MESSAGES.get(error_code, default)

Troubleshooting Guide

ErrorCheckSolution
401 AUTH_REQUIREDIs X-API-Key header present?Add header to request
401 AUTH_FAILEDIs key valid?Create new key in dashboard
429 RATE_LIMITRequests too fast?Add delays between requests
429 RATE_LIMITDaily limit reached?Wait 24 hours or contact sales
400 INVALID_PARAMETERCheck parameter valuesUse /v1/configuration to get valid values
404 NOT_FOUNDValid request ID?Verify the ID was returned by /v1/book
500 INTERNAL_ERRORTemporary issue?Retry with exponential backoff
Job failed: Insufficient creditsCheck credit balancePurchase more credits