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 #
| Parameter | Type | Default | Description |
|---|---|---|---|
api_key |
str | None |
Your API key. Falls back to NELLIE_API_KEY env var |
base_url |
str | https://api.nelliewriter.com |
API base URL |
max_retries |
int | 3 |
Number of retries for failed requests |
timeout |
int | 30 |
Request timeout in seconds |
Environment Variables #
| Variable | Description |
|---|---|
NELLIE_API_KEY |
Your API key (used if not passed to constructor) |
NELLIE_API_BASE_URL |
Override 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 #
| Parameter | Type | Default | Description | |
|---|---|---|---|---|
prompt |
str | " |
Book idea or instructions | |
style |
BookStyle | "automatic" |
Genre/narrative style | |
type |
BookType | "automatic" |
Output format type | |
images |
bool | False |
Generate illustrations | |
author |
str | "Nellie" |
Author attribution | |
custom_tone |
str | " |
Additional style guidance | |
model |
ModelVersion | "2.0" |
AI model version | |
output_format |
OutputFormat | "txt" |
Export format | |
webhook_url |
str | None | None |
Webhook 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 #
| Parameter | Type | Description |
|---|---|---|
request_id |
str | The 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 #
| Parameter | Type | Default | Description |
|---|---|---|---|
request_id |
str | — | The job ID |
poll_interval |
int | 120 |
Seconds between status checks |
timeout |
int | 7200 |
Max seconds to wait (2 hours) |
on_progress |
Callable | None |
Called after each status check |
Returns #
Final Book object (completed or failed).
Raises #
TimeoutError: If job doesn’t complete within timeoutAPIError: 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 #
| Parameter | Type | Default | Description |
|---|---|---|---|
request_id |
str | — | The ID of a completed book |
output_path |
str | — | Local file path to save the content |
chunk_size |
int | 8192 |
Chunk 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 valuestypes: List of valid type valuesformats: 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 namedescription: Model descriptioncost_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 madetotal_credits_used: Cumulative credits consumedrecent_requests: List of recentUsageRequestobjects
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 #
| Parameter | Type | Description | |
|---|---|---|---|
payload |
str | bytes | Raw request body |
sig_header |
str | X-Nellie-Signature header |
|
secret |
str | Your webhook secret |
Returns #
Book object parsed from the payload.
Raises #
WebhookSignatureError: Invalid or expired signatureValueError: 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 Requests500 Internal Server Error502 Bad Gateway503 Service Unavailable504 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()
Related Documentation #
- Authentication — Getting your API key
- Webhooks — Webhook verification
- Errors — Error code reference
- Examples — More code examples