Getting started
Quick Start
On this page
Install a client, create your API key, and generate an image and a video. Each example waits for the job and downloads the finished file; choose the language you use.

Install a client #
Choose one install; every tab is one command. The examples below use the published packages, and each tab needs only the tool it names. The Python package, nolgia, needs Python 3.10 or later.
npm install @nolgia/sdk
yarn add @nolgia/sdk
pnpm add @nolgia/sdk
bun add @nolgia/sdk
pip install nolgia
uv add nolgia
cargo add nolgia
brew install nolgiainc/nolgia/nolgia
curl -fsSL https://raw.githubusercontent.com/nolgiainc/nolgia-cli/main/install.sh | bash
curl https://api.nolgia.ai/v1/me -H "Authorization: Bearer $NOLGIA_TOKEN"
Get your key #
Create a Personal Access Token at nolgia.ai/settings/api-tokens, copy it once, and set it in your environment; it starts with nol_. The CLI can instead open the browser device flow with nolgia auth login; see Get your API key.
export NOLGIA_TOKEN=nol_...
nolgia auth login
Your first image #
Generate a paper-cut mountain range with flux-3-image and save it as first.png. The curl tab shows each call in turn: submit, wait on the returned id, then download asset.signed_url from the finished job. Every SDK tab downloads the file itself (in Rust, nolgia.download(url, path)), so nothing here needs a package its install tab did not name.
curl -sS https://api.nolgia.ai/v1/generate/image \
-H "Authorization: Bearer $NOLGIA_TOKEN" \
-H "Content-Type: application/json" \
-d '{"model":"flux-3-image","prompt":"a paper-cut mountain range at dawn"}'
curl -sS "https://api.nolgia.ai/v1/jobs/<id from the response>/wait?timeout_seconds=120" \
-H "Authorization: Bearer $NOLGIA_TOKEN"
curl -sS -o first.png "<asset.signed_url from the finished job>"
nolgia gen image --model flux-3-image --prompt "a paper-cut mountain range at dawn" --out first.png
import { writeFile } from "node:fs/promises";
import { createNolgiaClient } from "@nolgia/sdk";
const nolgia = createNolgiaClient(process.env.NOLGIA_TOKEN!);
async function finish(id: string, timeout: number) {
for (;;) {
const { data, response } = await nolgia.GET("/jobs/{id}/wait", {
params: { path: { id }, query: { timeout_seconds: timeout } },
});
if (response.status === 408) continue; // the wait window closed; the job is still running
if (data?.status === "succeeded" && data.asset) return data.asset;
throw new Error(`job ${id} ended ${data?.status ?? response.status}`);
}
}
async function download(url: string, file: string) {
await writeFile(file, Buffer.from(await (await fetch(url)).arrayBuffer()));
}
const { data: image, error } = await nolgia.POST("/generate/image", {
body: { model: "flux-3-image", prompt: "a paper-cut mountain range at dawn", num_images: 1 },
});
if (error) throw new Error(`${error.title}: ${error.detail ?? ""}`);
await download((await finish(image.id, 120)).signed_url, "first.png");
console.log("image job", image.id, "-> first.png");
import os
import httpx
from nolgia import AuthenticatedClient
from nolgia.api.generate import generate_image, generate_video
from nolgia.api.jobs import wait_for_job
from nolgia.models import GenerateImageRequest, GenerateVideoRequest, ImageModel, Job, VideoModel
client = AuthenticatedClient(base_url="https://api.nolgia.ai/v1", token=os.environ["NOLGIA_TOKEN"])
def finish(job_id, timeout):
while True:
response = wait_for_job.sync_detailed(job_id, client=client, timeout_seconds=timeout)
if response.status_code == 408: # the wait window closed while the job was still running
continue
done = response.parsed
if isinstance(done, Job) and done.status == "succeeded":
return done.asset
raise SystemExit(f"job {job_id} {getattr(done, 'status', done)}")
def download(url, path):
with open(path, "wb") as f:
f.write(httpx.get(url).content)
job = generate_image.sync(client=client, body=GenerateImageRequest(model=ImageModel("flux-3-image"), prompt="a paper-cut mountain range at dawn"))
if not isinstance(job, Job):
raise SystemExit(f"refused: {job}")
download(finish(job.id, 120).signed_url, "first.png")
print("image job", job.id, "-> first.png")
use nolgia::tokio;
#[tokio::main]
async fn main() -> nolgia::Result<()> {
let nolgia = nolgia::Client::from_env()?;
let image = nolgia
.subscribe(nolgia::image("flux-3-image", "a paper-cut mountain range at dawn"))
.await?;
nolgia.download(image.url().expect("an image"), "first.png").await?;
println!("image job {} -> first.png", image.job.id);
Ok(())
}
Your first video #
This step requires a Pro plan or higher: veo-3.1-lite is not available on Starter.
Generate four seconds at 720p with sound using veo-3.1-lite; append the TypeScript and Python code to the program above, or insert the Rust block before Ok(()) in main.
curl -sS https://api.nolgia.ai/v1/generate/video \
-H "Authorization: Bearer $NOLGIA_TOKEN" \
-H "Content-Type: application/json" \
-d '{"model":"veo-3.1-lite","prompt":"a paper-cut mountain range at dawn, slow push-in as the sun rises","duration_seconds":4}'
curl -sS "https://api.nolgia.ai/v1/jobs/<id from the response>/wait?timeout_seconds=300" \
-H "Authorization: Bearer $NOLGIA_TOKEN"
curl -sS -o first.mp4 "<asset.signed_url from the finished job>"
nolgia gen video --model veo-3.1-lite --prompt "a paper-cut mountain range at dawn, slow push-in as the sun rises" --duration-seconds 4 --out first.mp4
const { data: video, error: videoError } = await nolgia.POST("/generate/video", {
body: { model: "veo-3.1-lite", prompt: "a paper-cut mountain range at dawn, slow push-in as the sun rises", duration_seconds: 4 },
});
if (videoError) throw new Error(`${videoError.title}: ${videoError.detail ?? ""}`);
await download((await finish(video.id, 300)).signed_url, "first.mp4");
console.log("video job", video.id, "-> first.mp4");
job = generate_video.sync(client=client, body=GenerateVideoRequest(model=VideoModel("veo-3.1-lite"), prompt="a paper-cut mountain range at dawn, slow push-in as the sun rises", duration_seconds=4))
if not isinstance(job, Job):
raise SystemExit(f"refused: {job}")
download(finish(job.id, 300).signed_url, "first.mp4")
print("video job", job.id, "-> first.mp4")
let video = nolgia
.subscribe(
nolgia::video("veo-3.1-lite", "a paper-cut mountain range at dawn, slow push-in as the sun rises")
.set("duration_seconds", 4),
)
.await?;
nolgia.download(video.url().expect("a video"), "first.mp4").await?;
println!("video job {} -> first.mp4", video.job.id);
Wait, then download #
A job moves from queued to running, then reaches succeeded, failed or canceled. GET /jobs/{id}/wait holds the connection up to timeout_seconds (maximum 900) and answers 408 when the window closes while the job is still running. Download from asset.signed_url, a time-limited URL.
| Parameter | In | Required | Description |
|---|---|---|---|
id |
path | Yes | Job UUID. |
timeout_seconds |
query | No |
What it costs #
| Model id | Modality | Plan | Credits |
|---|---|---|---|
flux-3-image |
image | starter | 3 per image |
veo-3.1-lite |
video | pro | 14 per clip (5 s) |
POST /jobs/cost quotes the exact credits for your settings before you submit (a four-second veo-3.1-lite clip quoted 12 credits on 2026-09-20; the table shows the per-clip baseline); see Billing.

