NsfwAiApi/Leitfaden
NSFW-LLM-API in Python: Streaming, JSON und Tools
Um eine NSFW-LLM-API im Python-Stil zu nutzen, benötigst du nur das Standard-openai-Paket und zwei Änderungen: die Base URL und den Schlüssel. Dieser Leitfaden zeigt eine erste Anfrage, Streaming, JSON-Modus, Function Calling und sauberes Handling von Ratenlimits.

Einrichtung: Installation und erster Aufruf
Du benötigst ein aktuelles Python 3, das openai-Paket (pip install openai) und einen API-Schlüssel. Neue Konten können nach der Anmeldung mit einem Klick ein $0,50-Testguthaben auf der API-Schlüssel-Seite beanspruchen; es werden keine Zahlungsdaten benötigt. Speichere den Schlüssel in einer Umgebungsvariable, nicht im Quellcode.
Der Dienst spricht das OpenAI-Chat-Vervollständigungsprotokoll: POST /v1/chat/completions zur Generierung und GET /v1/models für die Modellliste. Die Authentifizierung erfolgt über ein Bearer-Token, und die Modell-ID ist uncensored. Dies ist das kleinste funktionierende Skript:
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)
Wenn du die Verbindung zuerst über eine Shell prüfen möchtest, funktioniert dieselbe Anfrage auch mit 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."}]}'
Das Modell ist darauf abgestimmt, bei der rechtmäßigen Nutzung für Erwachsene ohne Inhaltsablehnungen zu antworten. Das beeinflusst den Tonfall dessen, was es schreibt, nicht die Form der API, sodass der Rest deines Codes wie jede andere OpenAI-Integration aussieht. Halte Prompts für fiktionale Inhalte, Character-Chat und andere rechtmäßige Projekte für Erwachsene, und stelle sicher, dass jede Figur erwachsen ist.
Einige Gewohnheiten helfen von Anfang an. Lege die Anweisungen, die sich nie ändern, in die Systemnachricht, und behalte die Benutzernachricht für den Teil, der sich bei jedem Durchlauf ändert. Wähle eine Temperatur für eine Aufgabe und lasse sie in Ruhe, während du Prompts vergleichst, denn das Ändern von zwei Dingen gleichzeitig bringt nichts. Protokolliere die Request-ID und die Token-Anzahlen aus resp.usage neben jedem Aufruf, sodass bei einer Rechnung oder Ausgabe, die dich überrascht, ein Protokoll zur Überprüfung vorhanden ist.
Streaming-Antworten mit SSE
Setze stream=True, und der Server sendet Token als Server-Sent-Events. Für Chat-Apps ist dies von großer Bedeutung, da der Leser die ersten Wörter schnell sieht, anstatt auf die vollständige Antwort zu warten. Die Nutzungsgesamtmengen kommen am Ende des Streams an, sodass eine gestreamte Anfrage weiterhin genau abgerechnet und protokolliert werden kann.
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)
Zwei Details sparen Debugging-Zeit. Erstens kann der letzte Chunk eine leere choices-Liste haben, weshalb die Schleife dies vor dem Indizieren prüft. Zweitens hält eine gestreamte Anfrage ihren Concurrency-Slot bis zum Ende des Streams belegt, sodass dieser Slot beschäftigt bleibt, wenn dein Code einen Stream ohne Schließung verlässt. Verwende with client.chat.completions.create(..., stream=True) as stream: oder rufe stream.close() auf, wenn du frühzeitig aufhörst zu lesen.
JSON-Modus für strukturierte Ausgaben
Wenn dein Programm Daten und keinen Text benötigt, wie ein Charakterbogen, eine Liste von Handlungspunkten oder Tags für eine Geschichte, verwende den JSON-Modus mit response_format={"type": "json_object"}. Schreibe auch „JSON“ in den Prompt und beschreibe die gewünschten Schlüssel. Die Einstellung fordert ein JSON-Objekt an, kann aber nicht garantieren, dass deine Schlüssel vorhanden sind, also prüfe sie.
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"])
Wenn eine lange Antwort durch max_tokens abgeschnitten wird, ist das JSON unvollständig und json.loads schlägt fehl. Lass max_tokens ungesetzt, da es optional ist und die Vervollständigung den Rest des Fensters nutzen kann, oder setze es großzügig.
Function Calling
Tool-Aufrufe ermöglichen es dem Modell, deinen Code etwas tun zu lassen, wie Würfel zu werfen, eine Charakternotiz nachzuschlagen oder einen Preis abzurufen. Du übergibst tools und, wenn du möchtest, tool_choice. Die Antwort enthält tool_calls; du führst sie aus und sendest die Ergebnisse als Nachrichten mit role: "tool" zurück. Gestreamte Antworten enthalten auch in den Deltas tool_calls.
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)
Behandle die Argumente als nicht vertrauenswürdige Eingabe. Validiere sie, bevor du darauf reagierst, genau wie bei einem Webformular. Modelle senden manchmal ein fehlendes Feld oder einen falschen Typ, und eine kleine Schutzmaßnahme um json.loads verhindert, dass ein schlechter Aufruf deine Sitzung beendet.
Ratenlimits, Wiederholungen und dein Guthaben
Limits schützen den Service und sorgen für vorhersehbares Verhalten. Ein vollständiger Schlüssel erlaubt 8 parallele Anfragen und 300 Anfragen pro Minute. Ein Trial-Schlüssel erlaubt 2 parallele Anfragen und 60 Anfragen pro Minute. Der maximale Body beträgt 8 MB. Zwei Arten von Limit-Fehlern werden als HTTP 429 zurückgegeben: Der Code rate_limited bedeutet zu viele Anfragen pro Minute, und concurrency bedeutet zu viele gleichzeitig. Auch eine temporäre Überlastung gibt 429 zurück, mit einem Retry-After: 5-Header.
Das OpenAI SDK wiederholt einige Anfragen bei Fehlern standardmäßig. Für mehr Kontrolle schalte dies ab und schreibe eine kleine Schleife, die Retry-After respektiert:
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)
Jede Antwort enthält X-Request-Id, das du bei einer Kontaktaufnahme mit dem Support angibst, sowie X-Balance-USD, das dein verbleibendes Guthaben anzeigt. Die Header X-RateLimit-Limit-Requests und X-RateLimit-Limit-Concurrency zeigen dir, welche Limits für deinen Schlüssel gelten. Um viele Jobs parallel auszuführen, begrenze die Parallelität in deinem eigenen Code mit einem Semaphore, der auf die Größe deines Schlüssels ausgelegt ist, also 8 für einen vollständigen Schlüssel und 2 für einen Trial-Schlüssel, damit du selten überhaupt eine 429 siehst.
Viele parallele Anfragen ausführen
Batch-Jobs, wie das Generieren von Tags für einige hundert Story-Fragmente, sind der Bereich, in dem die Limits für parallele Anfragen relevant sind. Ein vollständiger Schlüssel akzeptiert 8 Anfragen gleichzeitig, führe die Arbeit also über einen asynchronen Client und ein Semaphore dieser Größe aus. Wenn du einen Trial-Schlüssel hast, verwende 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)
Das Semaphore ist der gesamte Trick. Ohne es sendet eine Liste von 200 Fragmenten 200 Anfragen gleichzeitig, und die meisten davon kommen als 429-Fehler mit dem Code concurrency zurück. Damit fließt die Arbeit mit der Geschwindigkeit, die dein Schlüssel erlaubt, und nichts geht verloren. Wenn du auch ein Budget pro Minute einhältst, ergeben 300 Anfragen pro Minute bei einem vollständigen Schlüssel fünf pro Sekunde, sodass sehr kurze Anfragen das Minutenlimit erreichen können, bevor das Limit für parallele Anfragen erreicht ist.
Häufige Probleme und Lösungen
- 401 oder Authentifizierungsfehler. Der Schlüssel fehlt, ist abgeschnitten oder wird an den falschen Host gesendet. Gib
client.base_urlaus und überprüfe die Umgebungsvariable. Der Header muss lautenAuthorization: Bearergefolgt von deinem Schlüssel. - Modell nicht gefunden. Die einzige Modell-ID ist
uncensored. Ein kopiertes Beispiel, das einen anderen Namen angibt, wird fehlschlagen, also rufeGET /v1/modelsauf, wenn du unsicher bist. - Leere oder kurze Antworten in einer Geschichtenschleife. Überprüfe, ob du die Assistant-Antwort vor der nächsten Runde an die Nachrichtenliste anhängst. Ohne sie sieht das Modell nur deine letzte Nachricht.
- Wiederholende Antworten. Erhöhe
frequency_penaltyetwas, zum Beispiel auf 0,3, oder senketemperature, wenn der Text abschweift. Die Parametertop_p,stopundseedwerden alle durchgegeben. - Unterschiedliche Ausgaben bei jedem Lauf. Setze
seedund eine niedrigetemperature, wenn du Ergebnisse benötigst, die so wiederholbar wie möglich sind, während du debuggst. - Anfrage zu groß. Die Body-Grenze beträgt 8 MB, und Prompt plus Vervollständigung müssen in 100.000 Token passen. Kürze oder fasse alten Kontext zusammen, bevor er eines der Limits erreicht.
Wenn auf Serverseite etwas falsch aussieht, speichere den X-Request-Id-Header aus der Antwort und sende ihn zusammen mit einer kurzen Beschreibung an den Kontakt auf der Support-Seite. Dies ermöglicht es den Operatoren, deine genaue Anfrage zu finden.
Was es kostet und was es nicht kann
Die Preise betragen $0,15 pro 1M Input-Token und $0,50 pro 1M Output-Token, vom Prepaid-Guthaben ohne Abonnement. Fehlgeschlagene oder abgelehnte Anfragen werden nicht berechnet, und Guthaben verfällt nie. Als Rechenbeispiel zahlt ein Skript, das 3.000 Token sendet und 800 empfängt, $0,00045 für Input plus $0,0004 für Output, was $0,00085 pro Aufruf ergibt, also kosten 1.000 Aufrufe $0,85. Aufladungen laufen von $20 bis $500, mit Boni von +5% ab $50, +10% ab $100 und +15% ab $200. Diese Website akzeptiert nur Krypto, in USDT auf TRC20 oder USDC auf Base. Siehe die Preisseite für die vollständige Tabelle und die Dokumentation für die Endpunkt-Referenz.
Die Grenzen sind klar. Es ist ein einzelnes Open-Weight-Modell, betrieben von einem unabhängigen Anbieter, und nicht die offizielle API eines Modell-Anbieters. Das Kontextfenster beträgt 100.000 Token für Prompt plus Vervollständigung. Es generiert keine Bilder, Audio oder Video, stellt keine Embeddings bereit und unterstützt kein Fine-Tuning. Verwende es nur für die rechtmäßige Nutzung für Erwachsene, und beachte die harte Grenze: keine sexuelle Darstellung von Minderjährigen. Prompts werden nicht zum Training verwendet.
Weitere Anleitungen
Fragen und Antworten
Brauche ich ein spezielles SDK, um diese API in Python aufzurufen?
Nein. Das offizielle openai-Paket funktioniert. Setze base_url auf https://api.nsfwaiapi.com/v1, gib deinen Schlüssel ein und verwende die Modell-ID uncensored.
Funktioniert das Streaming genauso wie bei OpenAI?
Ja. Übergebe stream=True und lese die Server-Sent-Events chunkweise. Die Nutzung wird am Ende des Streams gemeldet.
Was soll ich tun, wenn ich einen 429-Fehler erhalte?
Warte und wiederhole die Anfrage. Wenn die Antwort einen Retry-After-Header enthält, beachte ihn. Der Fehlercode rate_limited bedeutet zu viele Anfragen pro Minute, und concurrency bedeutet zu viele Anfragen gleichzeitig.
Kann ich es testen, ohne zu bezahlen?
Ja. Nach der Anmeldung kannst du auf der API-Schlüssel-Seite $0,50 an Trial-Guthaben beanspruchen. Es erfordert keine Zahlungsdetails, ist 7 Tage gültig und steht jeder Person nur einmal zu.
Dein Schlüssel ist nur ein Formular entfernt
Erstelle ein Konto, kopiere den Schlüssel, ändere die Basis-URL. Das ist die gesamte Einrichtung.
API-Schlüssel erhaltenNsfwAiApiUnzensierte LLM-API zur NSFW-Textgenerierung