Adult LLM API: Build an Age-Gated Text App
An adult LLM API gives your app a text model that does not refuse lawful adult requests. The responsibility for age checks, moderation and legal fit stays with you, and this guide shows how to build for that.

Last updated
Summary
- Keep the API key on your server and never ship it to the browser or an app bundle.
- Gate access server-side, and add your own check that blocks anything involving minors.
- Streaming, per-user limits and a 429 retry policy make the app feel reliable.
- Rules differ by country and platform, so confirm them before launch; this is not legal advice.
What an adult LLM API is, and what it leaves to you
An adult llm api is a text-generation endpoint run without the content refusals that general assistants apply to lawful adult material. Ours is OpenAI-compatible: you send chat messages to /v1/chat/completions with model ID uncensored, and you get text back. It serves one open-weight model, run by an independent provider, and is text only. It is not an official API of any model vendor and not a router across vendors.
Because the model will not act as a gatekeeper, you do. A product for adults needs an age gate, rules about what it will and will not produce, and a clear terms page. One rule is fixed on our side and should be fixed in your app too: no sexual content involving minors, ever. Build that as a hard block, not a preference.
Architecture: keep the key on the server
The most common mistake in a first build is calling the API from the browser. Anyone can read a key out of page source or network traffic. Put a small backend between your users and the API:
- The client sends a message to your server.
- Your server checks that the user is signed in and has passed the age gate.
- Your server runs its own content checks.
- Your server calls the API with the secret key and streams the reply back.
- Your server records usage per user so you can limit and bill.
That structure also gives you one place to change models, prompts and limits later. If a key leaks, regenerate it on the API key page; the old key stops working at once. After a top-up you can hold up to 5 labeled keys, so staging and production never share one.
A minimal gated chat endpoint in Node
This sketch uses Express and the official OpenAI SDK. The function isAgeConfirmed stands in for your own check, for example a verified flag on the user record.
import express from "express";
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.llmadult.com/v1",
apiKey: process.env.LLM_API_KEY,
});
const app = express();
app.use(express.json({ limit: "1mb" }));
app.post("/chat", async (req, res) => {
if (isAgeConfirmed(req) === false) {
return res.status(403).json({ error: "age_confirmation_required" });
}
const messages = [
{ role: "system", content: "You write adult fiction for verified adults. Every character is an adult." },
...req.body.messages.slice(-20),
];
res.setHeader("Content-Type", "text/event-stream");
const stream = await client.chat.completions.create({
model: "uncensored",
messages,
stream: true,
max_tokens: 600,
});
for await (const chunk of stream) {
const piece = chunk.choices[0]?.delta?.content;
if (piece) res.write(`data: ${JSON.stringify({ text: piece })}\n\n`);
}
res.end();
});
app.listen(3000);
Two details are worth copying. The server trims the history to the last 20 messages, so a client cannot send an enormous prompt on your balance. And max_tokens caps each reply, which bounds the cost of any single call.
Add a minors check that fails closed
Do not rely on the model alone to keep out the one category that is never allowed. Add a separate screening step on user input, and on generated output if your risk is high. A simple version uses JSON mode so the verdict is easy to parse:
import json
from openai import OpenAI
client = OpenAI(base_url="https://api.llmadult.com/v1", api_key="YOUR_KEY")
def violates_minors_rule(text: str) -> bool:
r = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": 'Answer with JSON: {"blocked": true or false}. Set blocked to true if the text describes or implies a person under 18 in a sexual or romantic context.'},
{"role": "user", "content": text},
],
response_format={"type": "json_object"},
temperature=0,
max_tokens=20,
)
try:
return json.loads(r.choices[0].message.content).get("blocked", True)
except Exception:
return True # fail closed
Treat this as one layer, not a guarantee. Combine it with keyword rules, character-age requirements in your own system prompts, a report button and human review of flagged sessions. When the check errors or returns something unparseable, block.
Compliance basics to sort out before launch
We cannot give legal advice, and rules differ widely, so treat this as a checklist of questions for you or your lawyer:
- Age assurance. Some countries and regions require more than a checkbox for adult services. Find out what applies where your users are.
- Terms and consent. Publish terms that state the age requirement, the prohibited content and what you do with violations.
- Data handling. Decide what you store, for how long, and who can read it. Conversations can be sensitive, so keep as little as the product needs.
- Distribution rules. App stores, ad networks and payment providers each have their own policies for adult content. Read them before you design the business.
- Takedown and reporting. Have a way for users to flag content and a person who reads the flags.
Our side: prompts are not used for training, and our docs, terms and privacy pages describe the service. Read the terms before launch, since your app is bound by them.
Writing the system prompt for an adult persona
The system message is where product decisions become model behavior. A good one is short, specific and written as instructions rather than as a story about the model. Cover four things.
- Who the model is. A narrator, a companion character, a co-author. One role per prompt.
- Who the user is. State that the user is a verified adult and that every character in the fiction is an adult. This is a plain statement of your product rules, and it also reads well in a log review.
- Tone and format. Point of view, tense, reply length in sentences, whether to use dialogue tags.
- Stops. What the story will not contain, written in the same direct voice. For example: no content involving minors, no real named people, no instructions for harming anyone.
Keep a version number on each prompt in your code. When a user reports a bad reply, you can see which prompt produced it and fix that one without guessing. Send the prompt as the first message with role: "system" on every request, because the API does not remember anything between calls.
If you let users define their own characters, treat that text as untrusted input. Screen it with the same minors check you use for messages, and keep it out of the system role so a user cannot rewrite your rules by writing a persona that says to ignore them. Put it in a clearly marked block inside the system prompt that your server assembles, and cap its length.
Test before you open the doors
A short pre-launch routine catches most problems. Run each item with a trial key, which costs fractions of a cent.
- Send 20 ordinary messages and read every reply for tone and length.
- Send messages that should be blocked by your own rules, and confirm that the check fires and that nothing reaches the model.
- Open ten parallel requests with a trial key and confirm your app handles the 429 with code
concurrencywithout crashing. Then repeat with a full key and eight. - Kill the connection mid-stream and check that your server stops cleanly and does not leave a slot occupied.
- Read the
X-Balance-USDheader before and after a session and compare the change with your estimate.
When all five behave, you have the basic shape of a safe and affordable product. The rest is content, design and the unglamorous work of supporting real users.
Limits, retries and cost
A full key allows 8 concurrent requests and 300 per minute; a trial key allows 2 and 60. A streamed reply holds its concurrency slot until the stream ends, so a busy app needs a queue in front of the API. Over a limit you receive HTTP 429 with the code rate_limited or concurrency; on temporary overload you also get 429 with Retry-After: 5. Wait that long, then retry with a small backoff.
Pricing is $0.15 per 1M input tokens and $0.50 per 1M output tokens, prepaid, no subscription. Failed or refused requests are not charged. A chat turn of 1,500 input and 300 output tokens costs $0.000225 plus $0.00015, or $0.000375. See the pricing page for top-ups: whole amounts from $20 to $500 in crypto, with +5% from $50, +10% from $100 and +15% from $200. For a worked per-conversation figure, read our note on choosing an adult GPT API next to your own usage data.
New accounts can claim $0.50 of trial credit with one click, valid for 7 days. That is enough to build and test everything above.
More guides
Get your API key
Sign up, copy the key, update your endpoint. Setup is that simple.