V1 Documentation
Documentation

SpicyAPI V1

One REST API for uncensored image, video and chat generation. No GPUs to rent, no queues to run — you send JSON, you get media back. Every request is billed per generation from your account balance, with no subscription.

The base URL for all endpoints is https://api.spicyapi.com.

curl https://api.spicyapi.com/v1/images/generations \
  -H "Authorization: Bearer $SPICYAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "spicy-image-1",
    "prompt": "a woman on a beach at golden hour, photorealistic"
  }'

Models

MODELTYPEPRICENOTES
spicy-pov-missionary-1Video (i2v)from $0.175 / secOur POV missionary fine-tune
spicy-pov-doggystyle-1Video (i2v)from $0.175 / secOur POV doggystyle fine-tune
spicy-pov-blowjob-1Video (i2v)from $0.175 / secOur POV kneeling-oral fine-tune
spicy-image-1Image$0.06 / imageFast realistic text-to-image
spicy-image-1-proImage$0.09 / imageHighest quality, best prompt adherence
spicy-image-edit-1Image edit$0.15 / imageEdit with up to 3 reference images
spicy-motion-1Video (i2v)from $0.175 / secNSFW-tuned image-to-video with audio
spicy-motion-2Video (i2v)from $0.175 / secLatest gen, up to 1080p / 15s
spicy-video-1Video (t2v)from $0.175 / secText-to-video, no source image
spicy-motion-3Video (i2v / t2v)from $0.10 / secWan 3.0 class: up to 30s, 480p to 1080p, native audio, source image optional
spicy-motion-3-fastVideo (i2v / t2v)from $0.14 / secSpicy Motion 3 on the accelerated backbone, same limits, faster turnaround
spicy-chat-1Chat$0.58 / 1M tokensUncensored roleplay chat, OpenAI-compatible

Call GET /v1/models for the live catalog including per-model size, duration and resolution limits.

Reference

Authentication

Authenticate with a bearer token. Create keys in the dashboard — a key is shown once at creation and stored only as a hash, so save it somewhere safe.

Authorization: Bearer sk-spicy-xxxxxxxxxxxxxxxxxxxx

Never ship a key in client-side code. Check your balance any time with GET /v1/account.

Endpoint

Image generation

POST /v1/images/generations — synchronous. Returns image URLs directly, typically in 10–30 seconds.

curl https://api.spicyapi.com/v1/images/generations \
  -H "Authorization: Bearer $SPICYAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "spicy-image-1",
    "prompt": "your prompt",
    "negative_prompt": "blurry, low quality",
    "size": "832*1216",
    "n": 2
  }'

{
  "id": "sj_2f1c...",
  "object": "image.generation",
  "model": "spicy-image-1",
  "data": [{ "url": "https://cdn.spicyapi.com/outputs/..." }],
  "cost_usd": 0.05
}

Every returned url is permanently hosted on our CDN and registered to your account — these URLs are what you pass later as inputs to image editing and video generation.

Treat the URL as your master copy and provenance token, not as end-user hosting: download the file once and serve it to your own users from your own storage or CDN. Sustained hotlinking of output URLs into consumer-facing apps falls outside fair use and may be rate-limited.

Serving a copy does not affect reuse: keep the original URL in your database, and it remains valid forever as an input to /v1/images/edits and /v1/videos/generations — your users view your copy, your backend passes our URL.

Endpoint

Image editing

POST /v1/images/edits — edit or recompose an existing image from a prompt. Pass up to three reference images. Each must be an output previously generated by your own account — use the url values returned by /v1/images/generations or /v1/images/edits. External image URLs are rejected with a 400 — see Input images for why, and for the integration pattern.

curl https://api.spicyapi.com/v1/images/edits \
  -H "Authorization: Bearer $SPICYAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "spicy-image-edit-1",
    "prompt": "change the outfit to a red dress",
    "image_urls": ["https://cdn.spicyapi.com/outputs/…/sj_2f1c…-0.png"]
  }'
Policy

Input images: no uploads, by design

SpicyAPI does not accept uploaded or external images anywhere. The only images that can be edited or animated are images that were generated through the API by your own account. This is a safety and legal-compliance requirement, not a technical limitation: because every video frame and edit source traces back to a moderated, machine-generated origin, neither you nor we can be handed real-world photographs of real people — which keeps the API (and every product built on it) compliant with international law on non-consensual and abusive imagery.

Enforcement is server-side: each generated output URL is registered to the account that created it, and input URLs are checked against that registry before anything is charged. A URL that was not minted for your account — including another customer's output, a guessed CDN path, or a re-hosted copy — is rejected with a 400.

Integrating this into your product — the pattern is a gallery: your users first generate images, browse what they've made, then pick one to edit or animate.

1. When a generation completes, store the returned urlin your own database against the end-user who made it. Your API account's library is shared across your whole app, so this per-user mapping is yours to keep.

2. To build the gallery, use your stored URLs — or fetch the account-wide library from GET /v1/images, newest first:

curl "https://api.spicyapi.com/v1/images?limit=50" \
  -H "Authorization: Bearer $SPICYAPI_KEY"

{
  "object": "list",
  "data": [
    { "url": "https://cdn.spicyapi.com/outputs/…/sj_2f1c…-0.png",
      "source": "image", "created": 1755856800 }
  ]
}

3. When the user picks an image, pass its exact URL straight into /v1/images/edits or /v1/videos/generations:

// e.g. an Express handler behind your own user auth
app.post("/animate", async (req, res) => {
  // look the image up in YOUR db so users can only
  // animate images they generated themselves
  const image = await db.images.findOwn(req.user.id, req.body.imageId);

  const task = await fetch("https://api.spicyapi.com/v1/videos/generations", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.SPICYAPI_KEY}`,
               "Content-Type": "application/json" },
    body: JSON.stringify({
      model: "spicy-motion-2",
      prompt: req.body.prompt,
      image_url: image.url,   // exact URL from /v1/images/generations
      resolution: "720P",
      duration: 5,
    }),
  }).then(r => r.json());

  res.json({ taskId: task.id });
});

Pass the URL byte-for-byte as it was returned — adding query parameters or changing the encoding will fail the provenance check.

Endpoint

Video generation

POST /v1/videos/generations — asynchronous. Returns a task immediately; poll it or receive a webhook. Video generation typically takes 1–5 minutes.

image_url must be an image generated by your own account: first create the frame with /v1/images/generations (or /v1/images/edits), then pass the exact url it returned. Uploaded or external images are rejected with a 400 — see Input images.

curl https://api.spicyapi.com/v1/videos/generations \
  -H "Authorization: Bearer $SPICYAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "spicy-motion-2",
    "prompt": "she turns towards the camera and smiles",
    "image_url": "https://cdn.spicyapi.com/outputs/…/sj_2f1c…-0.png",
    "resolution": "720P",
    "duration": 5
  }'

{ "id": "sj_9a2b...", "status": "queued", "cost_usd": 0.125 }

Poll with GET /v1/videos/tasks/{id}. Status moves through queued processing succeeded or failed. Failed generations are refunded automatically.

{
  "id": "sj_9a2b...",
  "status": "succeeded",
  "output": { "video_url": "https://cdn.spicyapi.com/outputs/..." },
  "cost_usd": 0.125
}

As with images, download the finished video and serve it to your end users from your own infrastructure — the CDN URL is your master copy, not a streaming host for your traffic.

Flagship

POV fine-tunes

These are our own models — fine-tuned in-house on the wan2.7 backbone for specific POV scenes, and the reason most people use this API. They take the same request shape as any other video model, so switching is a one-word change.

MODELSCENEMOTION
spicy-pov-missionary-1POV missionarySteady thrust rhythm, locked overhead framing
spicy-pov-doggystyle-1POV doggystyleAccelerating rear-entry motion, camera fixed behind
spicy-pov-blowjob-1POV blowjob (kneeling)Building oral rhythm, camera looking down and still
spicy-pov-missionary-1
spicy-pov-doggystyle-1
spicy-pov-blowjob-1

Actual outputs from each fine-tune — this is what the model produces with a single API call, no prompt engineering.

They are priced identically to the general video models — $0.175/sec at 720p — and are image-to-video, so pass a first frame that is already in the position you want animated — generated by your account via /v1/images/generations, like any other input image.

curl https://api.spicyapi.com/v1/videos/generations \
  -H "Authorization: Bearer $SPICYAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "spicy-pov-missionary-1",
    "prompt": "amateur bedroom, phone flash lighting",
    "image_url": "https://cdn.spicyapi.com/outputs/…/sj_2f1c…-0.png",
    "resolution": "720P",
    "duration": 5
  }'

Each fine-tune has an activation token (returned as lora_trigger on GET /v1/models). You do not need to add it — we insert it for you when it is missing, so the tuned motion always engages. Include it yourself only if you want to control where in the prompt it sits.

Endpoint

Chat completions

POST /v1/chat/completions is OpenAI-compatible, including streaming. Point any OpenAI SDK at our base URL and change the model name.

from openai import OpenAI

client = OpenAI(
    api_key="sk-spicy-...",
    base_url="https://api.spicyapi.com/v1",
)

stream = client.chat.completions.create(
    model="spicy-chat-1",
    messages=[
        {"role": "system", "content": "You are Emma, a flirty girlfriend."},
        {"role": "user", "content": "hey, what are you up to?"},
    ],
    stream=True,
)
Reference

Webhooks

Set a webhook URL in your dashboard and we POST there when a video task reaches a terminal state — no polling required.

POST your-endpoint
X-SpicyAPI-Signature: <hex hmac-sha256 of the raw body>

{
  "event": "task.succeeded",
  "data": {
    "id": "sj_9a2b...",
    "status": "succeeded",
    "output": { "video_url": "https://cdn.spicyapi.com/outputs/..." }
  }
}

Verify the signature against your signing secret before trusting the payload. We retry on the next poll cycle if your endpoint is unreachable.

Reference

Errors & limits

Errors return a standard shape with an HTTP status: 401 bad key, 402 insufficient balance, 400 invalid parameters, 422 prompt blocked by moderation, 429 model at capacity.

{ "error": { "message": "...", "type": "insufficient_balance" } }

All prompts pass through content moderation before generation. Blocked prompts are never charged. See our acceptable use policy.