Automating NSFW Video Generation with the API
The full Nureta API loop for adult video at volume: authenticate, create a task, poll or receive a signed callback, handle rate limits and automatic refunds.
2 min read
Everything in the playground is a thin layer over one REST endpoint. If you are generating adult video at any volume — a content pipeline, a bot, a batch job — drive the API directly. The loop is small: authenticate, create a task, then either poll it or receive a webhook when it finishes.
This guide is the whole loop end to end, with the rate limits and refund behavior you need to run it unattended.
Authenticate
Create a key on the API keys page and pass it as a bearer token. The base URL is https://developer.nureta.ai; the examples use a $TOKENSTORE_URL shell variable. Generations bill your USDC balance (on Base, 1 USDC = $1.00) at posted prices; an empty balance returns AccountOverdueError.
export TOKENSTORE_URL="https://developer.nureta.ai"
# Every request carries:
# Authorization: Bearer sk-...
# Content-Type: application/jsonCreate a task
POST to the generation endpoint. It reserves the price and returns a task id. Every field is documented under create a generation task.
curl -s -X POST "$TOKENSTORE_URL/api/v3/contents/generations/tasks" \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d '{
"model": "seahorse-720p",
"duration": 8,
"content": [{ "type": "text", "text": "a nude couple, cowgirl position, slow grinding, warm light" }]
}'
# => {"id": "cgt-..."}Poll for the result
Poll the task until status is succeeded or failed. On success the response carries content.video_url; on failure, an error and an automatic refund. Video tasks also carry a step object so you can show real progress while rendering.
TASK=cgt-...
while :; do
RES=$(curl -s "$TOKENSTORE_URL/api/v3/contents/generations/tasks/$TASK" \
-H "Authorization: Bearer sk-...")
STATUS=$(echo "$RES" | jq -r .status)
[ "$STATUS" = "succeeded" ] && echo "$RES" | jq -r .content.video_url && break
[ "$STATUS" = "failed" ] && echo "failed (refunded)" && break
sleep 3
doneOr use a callback
Polling is simple but wasteful at volume. Pass a callback_url on task creation and Nureta POSTs the finished task object to it once — no polling. Add a callback_secret (8–256 characters) and each delivery is signed so you can trust it.
curl -s -X POST "$TOKENSTORE_URL/api/v3/contents/generations/tasks" \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d '{
"model": "seahorse-720p",
"duration": 8,
"content": [{ "type": "text", "text": "a nude couple, missionary, slow" }],
"callback_url": "https://example.com/webhooks/nureta",
"callback_secret": "a-shared-secret-8-to-256-chars"
}'Verify the signature: compute an HMAC-SHA256 over timestamp + "." + rawBody with your secret and compare it constant-time against the hex in the X-Tokenstore-Signature header. Delivery is at-least-once — deduplicate by task id. Full details are in the callback webhook docs.
const crypto = require("crypto");
const expected = crypto
.createHmac("sha256", CALLBACK_SECRET)
.update(timestamp + "." + rawBody)
.digest("hex");
const ok = crypto.timingSafeEqual(
Buffer.from("sha256=" + expected),
Buffer.from(signatureHeader),
);Rate limits and batching
Every request against a key — create, poll, upload — counts toward its rate limit. New keys default to 10 requests/second, adjustable between 1 and 100 on the keys page. Over the limit returns 429 RateLimitExceeded with standard RateLimit headers; back off and retry. Upload presigning has its own fixed cap of 30 per minute.
- Prefer callbacks over polling to keep request volume down.
- If you must poll, back off between polls — every poll is a request against the limit.
- Batch task creation, but stay under your per-second ceiling; raise the key's limit if you need more headroom.
Refunds and failures
AccountOverdueError before any work starts, so a broke key fails fast instead of half-charging.Where to start
New to the endpoint? Walk the single-clip path first in how to create an NSFW video from a prompt, then wrap it in the loop above to run it unattended.
Read the generation API docs →