NsfwAiApi/Guide
API LLM NSFW en Python : streaming, JSON et outils
Pour utiliser une API LLM NSFW en Python, vous avez uniquement besoin du package openai standard et de deux modifications : l'URL de base et la clé. Ce guide montre une première requête, le streaming, le mode JSON, l'appel d'outils et la gestion propre des limites de débit.

Configuration : installation et premier appel
Vous avez besoin d'un Python 3 à jour, du paquet openai (pip install openai) et d'une clé API. Les nouveaux comptes peuvent réclamer un crédit d'essai gratuit de 0,50 $ sur la page Clé API en un clic après s'être connecté ; aucun détail de paiement n'est requis. Stockez la clé dans une variable d'environnement, pas dans le code source.
Le service utilise le protocole OpenAI de chat completions : POST /v1/chat/completions pour la génération et GET /v1/models pour la liste des modèles. L'authentification se fait par un jeton Bearer, et l'ID du modèle est uncensored. Voici le script minimal fonctionnel :
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.nsfwaiapi.com/v1",
api_key=os.environ["NSFWAIAPI_KEY"],
)
resp = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "You are a fiction co-writer for adult readers. Write vivid, non-explicit scenes."},
{"role": "user", "content": "Open a noir story in a rainy harbor town, three sentences."},
],
temperature=0.8,
)
print(resp.choices[0].message.content)
print(resp.usage)
Si vous préférez vérifier la connexion depuis un shell d'abord, la même requête fonctionne avec curl :
curl https://api.nsfwaiapi.com/v1/chat/completions \
-H "Authorization: Bearer $NSFWAIAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "uncensored", "messages": [{"role": "user", "content": "Say hello in five words."}]}'
Le modèle est ajusté pour répondre sans refus de contenu pour une utilisation adulte légale. Cela affecte le ton de ce qu'il écrira, pas la forme de l'API, donc le reste de votre code ressemble à n'importe quelle autre intégration OpenAI. Conservez les prompts pour la fiction, le chat de personnages et d'autres projets adultes légaux, et assurez-vous que chaque personnage est un adulte.
Quelques habitudes aident dès le premier jour. Placez les instructions qui ne changent jamais dans le message système, et gardez le message utilisateur pour la partie qui change à chaque tour. Choisissez une température pour une tâche et ne la touchez pas pendant que vous comparez les prompts, car changer deux choses à la fois ne vous apprend rien. Journalisez l'ID de requête et les nombres de tokens depuis resp.usage à côté de chaque appel, afin que lorsque votre facture ou vos résultats vous surprennent, il y ait un historique à vérifier.
Réponses en streaming avec SSE
Définissez stream=True et le serveur envoie les tokens sous forme d'événements envoyés par le serveur. Pour les applications de chat, cela a une grande importance, car le lecteur voit les premiers mots rapidement au lieu d'attendre la réponse complète. Les totaux d'utilisation arrivent à la fin du flux, donc une requête en streaming peut toujours être facturée et journalisée avec précision.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.nsfwaiapi.com/v1",
api_key=os.environ["NSFWAIAPI_KEY"],
)
stream = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "Write a short scene: two rival smugglers share a lantern during a storm."}],
stream=True,
)
text = ""
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
piece = chunk.choices[0].delta.content
text += piece
print(piece, end="", flush=True)
usage = getattr(chunk, "usage", None)
if usage:
print("\n[usage]", usage)
Deux détails font gagner du temps de débogage. Premièrement, le dernier fragment peut avoir une liste choices vide, c'est pourquoi la boucle la vérifie avant l'indexation. Deuxièmement, une requête en streaming occupe sa place de concurrence jusqu'à la fin du flux, donc si votre code abandonne un flux sans le fermer, cette place reste occupée. Utilisez with client.chat.completions.create(..., stream=True) as stream: ou appelez stream.close() lorsque vous arrêtez de lire plus tôt.
Mode JSON pour les sorties structurées
Lorsque votre programme a besoin de données et non de prose, comme une fiche de personnage, une liste de moments clés d'une scène ou des tags pour une histoire, utilisez le mode JSON avec response_format={"type": "json_object"}. Dites aussi « JSON » dans le prompt et décrivez les clés que vous souhaitez. Le paramètre demande un objet JSON, mais il ne peut pas promettre que vos clés soient présentes, donc vérifiez-les.
import json
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.nsfwaiapi.com/v1",
api_key=os.environ["NSFWAIAPI_KEY"],
)
resp = client.chat.completions.create(
model="uncensored",
response_format={"type": "json_object"},
messages=[
{"role": "system", "content": "Reply with a JSON object with keys: name, age, goal, secret. All characters are adults."},
{"role": "user", "content": "Create a ship's cook for a pirate adventure."},
],
temperature=0.7,
)
sheet = json.loads(resp.choices[0].message.content)
missing = [k for k in ("name", "age", "goal", "secret") if k not in sheet]
if missing:
raise ValueError(f"model skipped keys: {missing}")
print(sheet["name"], "-", sheet["goal"])
Si une longue réponse est coupée par max_tokens, le JSON sera incomplet et json.loads échouera. Laissez max_tokens non défini, car il est facultatif et la complétion peut utiliser le reste de la fenêtre, ou définissez-le de manière généreuse.
Appel de fonctions
L'appel de fonctions permet au modèle de demander à votre code de faire quelque chose, comme lancer des dés, consulter une note de personnage ou récupérer un prix. Vous passez tools et, si vous le souhaitez, tool_choice. La réponse contient tool_calls ; vous les exécutez et renvoyez les résultats en tant que messages avec role: "tool". Les réponses en streaming contiennent aussi tool_calls dans les deltas.
import json
import os
import random
from openai import OpenAI
client = OpenAI(
base_url="https://api.nsfwaiapi.com/v1",
api_key=os.environ["NSFWAIAPI_KEY"],
)
tools = [{
"type": "function",
"function": {
"name": "roll_die",
"description": "Roll one die with the given number of sides",
"parameters": {
"type": "object",
"properties": {"sides": {"type": "integer", "minimum": 2}},
"required": ["sides"],
},
},
}]
messages = [
{"role": "system", "content": "You run a tabletop fiction game. Use roll_die for any chance-based outcome."},
{"role": "user", "content": "I try to pick the lock on the captain's cabin. Roll a d20."},
]
first = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools, tool_choice="auto"
)
msg = first.choices[0].message
if msg.tool_calls:
messages.append(msg)
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = random.randint(1, args["sides"])
messages.append({"role": "tool", "tool_call_id": call.id, "content": str(result)})
final = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools
)
print(final.choices[0].message.content)
else:
print(msg.content)
Traitez les arguments comme des entrées non fiables. Validez-les avant d'agir dessus, exactement comme vous le feriez pour un formulaire web. Les modèles envoient parfois un champ manquant ou un type incorrect, et une petite protection autour de json.loads empêche un seul appel défectueux de mettre fin à votre session.
Limites de débit, tentatives et solde
Les limites protègent le service et vous offrent un comportement prévisible. Une clé complète permet 8 requêtes simultanées et 300 requêtes par minute. Une clé d'essai permet 2 et 60. La taille maximale du corps est de 8 MB. Deux types d'erreur de limite reviennent sous forme d'erreur HTTP 429 : le code rate_limited signifie trop de requêtes par minute, et concurrency signifie trop en même temps. Une surcharge temporaire renvoie également 429, avec un en-tête Retry-After: 5.
Le SDK OpenAI tente de nouveau quelques erreurs par défaut. Pour plus de contrôle, désactivez cela et écrivez une petite boucle qui respecte Retry-After :
import os
import time
from openai import OpenAI, RateLimitError
client = OpenAI(
base_url="https://api.nsfwaiapi.com/v1",
api_key=os.environ["NSFWAIAPI_KEY"],
max_retries=0,
)
def ask(messages, tries=5):
for attempt in range(tries):
try:
raw = client.chat.completions.with_raw_response.create(
model="uncensored", messages=messages
)
print("balance:", raw.headers.get("X-Balance-USD"), "request id:", raw.headers.get("X-Request-Id"))
return raw.parse()
except RateLimitError as err:
wait = float(err.response.headers.get("Retry-After", 2 ** attempt))
time.sleep(wait)
raise RuntimeError("still rate limited after retries")
reply = ask([{"role": "user", "content": "Describe a market at dawn in two sentences."}])
print(reply.choices[0].message.content)
Chaque réponse contient X-Request-Id, que vous citez si vous contactez le support, et X-Balance-USD, qui affiche votre crédit restant. Les en-têtes X-RateLimit-Limit-Requests et X-RateLimit-Limit-Concurrency vous indiquent quelles limites s'appliquent à votre clé. Pour exécuter de nombreux travaux en même temps, limitez le parallélisme dans votre propre code avec un sémaphore de taille adaptée à votre clé, 8 pour une clé complète et 2 pour une clé d'essai, afin que vous voyiez rarement une erreur 429.
Exécution de nombreuses requêtes en parallèle
Les tâches par lots, comme la génération de tags pour quelques centaines de fragments d'histoire, sont là où les limites de concurrence comptent. Une clé complète accepte 8 requêtes à la fois, donc exécutez le travail via un client asynchrone et un sémaphore de cette taille. Si vous avez une clé d'essai, utilisez 2.
import asyncio
import os
from openai import AsyncOpenAI
client = AsyncOpenAI(
base_url="https://api.nsfwaiapi.com/v1",
api_key=os.environ["NSFWAIAPI_KEY"],
)
gate = asyncio.Semaphore(8)
async def summarize(fragment):
async with gate:
resp = await client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "Summarize the passage in one sentence."},
{"role": "user", "content": fragment},
],
temperature=0.3,
)
return resp.choices[0].message.content
async def main(fragments):
return await asyncio.gather(*(summarize(f) for f in fragments))
results = asyncio.run(main(["The tide came in early.", "Marta hid the ledger under the floor."]))
print(results)
Le sémaphore est toute l'astuce. Sans lui, une liste de 200 fragments déclenche 200 requêtes ensemble, et la plupart reviennent avec des erreurs 429 avec le code concurrency. Avec lui, le travail s'écoule à la vitesse que votre clé permet et rien n'est gaspillé. Si vous gérez également un budget par minute, 300 requêtes par minute sur une clé complète représente cinq par seconde, donc des requêtes très courtes peuvent atteindre la limite par minute avant la limite de concurrence.
Problèmes courants et solutions
- 401 ou erreurs d'authentification. La clé est manquante, tronquée ou envoyée au mauvais hôte. Imprimez
client.base_urlet vérifiez la variable d'environnement. L'en-tête doit lireAuthorization: Bearersuivi de votre clé. - Modèle non trouvé. Le seul ID de modèle est
uncensored. Un exemple copié qui indique un autre nom échouera, donc appelezGET /v1/modelssi vous n'êtes pas sûr. - Réponses vides ou courtes dans une boucle d'histoire. Vérifiez que vous ajoutez la réponse de l'assistant à la liste des messages avant le prochain tour. Sans cela, le modèle ne voit que votre dernier message.
- Réponses qui se répètent. Augmentez
frequency_penaltyun peu, par exemple à 0,3, ou réduiseztemperaturesi le texte dévie. Les pénalités,top_p,stopetseedsont toutes transmises. - Sortie différente à chaque exécution. Définissez
seedet unetemperaturefaible lorsque vous avez besoin de résultats aussi reproductibles que possible pendant le débogage. - Requête trop volumineuse. La limite du corps est de 8 MB, et le prompt plus la complétion doivent tenir dans 100 000 tokens. Coupez ou résumez l'ancien contexte avant qu'il n'atteigne l'une ou l'autre limite.
Lorsque quelque chose semble incorrect côté serveur, enregistrez l'en-tête X-Request-Id de la réponse et envoyez-le au contact de la page support avec une courte description. Cela permet aux opérateurs de trouver votre requête exacte.
Coût et limitations
La tarification est de $0,15 par 1 M de tokens d'entrée et de $0,50 par 1 M de tokens de sortie, depuis un crédit prépayé sans abonnement. Les requêtes échouées ou refusées ne sont pas facturées, et le crédit n'expire jamais. À titre d'exemple, un script qui envoie 3 000 tokens et reçoit 800 paie $0,00045 pour l'entrée plus $0,0004 pour la sortie, ce qui fait $0,00085 par appel, donc 1 000 appels coûtent $0,85. Les recharges vont de $20 à $500, avec des bonus de +5 % à partir de $50, +10 % à partir de $100 et +15 % à partir de $200. Ce site accepte uniquement la crypto, en USDT sur TRC20 ou USDC sur Base. Consultez la page de tarification pour le tableau complet, et la documentation pour la référence de l'endpoint.
Les limites sont claires. C'est un seul modèle à poids ouverts, exécuté par un fournisseur indépendant, et non l'API officielle d'aucun fournisseur de modèle. La fenêtre de contexte est de 100 000 tokens pour le prompt plus la complétion. Il ne génère pas d'images, d'audio ou de vidéo, ne fournit pas d'embeddings et ne prend pas en charge le fine-tuning. Utilisez-le pour une utilisation adulte légale uniquement, et notez la limite stricte : pas de contenu sexuel impliquant des mineurs. Les prompts ne sont pas utilisés pour l'entraînement.
Plus de guides
Questions et réponses
Ai-je besoin d'un SDK spécial pour appeler cette API depuis Python ?
Non. Le package openai officiel fonctionne. Définissez base_url sur https://api.nsfwaiapi.com/v1, passez votre clé et utilisez l'ID de modèle uncensored.
Le streaming fonctionne-t-il de la même manière qu'avec OpenAI ?
Oui. Passez stream=True et lisez les événements envoyés par le serveur (SSE) morceau par morceau. L'utilisation est rapportée à la fin du flux.
Que faire en cas d'erreur 429 ?
Attendez et réessayez. Si la réponse contient un en-tête Retry-After, respectez-le. Le code d'erreur rate_limited signifie trop de requêtes par minute, et concurrency signifie trop de requêtes simultanées.
Puis-je le tester sans payer ?
Oui. Après l'inscription, vous pouvez réclamer $0,50 de crédit d'essai sur la page Clé API. Il ne nécessite aucun détail de paiement, est valable 7 jours et est unique par personne.
Il ne vous reste qu'un formulaire à remplir pour obtenir votre clé
Créez un compte, copiez la clé, modifiez l'URL de base. C'est toute la configuration.
Obtenir clé APINsfwAiApiAPI LLM sans censure pour la génération de texte NSFW