Uncensored Chatbot APIDirect API access to one uncensored LLMGet API key

Uncensored Chatbot APISafety

Running an Adult Chatbot Product Responsibly: A Builder's Checklist

An adult chatbot is a real product, and real products carry responsibilities. The API gives you a model that does not refuse lawful adult content, which means your app, not the model, is where age checks, house rules, reporting and abuse controls live. This checklist walks through each layer with working code, so you can launch with confidence.

Updated

Key points

  • The platform is for adults only; verify age at your door and keep the check server-side.
  • Write your own content policy and a report path, because the model's openness does not replace your rules.
  • Sexual content involving minors is always blocked with a 403; design a clear, calm UX for it.
  • Limit usage per user and keep the API key on your server; one key is shared by all your traffic.

Who is responsible for what

Think of the stack in two layers. The API serves lawful adult content, fiction and controversial topics without refusing, for users aged 18 and over. Everything around the model, the people who can reach it, the rules they agree to and what happens when something goes wrong, is yours. That split is freeing, but only if you take your half seriously.

Here is a short responsibilities table to keep on the wall.

ConcernPlatformYour app
Sexual content involving minorsAlways blocked (403)Explain it, end the scene, prevent workarounds
Age of your usersAdults only by termsGate and verify at your entrance
House rules beyond the lawNot appliedWrite, publish and enforce them
Per-user abuse300 requests per minute per keyPer-user and per-day limits
Secret keyIssues itKeep it on your server

This is engineering guidance, not legal advice; rules differ by place, so check the requirements where your users live.

Age gating that actually gates

A banner with an "I am 18" button is the minimum, and a determined minor can click it. It still matters, because it records an affirmative statement and keeps your policy honest. The key is where you enforce it. If the check only hides the chat in the browser, anyone can call your endpoint directly. Make the server refuse to talk until a signed marker exists.

import crypto from "node:crypto";

// Minimal 18+ gate: the user must confirm before the chat routes work.
// Replace the confirmation with a real verification step when your market requires it.
const SECRET = process.env.GATE_SECRET;   // random string kept on the server

function sign(value) {
  return crypto.createHmac("sha256", SECRET).update(value).digest("hex");
}

export function issueAdultCookie(res, userId) {
  const stamp = String(Date.now());
  const payload = `${userId}.${stamp}`;
  res.cookie("adult_ok", `${payload}.${sign(payload)}`, {
    httpOnly: true, sameSite: "strict", secure: true, maxAge: 30 * 24 * 3600 * 1000,
  });
}

export function requireAdult(req, res, next) {
  const raw = req.cookies?.adult_ok || "";
  const [userId, stamp, sig] = raw.split(".");
  if (!sig || sign(`${userId}.${stamp}`) !== sig) {
    return res.status(403).json({ state: "age_required" });
  }
  req.userId = userId;
  next();
}

Wire requireAdult in front of every chat route. Add stronger verification when your market or risk level calls for it, such as a third-party age check, and keep the marker short-lived enough that you can re-prompt. Never target students or schools in your marketing, and place the gate before any adult content is shown, including previews and sample conversations.

Writing your own content policy

The model does not refuse lawful adult material, so you decide what your product allows. A one-page policy is plenty. Cover what is welcome, what is off-limits in your community even if lawful (for example harassment of real people or doxxing), how users can report problems, and what actions you take: warnings, temporary mutes, bans.

  1. Publish it where users sign up, in plain language.
  2. Mirror it in your system prompt. A line such as "All characters are adults; end any scene that becomes non-consensual or involves real, named people" keeps the bot aligned with your rules.
  3. Make enforcement boring. Decide thresholds in advance so moderators do not improvise.
  4. Review the policy every quarter as your product evolves.

A reporting path users can find

Give every message a small report control, and make it work without a login detour. When someone reports a reply, capture the minimum you need to act: the user id, the message id, a short reason and a timestamp. Route it into a queue your moderators watch.

app.post("/api/report", requireAdult, express.json(), (req, res) => {
  const { messageId, reason } = req.body;
  // Store only what you need to act on: who, which message, why, when.
  queueForModerator({ userId: req.userId, messageId, reason: String(reason).slice(0, 500), at: Date.now() });
  res.json({ ok: true });
});

Keep your promises narrow. Tell users exactly what a report triggers, and only store what your own policy and applicable rules require. Review how long you keep reports, and delete them on a schedule. Acknowledge each report to the person who filed it, even with a one-line thank-you, because users stop reporting when nothing seems to happen. Track how quickly your moderators close items, and set a target you can actually meet.

Designing for the 403 minors block

Sexual content involving minors is always blocked, in fiction and roleplay too, and the API answers with a 403 and the code content_blocked. Do not retry it and do not try to rephrase on the user's behalf. Instead, treat it as a product moment that deserves a clear design.

  • Translate it on the server into a state your UI knows, so raw upstream text never reaches the screen.
  • Say what happened in one calm sentence and name the rule.
  • End or reset the scene. Offering "continue anyway" defeats the point.
  • Log the event with the user id so repeat attempts can trigger your own enforcement.
  • Do not guess at intent. Some blocks come from ambiguous wording; the neutral message works for both cases.
// server: translate upstream outcomes into states your UI understands
if (upstream.status === 403) {
  return res.status(403).json({
    state: "content_blocked",
    message: "That request breaks our rules, so we can't continue this scene.",
  });
}
if (upstream.status === 402) return res.status(503).json({ state: "paused" });
if (upstream.status === 503) return res.status(503).json({ state: "busy" });
// browser: show a calm, specific message for each state
const MESSAGES = {
  content_blocked: "This scene can't continue. Sexual content involving minors is never allowed. Start a new chat any time.",
  age_required: "Please confirm you are 18 or older to use the chat.",
  slow_down: "You're sending messages quickly. Give it a few seconds.",
  daily_limit: "You've reached today's message allowance. Come back tomorrow.",
  paused: "Chat is briefly unavailable. We're on it.",
  busy: "The service is busy. Try again in a moment.",
};

async function send(messages) {
  const res = await fetch("/api/chat", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ messages }),
  });
  if (!res.ok) {
    const info = await res.json().catch(() => ({}));
    showBanner(MESSAGES[info.state] || "Something went wrong.");
    if (info.state === "content_blocked") endCurrentScene();
    return null;
  }
  return res;
}

Per-user abuse and rate controls

Your single API key allows 300 requests per minute in total, shared by all users. One scripted account can starve everyone else, so enforce fair-use limits yourself, well below that ceiling. The example caps each user at 12 messages a minute and gives each a daily token budget measured from the usage figure in the final stream chunk.

const WINDOW_MS = 60_000;
const PER_USER_PER_MIN = 12;       // your product rule, far below the key's 300
const DAILY_TOKEN_BUDGET = 60_000; // per user, tracked from the usage chunk

const hits = new Map();            // userId -> array of timestamps
const spent = new Map();           // userId -> tokens used today

export function userLimit(req, res, next) {
  const now = Date.now();
  const recent = (hits.get(req.userId) || []).filter((t) => now - t < WINDOW_MS);
  if (recent.length >= PER_USER_PER_MIN) {
    return res.status(429).json({ state: "slow_down" });
  }
  if ((spent.get(req.userId) || 0) >= DAILY_TOKEN_BUDGET) {
    return res.status(429).json({ state: "daily_limit" });
  }
  recent.push(now);
  hits.set(req.userId, recent);
  next();
}

export function recordUsage(userId, totalTokens) {
  spent.set(userId, (spent.get(userId) || 0) + totalTokens);
}

For production, store counters in a shared store instead of process memory so limits survive restarts and work across several servers. Add a modest max_tokens on every request to bound the worst case, and watch for 402 on your own balance. A free trial of $0.50 for 7 days is a nice way to test these controls before real traffic arrives.

Keep the key on the server

Never ship the key in front-end bundles, mobile apps or public repositories. Store it as an environment variable or in a secret manager, load it only in your backend and exclude it from logs and error reports. If you suspect exposure, regenerate it from your account; the old key stops working immediately, so have a deploy ready. Remember there is one key per account, which makes a clean proxy layer even more valuable. The web chat tutorial shows the pattern end to end, and the docs list the error codes your wrapper should map.

Pre-launch checklist

Run through this list the day before you open the doors. Each item takes minutes and prevents a class of problems that is painful to fix after users arrive.

  1. Try to break the gate. Call your chat route with curl and no cookie. It must answer 403 with age_required, never a model reply.
  2. Trigger every state. Stub the upstream to return 402, 403, 429 and 503 in turn, and confirm each shows the right banner and never leaks raw error text.
  3. Read your own policy aloud. If a moderator cannot decide a case from it in a minute, tighten the wording.
  4. Test the report button from a phone screen. A control that is hard to reach does not count as a reporting path.
  5. Search your repository for the key. Look in front-end folders, build output, sample configs and commit history. Regenerate it if you find it anywhere public.
  6. Set alerts. Page yourself on a 402, on a spike of 403 blocks from a single user and on a jump in daily token use.
  7. Prepare the pause switch. A single flag that turns chat off gracefully is invaluable during an incident.

Safety is never finished, and neither is the list; add items every time an incident teaches you something new, and share them with whoever joins your team next. Revisit the list when you add features such as image uploads in your own UI, group rooms or public character sharing, because each one opens new paths for abuse. For help shaping the characters themselves, read the persona design guide, and keep the basics of your integration notes in the docs handy as your team grows.

Questions and answers

Is a checkbox enough for age verification?

It is a minimum and records a statement, but it is easy to bypass. Enforce it on the server and add stronger verification where your risk or local rules call for it.

What should my app do on a 403 content_blocked?

Do not retry. Translate it into a calm in-app message, end or reset the scene and log the event for your own enforcement.

Can I let users share my API key's capacity fairly?

Yes, by adding per-user rate and daily token limits on your server. The key itself is limited to 300 requests per minute in total.

Does the model enforce my house rules?

No. It does not refuse lawful adult content, so your policy, system prompt and moderation flow have to do that work.

Your key is one form away

Create an account, copy the key, change the base URL. That is the whole setup.

Get API keyRead the docs