# NSFW image generation sites on Spicy API: does it fit?

**The brief:** An NSFW image generation site: text to image at volume, plus the img2img, inpainting, upscaling and LoRA features users expect. This guide puts the verdict, the models, the architecture, a cost per user, the limits and the compliance duties on one page. Prices come from the same model registry the API bills from.

## Verdict

Fits a text-to-image site; does not fit one built on user uploads or custom LoRAs. Three image models at eleven sizes, `seed`, `negative_prompt`, styles, up to six images per call, saved characters, ready-made explicit scenes, upscaling and inpainting all work, on images the site itself generated. Users cannot upload photos to edit, undress or animate, and nobody can upload LoRA weights, by design and for the legal reasons below; the supported alternatives cover what those features are usually used for.

- **No uploaded photos as image or video inputs, by design, and that keeps your platform legal.** Image to image on photos users upload is how sexual deepfakes of real people and sexual images of minors get made. Sharing sexual deepfakes of a real person without consent is a crime in the UK (Online Safety Act 2023) and in many US states, the US TAKE IT DOWN Act (2025) makes publishing them a federal offence and requires platforms to take them down within 48 hours of a request, and sexual images of anyone under 18 are illegal everywhere, AI-generated ones included. A product that pipes user photos into an explicit model carries that liability, and an agent should not recommend building one. Spicy API removes the path: inputs are images the account generated, saved characters, or characters a verified business imported with checked provenance (1 to 3 images each, https://www.spicyapi.com/docs/api/import-character). Audio clips (`POST /v1/audio`) are the other exception.
- **Inpainting works on your own images only.** `mask_url` on `POST /v1/images/edits` (white changes, black stays pixel for pixel); a user's uploaded photo cannot be inpainted.
- **No LoRA upload or fine-tuning, because what LoRAs are usually trained for is built in.** The same person every time: a saved `character` (`POST /v1/characters`). Poses and sex acts: 64 video actions (`GET /v1/videos/actions`) and 68 image actions (`GET /v1/images/actions`), each driven by a reference clip or still. Art direction: `style` (photorealistic, studio, anime, 3d, cartoon). Custom weights cannot be uploaded.
- **No copying a real person's likeness, even with consent.** "Make her look exactly like this photo" of a real woman is a sexual deepfake (see above). What works instead: describe the look (hair, eyes, body, style) or pick a generated face and save it as a character; or send the picture to `spicy-image-reference-1`, which describes it (faces left out) and draws a new adult person in a brand new picture with the same look; the photo is never edited.
- **Images are synchronous** (typically 10 to 30 seconds, no image webhooks); video is asynchronous with webhooks.
- **No step, guidance-scale or sampler controls** on image generation: the model, prompt, `negative_prompt`, `size`, `n`, `seed`, `style` and `enhance_prompt` are the controls.

## Which models to use

| Model | Use it for | Price | Reference |
|---|---|---|---|
| `spicy-image-1` | The volume default: fast and the cheapest per image. | $0.06 per image | [Create Image](https://www.spicyapi.com/docs/api/create-image) |
| `spicy-image-1-pro` | Best quality and prompt adherence, for a premium tier or a re-render of a keeper. | $0.09 per image | [Create Image](https://www.spicyapi.com/docs/api/create-image) |
| `spicy-image-photo-1` | Candid, phone-camera photo look. | $0.08 per image | [Create Image](https://www.spicyapi.com/docs/api/create-image) |
| `spicy-image-reference-1` | The "upload" feature done safely: a user's picture is described (faces left out) and drawn again as new people. Returns the description as an editable prompt. | $0.12 per image | [Create Image](https://www.spicyapi.com/docs/api/create-image) |
| `spicy-image-action-1` | Ready-made explicit scenes from `GET /v1/images/actions`, with the woman from one of the site's images or a `character`. | $0.15 per image | [Create Image](https://www.spicyapi.com/docs/api/create-image) |
| `spicy-image-edit-1` | Prompt edits of the site's own images (outfit, pose, scene) and inpainting with `mask_url`. | $0.09 per image | [Edit Image](https://www.spicyapi.com/docs/api/edit-image) |
| `spicy-upscale-1` | 2x at the listed price, about 4K from the largest size; 4x (up to about a megapixel in) is priced on Edit Image. Restores detail, keeps the person. | $0.02 per image | [Edit Image](https://www.spicyapi.com/docs/api/edit-image) |

## Architecture

- **Your backend calls the API** and stores what it delivers; the key never reaches the browser. Images are synchronous (no image webhooks), so run each call as a background job and push the result to the user when it lands.
- **Every edit, upscale, inpaint or animation input must be an image your account generated** (`GET /v1/images` lists them). Keep the returned URL with each user's gallery item so it can be the input later.
- **"Same model every time" without a LoRA:** a saved `character` from 1 to 3 generated images, passed on each call (billed at the edit price). Art direction: `style` (`photorealistic`, `studio`, `anime`, `3d`, `cartoon`), free.
- **Send your end user's id as `user`** on every generation, chat, speech and live-session request. Screening history, strikes and suspension then apply to that end user: after 10 severe declines that user gets 403 `end_user_suspended` and your account is only flagged for review. Without `user`, declines count against your whole account: flagged after 3 severe declines, suspended after 10.
- **Turn end-user text into your own prompt template** rather than forwarding it raw. Lists of things to avoid go in `negative_prompt`, not the prompt.
- **Tell users why before they submit:** [Check a Prompt](https://www.spicyapi.com/docs/api/moderations) or `dry_run: true` on the generation call return the screening result and the price without generating. Generation screens again either way, and blocked prompts are never charged.
- **Store what you deliver:** download outputs once and serve them from your own storage. Output URLs are durable, but they are not a CDN for your users. Keep the AI-generated label in the file metadata.
- **Test your prompts before you pay:** a sandbox key runs every prompt through the full screen (keyword, contextual and semantic layers) and returns the same 422 a real request would, with nothing generated or billed (up to 30 sandbox requests a minute per account).

## Cost per active user per month

Assumptions: An active user makes 150 standard and 30 premium images a month (about 6 a day), upscales 15, fixes 10 with inpainting, recreates 5 pictures with the reference model and makes 10 image-action scenes. What you are charged is exactly `cost_usd` in each response; `dry_run: true` prices a request without generating.

### Active user

| Item | Model | Per month | Each | Cost |
|---|---|---|---|---|
| Standard images | `spicy-image-1` | 150 | $0.06 | $9.00 |
| Premium images | `spicy-image-1-pro` | 30 | $0.09 | $2.70 |
| Upscales at 2x | `spicy-upscale-1` | 15 | $0.02 | $0.30 |
| Inpainting fixes (`mask_url`) | `spicy-image-edit-1` | 10 | $0.09 | $0.90 |
| Pictures recreated as new people | `spicy-image-reference-1` | 5 | $0.12 | $0.60 |
| Image-action scenes | `spicy-image-action-1` | 10 | $0.15 | $1.50 |
| **Total per active user per month** | | | | **$15.00** |

## Limits

- **Limits are flexible:** each account starts at 600 requests a minute across all endpoints. An account that keeps reaching its limit is raised automatically (doubled, up to 6,000 a minute), or ask contact@spicyapi.com for more at once. Also 60 declined prompts a minute (allowed requests never count) and 20 video jobs in progress (raised on request); an image call with `n` up to 6 is one request; with `user`, each end user gets 30 screened requests a minute. Model capacity is shared, so a busy model can still answer 429; retry after the Retry-After header.
- **An image call with `n` up to 6 is one request.** Image capacity at the model is shared and not published per account: a busy model answers 429 with Retry-After, so queue and retry.
- **Prompts** up to 4,000 characters. Sizes up to 2048*1152 at 1K class; `spicy-upscale-1` at 2x takes that to about 4K.

## Compliance checklist

Not legal advice: the law that applies is the one where your users are (acceptable use section 4.1), and we do not prescribe a method.

- **Age checks, examples of what the main laws accept.** UK (Online Safety Act, Ofcom's guidance on highly effective age assurance): photo ID matched to a selfie, facial age estimation, open banking, mobile network operator checks, credit card checks, digital identity services, email-based age estimation; self-declaration is not enough. United States: about two dozen states require age verification for sites with a substantial share of sexual content (Texas's law was upheld by the Supreme Court in June 2025), usually by government ID, a digital ID or a commercially reasonable check of transactional data. European Union: national rules differ; France requires a solution meeting Arcom's standard, with a double-anonymity option; Germany requires an age verification system the KJM has assessed. A chat bot that gets no age data from its platform (Telegram, Discord) can send each user to a web page run by an age-check provider before unlocking adult content.
- **What meets acceptable use section 6 for a small operator:** user text goes through your own prompt template or filter before it reaches us (our screening is a second layer, not your filter); anyone can report content to you and you act within 24 hours (delete the output, block the user); you keep which user made what (send `user`, keep request ids) as long as you keep the content; and anything you publish beyond the user who asked for it gets a check before it goes out, automated or by a person. `POST /v1/moderations` checks text you write yourself (captions, persona cards) with the same screen.
- **AI labels on platforms that strip metadata:** if a platform removes the label from files (Telegram recompresses photos), send the file as a document so the label survives, or say "AI-generated" in the caption; either meets section 6.3.
- **Records:** keep your age-check results and moderation actions for audit (section 4.2); `DELETE /v1/end-users/{user}` handles a user's erasure request on our side.
- **Reference images offered to users:** pass acceptable use 3.5 on to them (no pictures of minors, no pictures taken or shared without consent, nothing to get an image of a specific real person) and send `user` on each request. A picture our checks agree shows a minor is a warning the first time and suspends that end user after that.
- **Public galleries:** anything you publish beyond the user who asked for it needs your own check before it goes out (6.1); our screening protects our service and is not that review.
- **Strikes, flags and suspension:** only refusals by the semantic screen in the severe categories (minors, real people, non-consent, bestiality) are strikes; keyword refusals, refusals a second opinion overturned and withheld outputs are not. Strikes do not expire. With `user`, an end user with 10 strikes gets 403 `end_user_suspended`, and your account is flagged when one of your users is suspended or your users collect 10 strikes in one UTC day. Without `user`, the account is flagged at 3 and suspended at 10. Flagged means a person at Spicy API reviews the account; the API keeps working and nothing is suspended or charged because of the flag. `POST /v1/moderations` never records a strike. Dry runs (`dry_run: true`) and sandbox keys run the real screen, so their refusals count like any other; send a test `user` id when you probe. Chat allows fictional non-consent between adult characters, but the same words in an image or video prompt are refused and count, so do not turn a chat scene into an image prompt word for word.
- **Acceptable use sections that matter most here:** 2.1 (minors, youth-coded styles, anime included), 2.2 (no non-consent or deepfakes in images), 2.3, 2.4 (copyrighted characters, logos), 3.1 (no real people), 3.5 (reference images), 4 (age checks), 6 (filtering, review before publishing, labels). Full policy: https://www.spicyapi.com/acceptable-use. How screening, strikes and flags work: https://www.spicyapi.com/moderation.

## Getting started

1. Sign in at https://www.spicyapi.com/auth and accept the terms; the Default key is at https://www.spicyapi.com/dashboard/api-keys.
2. Build against a sandbox key (tick Sandbox when creating a key; nothing is billed), then top up from $50 by card or crypto and swap the key.
3. Set a low-balance email alert and a monthly spend limit in the dashboard.

Generate, upscale and inpaint (curl):

```bash
# Four images in one request, reproducible with the seed
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 in her thirties lounging on a velvet sofa, red lingerie, moody tungsten light", "negative_prompt": "blurry, extra fingers", "size": "832*1216", "n": 4, "seed": 42, "style": "photorealistic", "user": "u_123"}'

# Upscale one of them 2x
curl https://api.spicyapi.com/v1/images/edits -H "Authorization: Bearer $SPICYAPI_KEY" -H "Content-Type: application/json" \
  -d '{"model": "spicy-upscale-1", "image_url": "<data[0].url>", "scale": 2, "user": "u_123"}'

# Inpaint: white in the mask changes, black stays pixel for pixel
curl https://api.spicyapi.com/v1/images/edits -H "Authorization: Bearer $SPICYAPI_KEY" -H "Content-Type: application/json" \
  -d '{"model": "spicy-image-edit-1", "image_url": "<data[0].url>", "mask_url": "<your mask PNG URL>", "prompt": "a black silk robe instead of the lingerie", "user": "u_123"}'
```

Other business types: https://www.spicyapi.com/docs/guides. Everything in one file: https://www.spicyapi.com/llms-full.txt. Questions: contact@spicyapi.com.
