One prompt. A finished book.

Open Nellie Sign in

Troubleshooting Guide

Step-by-step guides for debugging common Nellie API issues.

Quick Diagnosis

Is the API Accessible?

# Test API connectivity
curl -I https://api.nelliewriter.com/v1/configuration

Expected: HTTP/2 200

Is Your Key Valid?

# Test authentication
curl https://api.nelliewriter.com/v1/usage \
  -H "X-API-Key: nel_your_key"

Expected: JSON response with usage data

Is Your Request Valid?

# Test with minimal request
curl -X POST https://api.nelliewriter.com/v1/book \
  -H "X-API-Key: nel_your_key" \
  -H "Content-Type: application/json" \
  -d '{}'

Expected: 202 Accepted with requestId


Authentication Issues

“Unauthorized” Error

Symptoms:

{
  "success": false,
  "error": "Unauthorized",
  "errorCode": "AUTH_REQUIRED"
}

Diagnosis:

  1. Check header name:

    # Correct
    -H "X-API-Key: nel_..."
    
    # Wrong
    -H "Authorization: Bearer nel_..."
    -H "Api-Key: nel_..."
  2. Check key format:

    # Valid format
    nel_abc123def456ghi789...
    
    # Invalid
    abc123...  # Missing prefix
  3. Check environment variable:

    echo $NELLIE_API_KEY
    # Should output your key

Solutions:

# Debug authentication
import os

api_key = os.environ.get("NELLIE_API_KEY")

# Check if set
if not api_key:
    print("❌ NELLIE_API_KEY not set")
elif not api_key.startswith("nel_"):
    print("❌ Invalid key format (should start with 'nel_')")
else:
    print(f"✅ Key found: {api_key[:10]}...{api_key[-4:]}")

“Invalid API Key” Error

Symptoms:

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

Causes:

  • Key was revoked
  • Key was copied incorrectly
  • Using wrong environment’s key

Solution:

  1. Log into the Nellie dashboard
  2. Go to Settings → API Management
  3. Check if your key is listed and active
  4. If not, create a new key

Rate Limit Issues

“Rate limit exceeded” Error

Symptoms:

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

Diagnosis:

# Check your request timing
import time

start = time.time()
# ... your code ...
elapsed = time.time() - start
print(f"Time since last request: {elapsed}s")
# Should be > 6 seconds

Solutions:

  1. Add delays between requests:

    import time
    
    for prompt in prompts:
         book = client.books.create(prompt=prompt)
         time.sleep(7)  # Wait 7 seconds between requests
  2. Implement retry with backoff:

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

“Daily rate limit exceeded” Error

Symptoms:

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

Diagnosis:

usage = client.get_usage()
print(f"Total requests today: {usage.total_requests}")
# If >= 15, you've hit the limit

Solutions:

  • Wait 24 hours from your first request of the day
  • Contact sales for higher limits
  • Use multiple API keys (with separate accounts)

Generation Failures

Job Status “failed”

Symptoms:

{
  "requestId": "...",
  "status": "failed",
  "error": "Error message here"
}

Diagnosis:

status = client.books.retrieve(request_id)

print(f"Status: {status.status}")
print(f"Progress: {status.progress}%")
print(f"Error: {status.error}")
print(f"Messages: {status.messages}")

Common Errors:

ErrorCauseSolution
“Insufficient credits”Not enough creditsPurchase more credits
“Content policy violation”Inappropriate promptModify your prompt
“Generation timeout”Job took too longRetry with simpler prompt
“Internal error”Server issueRetry after a few minutes

“Insufficient credits” Error

Diagnosis:

usage = client.get_usage()
models = client.get_models()

print(f"Credits used: {usage.total_credits_used}")

# Check cost for your model
for model in models:
    print(f"{model.name}: {model.cost_per_book} credits")

Solution:

  1. Purchase more credits in the Nellie app
  2. Use a lower-cost model (2.0 instead of 3.0)
  3. Disable images to reduce cost

Job Stuck at 0%

Symptoms:

  • Status stays “queued” for extended time
  • Progress stays at 0%

Diagnosis:

import time

status = client.books.retrieve(request_id)
print(f"Created: {status.created_at}")
print(f"Status: {status.status}")

# Check how long it's been
from datetime import datetime
created = datetime.fromisoformat(status.created_at.replace('Z', '+00:00'))
elapsed = datetime.now(created.tzinfo) - created
print(f"Elapsed: {elapsed}")

Solutions:

  • Wait longer (generation can take 15-80 minutes)
  • Check system status for outages
  • If > 2 hours, contact support with requestId

Webhook Issues

Webhooks Not Received

Diagnosis Checklist:

  1. Is your URL publicly accessible?

    curl -X POST https://your-webhook-url.com/path \
      -H "Content-Type: application/json" \
      -d '{"test": true}'
  2. Is it HTTPS?

    • Must be https:// (not http://)
  3. Check server logs:

    • Look for incoming POST requests
    • Check for any errors
  4. Is the certificate valid?

    curl -v https://your-webhook-url.com 2>&1 | grep "SSL certificate"

Solutions:

  • Use a tunnel for local testing:

    ngrok http 5000
    # Then use the https://xxx.ngrok.io URL
  • Check firewall allows incoming connections

  • Ensure your server responds within 30 seconds

Signature Verification Fails

Symptoms:

  • WebhookSignatureError raised
  • Signature comparison fails

Diagnosis:

# Debug signature verification
sig_header = request.headers.get("X-Nellie-Signature")
print(f"Header: {sig_header}")

# Parse components
parts = dict(x.split('=', 1) for x in sig_header.split(','))
print(f"Timestamp: {parts.get('t')}")
print(f"Signature: {parts.get('v1')}")

# Check timestamp age
import time
timestamp = int(parts.get('t', 0))
age = abs(time.time() - timestamp)
print(f"Signature age: {age}s (max allowed: 300s)")

Common Causes:

  1. Wrong secret:

    • Make sure you’re using the webhook secret (starts with whsec_)
    • Not the API key
  2. Modified payload:

    • Use raw request body, not parsed JSON
    # ✅ Correct
    payload = request.data  # Raw bytes
    
    # ❌ Wrong
    payload = json.dumps(request.json)  # Re-serialized
  3. Timestamp expired:

    • Signature must be within 5 minutes
    • Check server time sync

Solution:

from nellie_api import Webhook, WebhookSignatureError

@app.route("/webhook", methods=["POST"])
def webhook():
    # Use raw body
    payload = request.data
    sig_header = request.headers.get("X-Nellie-Signature")
    
    # Debug info
    print(f"Payload length: {len(payload)}")
    print(f"Signature header: {sig_header}")
    
    try:
        event = Webhook.construct_event(payload, sig_header, WEBHOOK_SECRET)
        return "OK", 200
    except WebhookSignatureError as e:
        print(f"Verification failed: {e}")
        return "Invalid signature", 400

SDK Issues

Import Errors

Symptoms:

ModuleNotFoundError: No module named 'nellie_api'

Solutions:

  1. Install the package:

    pip install nellie-api
  2. Check virtual environment:

    which python
    pip list | grep nellie
  3. Check Python version (requires 3.8+):

    python --version

Connection Errors

Symptoms:

APIError: Connection error: ...

Diagnosis:

import requests

try:
    response = requests.get("https://api.nelliewriter.com/v1/configuration", timeout=10)
    print(f"Status: {response.status_code}")
except requests.exceptions.RequestException as e:
    print(f"Connection error: {e}")

Solutions:

  • Check internet connectivity
  • Check firewall/proxy settings
  • Try increasing timeout:
    client = Nellie(api_key="...", timeout=60)

Timeout Errors

Symptoms:

TimeoutError: Book generation timed out after Xs

Solutions:

  1. Increase timeout:

    result = client.books.wait_for_completion(
        request_id,
        timeout=14400  # 4 hours
    )
  2. Use webhooks instead of polling

  3. Check job status manually:

    status = client.books.retrieve(request_id)
    print(f"Status: {status.status}, Progress: {status.progress}%")

Debugging Tools

Request Logger

import logging

# Enable debug logging
logging.basicConfig(level=logging.DEBUG)

# Or specifically for requests
logging.getLogger("urllib3").setLevel(logging.DEBUG)

API Tester Script

#!/usr/bin/env python3
"""Test Nellie API connectivity and authentication."""

import os
import sys
from nellie_api import Nellie, NellieError

def main():
    print("=" * 50)
    print("NELLIE API DIAGNOSTIC")
    print("=" * 50)
    
    # Check environment
    api_key = os.environ.get("NELLIE_API_KEY")
    if not api_key:
        print("❌ NELLIE_API_KEY not set")
        sys.exit(1)
    print(f"✅ API key found: {api_key[:10]}...{api_key[-4:]}")
    
    try:
        client = Nellie(api_key=api_key)
        
        # Test configuration endpoint
        print("\nTesting /v1/configuration...")
        config = client.get_configuration()
        print(f"✅ Got {len(config.styles)} styles, {len(config.types)} types")
        
        # Test authenticated endpoint
        print("\nTesting /v1/usage...")
        usage = client.get_usage()
        print(f"✅ Total requests: {usage.total_requests}")
        print(f"✅ Credits used: {usage.total_credits_used}")
        
        # Test models
        print("\nTesting /v1/models...")
        models = client.get_models()
        for m in models:
            print(f"   {m.name}: {m.cost_per_book} credits")
        
        print("\n" + "=" * 50)
        print("ALL TESTS PASSED ✅")
        print("=" * 50)
        
    except NellieError as e:
        print(f"\n❌ Error: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()

Webhook Tester

#!/usr/bin/env python3
"""Test webhook signature verification."""

import json
from nellie_api import Webhook

# Test payload
payload = json.dumps({
    "requestId": "test-123",
    "status": "completed",
    "resultUrl": "https://example.com/test.pdf"
})

# Generate signature
secret = "whsec_test_secret"
signature = Webhook.generate_signature(payload, secret)

print(f"Payload: {payload}")
print(f"Signature: {signature}")

# Verify
is_valid = Webhook.verify_signature(payload, signature, secret)
print(f"Verification: {'✅ Valid' if is_valid else '❌ Invalid'}")

Getting Help

Information to Include

When contacting support, include:

  1. Request ID (if applicable)
  2. Error message (full JSON response)
  3. Timestamp of the issue
  4. Code snippet (without API keys!)
  5. SDK version:
    import nellie_api
    print(nellie_api.__version__)

Support Channels