Skip to main content

Build with nocensor.ai

REST API for image generation, video, face swap, enhance, and multi-stage pipelines. Scoped API key auth, async job polling, webhooks — same credit system as the UI.

API key auth

Bearer token with per-key rate limits. Max 5 keys per account.

Async jobs

202 Accepted + polling. No blocking requests. Signed URLs when complete.

Same credit system

Credits deducted on submission. Failed jobs auto-refunded by cron.

Prompt safety

Same content filter as the UI. CSAM blocked at all tiers.

Authentication

All endpoints (except /api/v1/health) require a Bearer token:

Authorization: Bearer nc_live_<your_key>

API keys are generated in Settings → API Keys. Keys are shown once at creation — store them securely.

Each key carries one or more scopes. Using a key on an endpoint outside its scope returns 403 INSUFFICIENT_SCOPE.

ScopeGrants access to
generationPOST /generate, /video, /face-swap, /enhance, /pipelines, /undress, /loras, /loras/{id}/train, /voice-calls
mgmtGET /jobs, /jobs/{id}, /account, /credits, /payments, /models, /characters, /pipelines/{id}; DELETE /jobs/{id}
webhooksAll /webhooks/* endpoints
affiliateGET /affiliate/summary, /affiliate/daily, /affiliate/clicks, /affiliate/signups, /affiliate/sales (your own affiliate stats, as JSON or CSV)

API Reference

Full OpenAPI 3.1 spec available at /openapi.yaml.

Generation

POST/api/v1/generate

Generate an image

POST/api/v1/video

Generate a video

POST/api/v1/undress

AI undress

POST/api/v1/face-swap

Swap faces in an image

POST/api/v1/enhance

Enhance / upscale an image

Pipelines

POST/api/v1/pipelines

Submit a multi-stage image pipeline

Add `?dry_run=true` to get a cost breakdown (200) without submitting the job (202).

GET/api/v1/pipelines/{id}

Get pipeline status

Jobs

GET/api/v1/jobs

List generation jobs

GET/api/v1/jobs/{id}

Get job status / result

DELETE/api/v1/jobs/{id}

Cancel a pending job

GET/api/v1/jobs/{id}/events

Stream live job events (Server-Sent Events)

Events: `snapshot` (job detail) on connect; `status`; `progress` `{step,total,percent,stage:{key,label}}` (best-effort); terminal `completed` | `failed` | `cancelled` then close; `reconnect` at ~55s. Resume with `Last-Event-ID`. Authenticate with a Bearer key, or with `?token=` from `events_url` (browser-safe, 10 min, one job).

Webhooks

GET/api/v1/webhooks

List webhooks

POST/api/v1/webhooks

Create a webhook

The `secret` field is only returned at creation time.

GET/api/v1/webhooks/{id}

Get webhook details

PATCH/api/v1/webhooks/{id}

Update a webhook

DELETE/api/v1/webhooks/{id}

Delete a webhook

GET/api/v1/webhooks/{id}/deliveries

List webhook delivery attempts

POST/api/v1/webhooks/{id}/test

Send a test delivery

Characters

POST/api/v1/voice-calls

Open a voice call with a character

Finds or creates your companion session with a character and returns `call_url`, the link that opens the live voice call. The call runs in the browser of the account that owns the API key, which must be signed in to nocensor.ai. It is billed per minute from that account (`credit_per_minute`) and needs at least `min_credits_to_start` credits. This endpoint charges nothing itself. Phone-number bridging is not available yet.

GET/api/v1/loras

List your trained characters

Returns every character (LoRA) on your account, newest first, with the `id` that generation requests take in `loras[]` and that `POST /api/v1/loras/{id}/train` and `POST /api/v1/loras/{id}/download` take in the path. Only `ready` characters can generate or be downloaded; `pending` drafts can still be trained and `failed` ones report why training did not complete through their job. Deleted characters are not listed. Free; nothing is charged.

POST/api/v1/loras

Create a character training draft and get photo upload URLs

Step 1 of character (LoRA) training. Creates a pending character and returns one presigned S3 URL per photo. PUT each photo to its `url` with exactly the declared `size` in bytes and the given `Content-Type` header, and NO `Authorization` header, within `expires_in` seconds. Then call `POST /api/v1/loras/{id}/train`. Repeating this call with the same `name` re-arms the same untrained draft with fresh URLs. Nothing is charged here.

POST/api/v1/loras/{id}/train

Start training a character from its uploaded photos

Step 2 of character training. Verifies every photo from step 1 finished uploading, charges the tier chosen at creation, and queues training. The body must be empty or `{}` — name and tier are fixed at creation. Track it with `GET /api/v1/jobs/{id}`; once the job completes, use the character id in `loras[]` on generation requests. Training a character again after it was accepted returns `LORA_NOT_TRAINABLE`, never a second charge.

POST/api/v1/loras/{id}/download

Download your trained character file (.safetensors)

Returns a signed download URL, valid for `expires_in_seconds`, for one of YOUR ready characters, plus the trigger word to add to prompts and the base model it was trained on. Every call must attest `attest_real_person_consent: true` (the character is your own likeness, or an adult who consented to it) and is recorded against your account. The file inherits the licence of the base model it was trained on (`license_url`): it binds you and anyone you share the file with. When that licence requires a notice to travel with the file (Ultra characters, Krea 2 Community License), `license_notice` carries its text: keep it beside the file as NOTICE.txt; otherwise it is null. Free; nothing is charged. Characters trained before downloads existed return `LORA_NOT_EXPORTABLE`.

Account

GET/api/v1/account

Get account details

GET/api/v1/credits

Get credit balance

GET/api/v1/payments

List payment history

GET/api/v1/characters

List available characters

GET/api/v1/models

List available models

Affiliate stats

GET/api/v1/affiliate/summary

Affiliate totals and balances

Lifetime and last-30-day clicks, signups, sales and commission, plus commission balances by status. Equals the sum of `/daily` rows for the same window. Requires an API key with the `affiliate` scope. Dates are UTC. `format=csv` returns the same data as CSV; errors are always JSON.

GET/api/v1/affiliate/daily

Affiliate totals per UTC day

One row per UTC day (and per placement tag with `group_by=day_sub`). Sales are bucketed by sale date; commission status is as of the read. Click columns are null before `meta.clicks_tracked_since`. Requires an API key with the `affiliate` scope. Dates are UTC. `format=csv` returns the same data as CSV; errors are always JSON.

GET/api/v1/affiliate/clicks

Affiliate link presses

Every press of your link, newest first, with placement tag, utm values, your `click_id` and a bot flag. `counted=false` presses repeat a visitor within the dedupe window. Window max 92 days. Requires an API key with the `affiliate` scope. Dates are UTC. `format=csv` returns the same data as CSV; errors are always JSON.

GET/api/v1/affiliate/signups

Referred signups

One row per referred account, by UTC signup date. No buyer identity and no time of day. Requires an API key with the `affiliate` scope. Dates are UTC. `format=csv` returns the same data as CSV; errors are always JSON.

GET/api/v1/affiliate/sales

Referred sales and commissions

One row per commission, by UTC sale date, with gross, rate, commission, status and payout. No buyer identity and no time of day. Requires an API key with the `affiliate` scope. Dates are UTC. `format=csv` returns the same data as CSV; errors are always JSON.

System

GET/api/v1/health

Health check

Quick start

Use the official TypeScript SDK (@nocensor/sdk) for end-to-end type safety, retries, and polling — or hit the REST API directly with curl.

SDK (Node, browsers, Deno, Bun, Workers)

npm install @nocensor/sdk

import { NoCensor } from '@nocensor/sdk'

const nc = new NoCensor({ apiKey: process.env.NOCENSOR_API_KEY })

const job = await nc.generate.createAndWait({
  prompt: 'beautiful woman, photorealistic',
  model: 'realistic',
})

console.log(job.outputs[0].url)

1. Submit a job with curl (202 Accepted)

curl -X POST https://nocensor.ai/api/v1/generate \
  -H "Authorization: Bearer nc_live_<your_key>" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "beautiful woman, photorealistic", "model": "realistic"}'

# 202 Accepted
# {
#   "data": { "id": "job_...", "status": "pending" },
#   "meta": { "poll_url": "/api/v1/jobs/job_...", "credits_remaining": 490 }
# }

2. Poll until complete

curl https://nocensor.ai/api/v1/jobs/<job_id> \
  -H "Authorization: Bearer nc_live_<your_key>"

# 200 OK — when done:
# {
#   "data": {
#     "id": "job_...", "status": "completed",
#     "outputs": [{ "url": "https://...", "media_type": "image", "dimensions": { "width": 832, "height": 1216 }, "expires_at": "..." }]
#   }
# }

Rate limits

LimitValueScope
Generation10 rpmPOST /generate, /video, /face-swap, /enhance, /pipelines, /undress, /loras, /loras/{id}/train
Management60 rpmGET /jobs, /jobs/{id}, /account, /credits, /payments, /models, /characters, /pipelines/{id}; DELETE /jobs/{id}; POST /voice-calls
Webhooks10 rpmAll /webhooks/* endpoints
Active keys5 maxPer account

All responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. Rate-limited responses include a Retry-After header.

Error codes

CodeHTTPTrigger
UNAUTHORIZED401Missing, invalid, or revoked API key
INVALID_EVENTS_TOKEN401/jobs/{id}/events `token` is invalid, expired, or issued for another job
FORBIDDEN403Account suspended or key missing required scope
INSUFFICIENT_SCOPE403API key lacks the required scope for this endpoint
NOT_AN_AFFILIATE403Affiliate stats endpoint called with a key whose account has no affiliate profile
AFFILIATE_SUSPENDED403Affiliate stats endpoint called for a suspended affiliate account
INSUFFICIENT_CREDITS402Not enough credits for the workflow, or below the voice-call minimum on /voice-calls
PURCHASE_REQUIRED402Endpoint requires a prior real purchase (e.g. /undress)
VIDEO_REQUIRES_PAID_TIER402/video — paid tier required; route the user to the /credits upsell page
RATE_LIMITED429Per-key or per-user rate limit exceeded
VALIDATION_ERROR422Request failed schema validation — `details[]` lists every failing field
INVALID_REQUEST400Malformed or unparseable JSON body
INVALID_IDEMPOTENCY_KEY400Idempotency-Key is not 1-255 printable ASCII characters
INVALID_INPUT400Invalid or missing request field
INVALID_WORKFLOW400Unknown model or workflow id in the request
UNKNOWN_WORKFLOW400Workflow id not registered at dispatch time
PROMPT_BLOCKED400Content policy filter triggered
PROMPT_REJECTED400Content policy filter triggered (enhance operations)
CONTENT_POLICY_VIOLATION400Source image rejected by the image safety check
PAYLOAD_TOO_LARGE413Image exceeds 3.5MB
SOURCE_IMAGE_UNREACHABLE422/video i2v image URL could not be fetched — a nocensor output URL expires about an hour after it is issued, so re-read it from GET /api/v1/jobs/{id} or send inline base64
NOT_FOUND404Job does not exist or not owned by caller
SOURCE_NOT_FOUND404job_<uuid> source reference not owned by caller or has no output
PIPELINE_NOT_FOUND404Pipeline correlation_id has no jobs owned by caller
LORA_NOT_FOUND404LoRA id in `loras[]` not owned by caller, or a /loras/{id}/train or /voice-calls id that is not your character
JOB_NOT_CANCELLABLE409DELETE on a job already completed, failed, or cancelled
JOB_FAILED200/jobs/{id}/events `failed` event data.code — the job itself failed, not the stream
IDEMPOTENCY_KEY_REUSED409Idempotency-Key reused with a different request body
IDEMPOTENCY_IN_PROGRESS409A request with this Idempotency-Key is still running — retry after Retry-After
LORA_NOT_READY409LoRA is soft-deleted, still training, failed, or missing output
LORA_LIMIT_REACHED409POST /loras when the account already holds the maximum number of characters
LORA_NOT_TRAINABLE409POST /loras/{id}/train on a character already submitted, training, ready, or failed
LORA_UPLOAD_INCOMPLETE409POST /loras/{id}/train before every photo finished uploading — the draft is retired; create it again
LORA_NOT_EXPORTABLE409POST /loras/{id}/download on a character trained before downloads existed — retrain it to export
ATTESTATION_REQUIRED400POST /loras/{id}/download without the per-export real-person attestation (a missing or false attest_real_person_consent body field returns 422)
LORA_NOT_OWNED403LoRA id in a pipeline stage not owned by caller
LORA_INCOMPATIBLE400LoRA base model doesn't match the target model/endpoint
CHARACTER_INCOMPATIBLE400characterId is not available on the requested model (no charge)
TOO_MANY_LORAS400More loras[] than the model allows (max 2 for images, max 1 for video)
PIPELINE_TOO_MANY_STAGES400Pipeline exceeds the 5-stage max
PIPELINE_INVALID_STAGE_ORDER400Image stage placed after a video-producing stage
CHARACTER_LOCKED403Character has not been unlocked by this account
REGION_BLOCKED451Feature unavailable in the caller's region (e.g. AI undress in banned US states, or restricted countries)
WEBHOOK_URL_INVALID400Webhook URL failed validation
WEBHOOK_LIMIT_REACHED409Maximum webhooks per account reached
WEBHOOK_INACTIVE409Webhook is disabled — reactivate before test or deliveries
GPU_UNAVAILABLE503All GPU workers throttled — retry shortly
IDEMPOTENCY_UNAVAILABLE503Idempotency check temporarily unavailable — retry
AUTH_UNAVAILABLE503API key check could not complete — your key was not judged; retry after the Retry-After header (never rotate the key for this)
TIMEOUT503Dispatch timed out while the service was busy — retry
AUDIO_UNAVAILABLE503/video with sound or /voice-calls while the voice provider is down — retry later (for /video, or set audioMode to "none")
FEATURE_UNAVAILABLE503Endpoint or model restricted to admin keys (v1/undress, video model wan-21) — use the alternative named in the message
STORAGE_ERROR500Transient storage failure signing a source URL — retry
DB_ERROR500Transient database error — retry
INTERNAL_ERROR500Unexpected server error — retry

Ready to build?

Create an account, get your API key, and start generating.

Get started free