Python SDK Reference

The official Python client library for the Nellie AI Book Generation API.

SDK Version: 0.1.0
API Version: v1
Python: 3.8+

Installation

pip install nellie-api

Quick Start

from nellie_api import Nellie

# Initialize client
client = Nellie(api_key="nel_your_api_key")

# Generate a book
book = client.books.create(
    prompt="A mystery novel set in Victorian London",
    style="mystery",
    type="novel",
    output_format="epub"
)

print(f"Started job: {book.request_id}")

# Wait for completion
result = client.books.wait_for_completion(
    book.request_id,
    on_progress=lambda s: print(f"Progress: {s.progress}%")
)

if result.is_successful():
    print(f"Download: {result.result_url}")

Client Initialization

Constructor

client = Nellie(
    api_key="nel_...",      # Required (or use NELLIE_API_KEY env var)
    base_url=None,           # Optional: Override API URL
    max_retries=3,           # Optional: Retry count for failures
    timeout=30               # Optional: Request timeout in seconds
)

Parameters

ParameterTypeDefaultDescription
api_keystrNoneYour API key. Falls back to NELLIE_API_KEY env var
base_urlstrhttps://api.nelliewriter.comAPI base URL
max_retriesint3Number of retries for failed requests
timeoutint30Request timeout in seconds

Environment Variables

VariableDescription
NELLIE_API_KEYYour API key (used if not passed to constructor)
NELLIE_API_BASE_URLOverride the API base URL
import os
os.environ["NELLIE_API_KEY"] = "nel_..."

# No api_key needed
client = Nellie()

Books

create()

Start a new book generation job.

book = client.books.create(
    prompt="A sci-fi adventure on Mars",
    style="sci_fi",
    type="novel",
    images=True,
    author="Jane Smith",
    custom_tone="Cinematic pacing, vivid descriptions",
    model="3.0",
    output_format="epub",
    webhook_url="https://myapp.com/webhook"
)

Parameters

ParameterTypeDefaultDescription
promptstr""Book idea or instructions
styleBookStyle"automatic"Genre/narrative style
typeBookType"automatic"Output format type
imagesboolFalseGenerate illustrations
authorstr"Nellie"Author attribution
custom_tonestr""Additional style guidance
modelModelVersion"2.0"AI model version
output_formatOutputFormat"txt"Export format
webhook_urlstr | NoneNoneWebhook notification URL

Returns

Book object with initial status.

retrieve()

Get the status of an existing job.

status = client.books.retrieve("request-id-here")

print(f"Status: {status.status}")
print(f"Progress: {status.progress}%")

if status.is_successful():
    print(f"Download: {status.result_url}")

Parameters

ParameterTypeDescription
request_idstrThe job ID from create()

Returns

Book object with current status.

wait_for_completion()

Poll until the job finishes.

result = client.books.wait_for_completion(
    request_id="...",
    poll_interval=120,      # Seconds between checks (min: 60)
    timeout=7200,           # Max wait time in seconds
    on_progress=callback    # Optional progress callback
)

Parameters

ParameterTypeDefaultDescription
request_idstrThe job ID
poll_intervalint120Seconds between status checks
timeoutint7200Max seconds to wait (2 hours)
on_progressCallableNoneCalled after each status check

Returns

Final Book object (completed or failed).

Raises

  • TimeoutError: If job doesn’t complete within timeout
  • APIError: If status check fails

Example with Progress Callback

def on_progress(book):
    print(f"[{book.progress}%] {book.status}")
    if book.messages:
        print(f"  Latest: {book.messages[-1]}")

result = client.books.wait_for_completion(
    book.request_id,
    on_progress=on_progress
)

download()

Download the generated content for a completed book directly to a file.

# Wait for completion first
result = client.books.wait_for_completion(book.request_id)

if result.is_successful():
    # Download to a local file
    path = client.books.download(
        request_id=result.request_id,
        output_path="my_novel.epub"
    )
    print(f"Downloaded to: {path}")

Parameters

ParameterTypeDefaultDescription
request_idstrThe ID of a completed book
output_pathstrLocal file path to save the content
chunk_sizeint8192Chunk size for streaming download

Returns

str — The path to the downloaded file.

Raises

  • APIError: If the book is not completed or download fails

Example

from nellie_api import Nellie

client = Nellie()

# Generate and wait
book = client.books.create(
    prompt="A mystery novel",
    output_format="pdf"
)
result = client.books.wait_for_completion(book.request_id)

# Download when complete
if result.is_successful():
    client.books.download(result.request_id, "mystery_novel.pdf")
    print("Download complete!")

Configuration Methods

get_configuration()

Get valid parameter options.

config = client.get_configuration()

print(f"Styles: {config.styles}")
print(f"Types: {config.types}")
print(f"Formats: {config.formats}")

Returns

Configuration object containing:

  • styles: List of valid style values
  • types: List of valid type values
  • formats: List of valid output_format values

get_models()

Get available AI models.

models = client.get_models()

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

Returns

List of Model objects containing:

  • id: Model identifier (e.g., “2.0”, “3.0”)
  • name: Human-readable name
  • description: Model description
  • cost_per_book: Base credit cost

get_usage()

Get usage statistics for your API key.

usage = client.get_usage()

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

for req in usage.recent_requests:
    print(f"  {req.request_id}: {req.status}")

Returns

Usage object containing:

  • total_requests: Total API requests made
  • total_credits_used: Cumulative credits consumed
  • recent_requests: List of recent UsageRequest objects

Data Types

Book

Represents a book generation job.

@dataclass
class Book:
    request_id: str
    status: str              # "queued", "processing", "completed", "failed"
    message: str | None
    status_url: str | None
    progress: int            # 0-100
    created_at: str | None
    completed_at: str | None
    credits_used: int
    result_url: str | None
    messages: list[str]
    error: str | None
    error_message: str | None

Methods

book.is_complete()     # True if completed or failed
book.is_successful()   # True if completed successfully

Configuration

@dataclass
class Configuration:
    styles: list[str]
    types: list[str]
    formats: list[str]

Model

@dataclass
class Model:
    id: str
    name: str
    description: str
    cost_per_book: int

Usage

@dataclass
class Usage:
    total_requests: int
    total_credits_used: int
    recent_requests: list[UsageRequest]

@dataclass
class UsageRequest:
    request_id: str
    status: str
    created_at: str | None
    completed_at: str | None
    credits_used: int

Type Literals

The SDK exports type literals for IDE autocomplete:

from nellie_api import (
    BookStyle,      # "automatic" | "action" | "mystery" | ...
    BookType,       # "automatic" | "novel" | "comic" | ...
    ModelVersion,    # "2.0" | "3.0"
    OutputFormat,    # "txt" | "pdf" | "epub" | ...
)

# Use for type hints
style: BookStyle = "mystery"
model: ModelVersion = "3.0"

Webhooks

Webhook.construct_event()

Verify and parse a webhook payload.

from nellie_api import Webhook, WebhookSignatureError

try:
    event = Webhook.construct_event(
        payload=request.body,           # Raw bytes or string
        sig_header=request.headers['X-Nellie-Signature'],
        secret="whsec_..."
    )
    
    if event.is_successful():
        print(f"Book ready: {event.result_url}")
        
except WebhookSignatureError:
    return "Invalid signature", 400

Parameters

ParameterTypeDescription
payloadstr | bytesRaw request body
sig_headerstrX-Nellie-Signature header
secretstrYour webhook secret

Returns

Book object parsed from the payload.

Raises

  • WebhookSignatureError: Invalid or expired signature
  • ValueError: Invalid JSON payload

Webhook.verify_signature()

Verify signature without parsing.

is_valid = Webhook.verify_signature(
    payload=request.body,
    sig_header=request.headers['X-Nellie-Signature'],
    secret="whsec_..."
)

if is_valid:
    data = json.loads(request.body)

Webhook.generate_signature()

Generate a signature for testing.

import json
from nellie_api import Webhook

payload = json.dumps({"requestId": "test", "status": "completed"})
signature = Webhook.generate_signature(payload, "whsec_test_secret")

# Use in tests
response = client.post(
    '/webhook',
    data=payload,
    headers={'X-Nellie-Signature': signature}
)

Error Handling

Exception Hierarchy

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

AuthenticationError

Raised when API key is invalid or missing.

from nellie_api import Nellie, AuthenticationError

try:
    client = Nellie(api_key="invalid")
    client.books.create(prompt="...")
except AuthenticationError as e:
    print(f"Auth failed: {e}")
    print(f"Status code: {e.status_code}")  # 401

RateLimitError

Raised when rate limits are exceeded.

from nellie_api import RateLimitError
import time

try:
    book = client.books.create(prompt="...")
except RateLimitError as e:
    print("Rate limited, waiting...")
    time.sleep(60)
    # Retry

APIError

Raised for other API errors.

from nellie_api import APIError

try:
    book = client.books.create(style="invalid_style")
except APIError as e:
    print(f"API error: {e}")
    print(f"Status: {e.status_code}")
    print(f"Body: {e.body}")

Complete Error Handling Example

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

def generate_book_safely(prompt: str) -> str | None:
    try:
        client = Nellie()
        book = client.books.create(prompt=prompt)
        result = client.books.wait_for_completion(book.request_id)
        
        if result.is_successful():
            return result.result_url
        else:
            print(f"Generation failed: {result.error}")
            return None
            
    except AuthenticationError:
        print("Invalid API key - check NELLIE_API_KEY")
        return None
        
    except RateLimitError:
        print("Rate limit exceeded - try again later")
        return None
        
    except TimeoutError:
        print("Generation timed out")
        return None
        
    except APIError as e:
        print(f"API error ({e.status_code}): {e}")
        return None
        
    except NellieError as e:
        print(f"Unexpected error: {e}")
        return None

Retry Configuration

The SDK automatically retries failed requests:

# Default: 3 retries with exponential backoff
client = Nellie(api_key="...", max_retries=3)

# Disable retries
client = Nellie(api_key="...", max_retries=0)

# More aggressive retries
client = Nellie(api_key="...", max_retries=5)

Retries apply to:

  • 429 Too Many Requests
  • 500 Internal Server Error
  • 502 Bad Gateway
  • 503 Service Unavailable
  • 504 Gateway Timeout

Complete Example

from nellie_api import Nellie, APIError, RateLimitError
import os

def main():
    # Initialize client
    client = Nellie(api_key=os.environ["NELLIE_API_KEY"])
    
    # Check available credits
    usage = client.get_usage()
    print(f"Credits used: {usage.total_credits_used}")
    
    # Get model costs
    models = client.get_models()
    model_3 = next(m for m in models if m.id == "3.0")
    print(f"Nellie 3.0 costs {model_3.cost_per_book} credits")
    
    # Generate a book
    try:
        book = client.books.create(
            prompt="A cyberpunk heist in Neo Tokyo",
            style="cyberpunk",
            type="novel",
            images=True,
            model="3.0",
            output_format="pdf"
        )
        print(f"Started: {book.request_id}")
        
        # Wait with progress updates
        result = client.books.wait_for_completion(
            book.request_id,
            poll_interval=120,
            timeout=7200,
            on_progress=lambda s: print(f"[{s.progress}%] {s.status}")
        )
        
        if result.is_successful():
            print(f"\n✅ Done! Download: {result.result_url}")
            print(f"Credits used: {result.credits_used}")
        else:
            print(f"\n❌ Failed: {result.error}")
            
    except RateLimitError:
        print("Rate limit - wait and retry")
    except APIError as e:
        print(f"API Error: {e}")

if __name__ == "__main__":
    main()