NekoAI-API
An async, fully typed Python client for the NovelAI API, covering image generation (with first-class support for the V4.5 model family), text generation, Director image tools, and account utilities.
Request payloads are validated with pydantic and verified field-by-field against payloads captured from the NovelAI web client, so what this library sends is what the website sends.
Features
- Image generation — text-to-image, img2img, inpainting, and vibe transfer
(reference images are encoded through
/ai/encode-vibeautomatically, with caching) for every model from V3 to V4.5. - V4.5 support — multi-character prompts with canvas positioning, character-level undesired content, and real-time streaming of denoising steps.
- Text generation — story continuation with Erato, Kayra, and Clio. Plain text in, plain text out, with optional token-by-token streaming.
- Director tools — line art, sketch, background removal, declutter, colorize, and emotion change, each as a single method call.
- Utilities — upscaling, tag autocompletion, ControlNet annotation, and subscription/Anlas queries.
- Robust by default — automatic retry with backoff on rate limits, optional client-side request throttling, and typed exceptions.
- CLI — the
nekoaicommand exposes generation, tools, and account queries for shell usage and scripting. - Custom hosts — every endpoint group can be pointed at a reverse proxy or self-hosted gateway.
Installation
Requires Python 3.10 or later.
pip install -U nekoai-api
# or
uv add nekoai-api
Quick Start
import asyncio
from nekoai import Model, NovelAI, Resolution
async def main():
# Reads the token from the NAI_TOKEN environment variable when omitted
async with NovelAI(token="your_access_token") as client:
images = await client.generate_image(
prompt="1girl, silver hair, blue eyes, white dress, flower garden",
model=Model.V4_5,
res_preset=Resolution.NORMAL_PORTRAIT,
)
for image in images:
image.save("output")
asyncio.run(main())
Authentication
The client accepts either an access token or account credentials:
NovelAI(token="...") # explicit token
NovelAI() # token from $NAI_TOKEN
NovelAI(username="user@example.com", password="...")
To obtain a persistent token, log in once and store the result:
nekoai login <username> <password>
Image Generation
generate_image accepts either a prepared Metadata object or keyword
arguments, and returns a list of Image objects. All parameters are validated
before the request is sent; see the Metadata docstring for the full set.
Models
| Model | Enum | Inpainting variant |
|---|---|---|
| NAI Diffusion V4.5 Full (recommended) | Model.V4_5 |
Model.V4_5_INP |
| NAI Diffusion V4.5 Curated | Model.V4_5_CUR |
Model.V4_5_CUR_INP |
| NAI Diffusion V4 Full | Model.V4 |
Model.V4_INP |
| NAI Diffusion V4 Curated | Model.V4_CUR |
Model.V4_CUR_INP |
| NAI Diffusion V3 | Model.V3 |
Model.V3_INP |
| NAI Diffusion Furry V3 | Model.FURRY |
Model.FURRY_INP |
Common parameters
images = await client.generate_image(
prompt="1girl, cute",
model=Model.V4_5, # default: Model.V4_5
res_preset=Resolution.NORMAL_SQUARE, # or width=..., height=...
steps=28,
scale=6.0, # prompt guidance
seed=1234567890, # random when omitted
n_samples=1,
negative_prompt="lowres",
qualityToggle=True, # append model quality tags
ucPreset=0, # undesired-content preset (varies by model)
)
Multi-character prompts (V4/V4.5)
Each character gets its own prompt, undesired content, and canvas position:
from nekoai import CharacterPrompt, PositionCoords
images = await client.generate_image(
prompt="two people standing together, park background",
model=Model.V4_5,
res_preset=Resolution.NORMAL_LANDSCAPE,
characterPrompts=[
CharacterPrompt(
prompt="girl, red hair, red dress",
uc="bad hands, bad anatomy",
center=PositionCoords(x=0.3, y=0.5),
),
CharacterPrompt(
prompt="boy, blue hair, blue uniform",
uc="bad hands, bad anatomy",
center=PositionCoords(x=0.7, y=0.5),
),
],
)
Real-time streaming (V4/V4.5)
generate_image_stream yields events for every denoising step, which is useful
for progress UIs and timelapses:
from nekoai import EventType
async for event in client.generate_image_stream(
prompt="1girl, cute, anime style",
model=Model.V4_5,
res_preset=Resolution.NORMAL_PORTRAIT,
):
if event.event_type == EventType.INTERMEDIATE:
print(f"step {event.step_ix} (sigma={event.sigma:.2f})")
elif event.event_type == EventType.FINAL:
event.image.save("output", "final.png")
generate_image works with every model and returns the final images;
streaming requires a V4/V4.5 model.
Image to image
from nekoai import Action, parse_image
width, height, base64_image = parse_image("input/source.png")
images = await client.generate_image(
prompt="1girl, fantasy outfit",
model=Model.V4_5,
action=Action.IMG2IMG,
width=width,
height=height,
image=base64_image,
strength=0.5, # lower = closer to the original
noise=0.1,
)
Inpainting
Provide a base image and a black/white mask (white areas are repainted) and use an inpainting model:
from nekoai import Action, parse_image
width, height, base64_image = parse_image("input/portrait.png")
_, _, base64_mask = parse_image("input/mask.png")
images = await client.generate_image(
prompt="1girl, detailed background",
model=Model.V4_5_INP,
action=Action.INPAINT,
width=width,
height=height,
image=base64_image,
mask=base64_mask,
add_original_image=True, # overlay untouched pixels from the original
)
Vibe transfer
Borrow the style and mood of reference images. For V4/V4.5 models the client
encodes references through /ai/encode-vibe automatically (2 Anlas per new
image; results are cached for the client's lifetime, and the Metadata object
can be reused across calls):
from nekoai import parse_image
_, _, reference = parse_image("input/style_reference.png")
images = await client.generate_image(
prompt="landscape, mountains, sunset",
model=Model.V4_5,
res_preset=Resolution.NORMAL_LANDSCAPE,
reference_image_multiple=[reference],
reference_information_extracted_multiple=[1.0],
reference_strength_multiple=[0.7],
)
Text Generation
Continue a story with NovelAI's text models. Input and output are plain text — no tokenizer required. Declared parameters are validated, and any additional sampling parameter accepted by the API (mirostat, cfg, phrase repetition penalty, ...) passes through unchanged:
from nekoai import TextModel
output = await client.generate_text(
"The dragon circled the tower once more,",
model=TextModel.ERATO, # llama-3-erato-v1 (default)
max_length=150,
temperature=1.0,
)
# Stream tokens as they are generated:
async for token in client.generate_text_stream("Once upon a time,", max_length=50):
print(token, end="", flush=True)
| Model | Enum | Identifier |
|---|---|---|
| Erato (recommended) | TextModel.ERATO |
llama-3-erato-v1 |
| Kayra | TextModel.KAYRA |
kayra-v1 |
| Clio | TextModel.CLIO |
clio-v1 |
Other model identifiers can be passed as plain strings. Note that
generate_until_sentence (enabled by default) lets the model run slightly past
max_length to finish a sentence; disable it for hard caps.
Director Tools
Every Director tool is a single method call. Image inputs accept a file path,
pathlib.Path, raw bytes, a file-like object, or a base64 string.
from nekoai import EmotionLevel, EmotionOptions
result = await client.lineart("image.png") # image to line art
result = await client.sketch("image.png") # image to sketch
result = await client.background_removal("image.png") # remove background (costs Anlas)
result = await client.declutter("image.png") # remove text/artifacts
result = await client.colorize("lineart.png", prompt="silver hair", defry=0)
result = await client.change_emotion(
"image.png",
emotion=EmotionOptions.HAPPY,
emotion_level=EmotionLevel.NORMAL,
)
result.save("output")
Utilities
# Upscale 2x or 4x (costs Anlas)
upscaled = await client.upscale("image.png", scale=4)
# Tag autocompletion
tags = await client.suggest_tags("blue hai") # [{"tag": "blue hair", ...}, ...]
# ControlNet condition masks (edge/depth preprocessing).
# Note: ControlNet-guided generation is a V1/V2-era feature not supported by V3+.
from nekoai import Controlnet
mask = await client.annotate_image("image.png", model=Controlnet.SCRIBBLER)
# Subscription tier and Anlas balance
subscription = await client.get_subscription()
user_data = await client.get_user_data()
Command Line Interface
Authentication comes from --token, the NAI_TOKEN environment variable, or
--username/--password.
| Command | Purpose |
|---|---|
nekoai login <user> <pass> |
Exchange credentials for an access token |
nekoai generate <prompt> |
Generate images (txt2img, img2img, inpaint, vibe) |
nekoai text <prompt> |
Generate a text continuation |
nekoai tool <name> <image> |
Run a Director or annotation tool |
nekoai upscale <image> |
Upscale an image 2x or 4x |
nekoai tags <partial> |
Suggest completions for a partial tag |
nekoai subscription |
Show subscription info and Anlas balance |
export NAI_TOKEN="your_access_token"
nekoai generate "1girl, cute" -m v4_5 -s 832x1216 --steps 28 -n 2
nekoai generate "1girl, cute" --stream # live step progress
nekoai generate "1girl, fantasy outfit" --image src.png --strength 0.5 # img2img
nekoai generate "detailed background" --image base.png --mask mask.png # inpaint
nekoai generate "landscape, sunset" --reference-image style.png # vibe
nekoai text "Once upon a time," --max-length 80 --stream
nekoai tool lineart image.png
nekoai tool emotion image.png --emotion happy
nekoai upscale image.png --scale 4
Configuration
Custom hosts
Every host can point at a custom base URL (reverse proxy, self-hosted gateway).
host serves image and account endpoints, text_host serves text generation,
and api_host covers the endpoints still served only by the legacy API host
(upscale, ControlNet annotation):
client = NovelAI(
token="...",
host="https://your-image-proxy.example.com", # default: https://image.novelai.net
text_host="https://your-text-proxy.example.com", # default: https://text.novelai.net
api_host="https://your-api-proxy.example.com", # default: https://api.novelai.net
)
Rate limiting and retries
Requests that hit the rate limit (HTTP 429) are retried with exponential
backoff (max_retries, default 2). For batch workloads, rate_limit enforces
a minimum spacing between requests client-side:
client = NovelAI(token="...", rate_limit=10) # at least 10 seconds between requests
Timeouts and connection lifetime
client = NovelAI(token="...")
await client.init(timeout=60) # per-request timeout in seconds
await client.init(auto_close=True, close_delay=300) # close idle connections
Error Handling
All exceptions derive from nekoai.NovelAIError:
| Exception | Raised on |
|---|---|
AuthError |
Invalid or expired credentials (HTTP 401) |
APIError |
Request validation failure (HTTP 400) |
NotEnoughCreditsError |
Insufficient Anlas or no active subscription (HTTP 402) |
ConcurrentError |
Rate limit exceeded after retries (HTTP 429) |
TimeoutError |
Request exceeded the client timeout |
ImageProcessingError |
Unreadable or unsupported input image |
from nekoai import NovelAI, NovelAIError
try:
images = await client.generate_image(prompt="1girl")
except NovelAIError as e:
print(f"generation failed: {e}")
Examples
One runnable script per feature lives in examples/requests/,
each reading NAI_TOKEN from the environment. See its
README for the full index and per-feature Anlas
costs. NovelAI allows one concurrent generation per account, so run examples
one at a time.
Development
The project uses uv for dependency management and ruff for linting and formatting:
uv sync # install dependencies (including the dev group)
uv run pytest # run the test suite
uv run ruff check . # lint
uv run ruff format . # format
Payload-shape tests compare generated request bodies against real payloads
captured from the NovelAI web client (examples/payloads/); please keep them
green when touching Metadata serialization.
License
Licensed under AGPL-3.0. Originally inspired by HanaokaYuzu/NovelAI-API and adopts a copyleft license accordingly.
References
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nekoai_api-0.5.0.tar.gz.
File metadata
- Download URL: nekoai_api-0.5.0.tar.gz
- Upload date:
- Size: 50.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4ea36e16af37572e8533ba6ed84a2c7de9df721e70a1c30b59b1545eb7b86f89
|
|
| MD5 |
3295470c5c2735f98a5dbc49f6ce829e
|
|
| BLAKE2b-256 |
1c786bafaa7b805de80834163da7254b1c4d04cd1dad45da5858728a6505fa9d
|
Provenance
The following attestation bundles were made for nekoai_api-0.5.0.tar.gz:
Publisher:
publish.yml on Nya-Foundation/NekoAI-API
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nekoai_api-0.5.0.tar.gz -
Subject digest:
4ea36e16af37572e8533ba6ed84a2c7de9df721e70a1c30b59b1545eb7b86f89 - Sigstore transparency entry: 2191786022
- Sigstore integration time:
-
Permalink:
Nya-Foundation/NekoAI-API@a8e6e94aacf44e643d8a73c0665e22b0ab5d0674 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Nya-Foundation
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a8e6e94aacf44e643d8a73c0665e22b0ab5d0674 -
Trigger Event:
push
-
Statement type:
File details
Details for the file nekoai_api-0.5.0-py3-none-any.whl.
File metadata
- Download URL: nekoai_api-0.5.0-py3-none-any.whl
- Upload date:
- Size: 51.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e6a3ccfd1b16e434ff8b80e07939e03dd07277dfba34fbc8116c0473ad5cbb5
|
|
| MD5 |
ea16c1892b044cd0efde3d6a8a8b6e60
|
|
| BLAKE2b-256 |
b141e830fc8b2736aa5a024617cccd4f2a2578b0b1d0a5490e01a33478248b7a
|
Provenance
The following attestation bundles were made for nekoai_api-0.5.0-py3-none-any.whl:
Publisher:
publish.yml on Nya-Foundation/NekoAI-API
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nekoai_api-0.5.0-py3-none-any.whl -
Subject digest:
2e6a3ccfd1b16e434ff8b80e07939e03dd07277dfba34fbc8116c0473ad5cbb5 - Sigstore transparency entry: 2191786041
- Sigstore integration time:
-
Permalink:
Nya-Foundation/NekoAI-API@a8e6e94aacf44e643d8a73c0665e22b0ab5d0674 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Nya-Foundation
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a8e6e94aacf44e643d8a73c0665e22b0ab5d0674 -
Trigger Event:
push
-
Statement type: