tokenstore

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/json

Create 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
done

Or 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

Task creation reserves the price. If a task fails, the reservation is refunded automatically — you are never charged for a render that produced no video. An insufficient balance at create time returns 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
Automating NSFW Video Generation with the API — Nureta Guides