NsfwAiApi/Guia
API LLM NSFW em Python: streaming, JSON e ferramentas
Para usar uma API de LLM NSFW no estilo Python, você só precisa do pacote openai padrão e de duas alterações: a URL base e a chave. Este guia mostra uma primeira requisição, streaming, modo JSON, chamada de funções e tratamento limpo de limites de requisições.

Configuração: instale e faça a primeira chamada
Você precisa de um Python 3 atual, do pacote openai (pip install openai) e de uma chave de API. Contas novas podem resgatar um crédito de teste de $0,50 na página da chave de API com um clique após o cadastro; não precisa de detalhes de pagamento. Armazene a chave em uma variável de ambiente, não no código-fonte.
O serviço fala o protocolo de conclusões de chat do OpenAI: POST /v1/chat/completions para geração e GET /v1/models para a lista de modelos. A autenticação é um token de autorização, e o ID do modelo é uncensored. Este é o script mínimo funcional:
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)
Se preferir verificar a conexão diretamente do shell, a mesma requisição funciona com 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."}]}'
O modelo é ajustado para responder sem recusas de conteúdo para uso adulto lícito. Isso afeta o tom do que ele escreverá, não a forma da API, então o resto do seu código parece qualquer outra integração com OpenAI. Mantenha prompts para ficção, bate-papo com personagens e outros projetos adultos lícitos, e mantenha todos os personagens como adultos.
Alguns hábitos ajudam desde o primeiro dia. Coloque as instruções que nunca mudam na mensagem do sistema e mantenha a mensagem do usuário para a parte que muda a cada turno. Escolha uma temperatura para uma tarefa e deixe-a assim enquanto compara prompts, porque mudar duas coisas ao mesmo tempo não lhe ensina nada. Registre o ID da requisição e as contagens de tokens de resp.usage ao lado de cada chamada, para que, quando sua conta ou sua saída te surpreendam, haja um registro para verificar.
Respostas em streaming com SSE
Defina stream=True e o servidor envia tokens como eventos enviados pelo servidor. Para apps de chat, isso é muito importante, porque o leitor vê as primeiras palavras rapidamente em vez de esperar pela resposta completa. Os totais de uso chegam ao final do fluxo, para que uma requisição em streaming ainda possa ser cobrada e registrada com precisão.
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)
Dois detalhes economizam tempo de depuração. Primeiro, o último chunk pode ter uma lista choices vazia, é por isso que o loop verifica antes de indexar. Segundo, uma requisição em streaming mantém sua slot de concorrência até o stream terminar, então se seu código abandonar um stream sem fechá-lo, esse slot fica ocupado. Use with client.chat.completions.create(..., stream=True) as stream: ou chame stream.close() quando parar de ler antecipadamente.
Modo JSON para saída estruturada
Quando seu programa precisa de dados e não de prosa, como uma ficha de personagem, uma lista de passos da trama ou tags para uma história, use o modo JSON com response_format={"type": "json_object"}. Também diga "JSON" no prompt e descreva as chaves que você quer. A configuração pede um objeto JSON, mas não pode prometer que suas chaves estão presentes, então verifique-as.
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"])
Se uma resposta longa for cortada por max_tokens, o JSON estará incompleto e json.loads falhará. Deixe max_tokens não definido, já que é opcional e a conclusão pode usar o resto da janela, ou defina-o generosamente.
Chamada de funções
A chamada de funções permite que o modelo peça ao seu código para fazer algo, como rolar dados, consultar uma nota de personagem ou buscar um preço. Você passa tools e, se quiser, tool_choice. A resposta contém tool_calls; você as executa e envia os resultados de volta como mensagens com role: "tool". As respostas em streaming carregam tool_calls também nos 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)
Trate os argumentos como entrada não confiável. Valide-os antes de agir sobre eles, exatamente como você faria para um formulário web. Modelos às vezes enviam um campo ausente ou um tipo errado, e uma pequena proteção em torno de json.loads impede que uma chamada ruim encerre sua sessão.
Limites de taxa, tentativas e seu saldo
Os limites protegem o serviço e dão a você comportamento previsível. Uma chave completa permite 8 requisições simultâneas e 300 requisições por minuto. Uma chave de teste permite 2 e 60. O corpo máximo é 8 MB. Dois tipos de erro de limite retornam como HTTP 429: o código rate_limited significa muitas requisições por minuto, e concurrency significa muitas ao mesmo tempo. Uma sobrecarga temporária também retorna 429, com um cabeçalho Retry-After: 5.
O SDK da OpenAI tenta novamente alguns erros por padrão. Para controle, desative isso e escreva um pequeno loop que respeita 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)
Toda resposta carrega X-Request-Id, que é o que você cita se entrar em contato com o suporte, e X-Balance-USD, que mostra seu crédito restante. Os cabeçalhos X-RateLimit-Limit-Requests e X-RateLimit-Limit-Concurrency informam quais limites se aplicam à sua chave. Para executar muitos trabalhos ao mesmo tempo, limite a concorrência no seu próprio código com um semáforo dimensionado para a sua chave, 8 para uma chave completa e 2 para uma chave de teste, para que você raramente veja um 429.
Executando muitas requisições em paralelo
Trabalhos em lote, como gerar tags para algumas centenas de fragmentos de história, são onde os limites de concorrência importam. Uma chave completa aceita 8 requisições ao mesmo tempo, então execute o trabalho por meio de um cliente assíncrono e um semáforo desse tamanho. Se você estiver em uma chave de teste, use 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)
O semáforo é o truque todo. Sem ele, uma lista de 200 fragmentos dispara 200 requisições juntas, e a maioria delas volta como erros 429 com o código concurrency. Com ele, o trabalho flui na velocidade que sua chave permite e nada é desperdiçado. Se você também executar um limite por minuto, 300 requisições por minuto em uma chave completa resultam em cinco por segundo, para que requisições muito curtas possam atingir o limite por minuto antes do limite de concorrência.
Problemas comuns e soluções
- 401 ou erros de autenticação. A chave está ausente, truncada ou enviada para o host errado. Imprima
client.base_urle verifique a variável de ambiente. O cabeçalho deve lerAuthorization: Bearerseguido pela sua chave. - Modelo não encontrado. O único ID do modelo é
uncensored. Um exemplo copiado que indica outro nome falhará, então chameGET /v1/modelsse tiver dúvidas. - Respostas vazias ou curtas em um loop de história. Verifique se você anexa a resposta do assistente à lista de mensagens antes do próximo turno. Sem isso, o modelo vê apenas sua última mensagem.
- Respostas que se repetem. Aumente
frequency_penaltyum pouco, por exemplo para 0,3, ou diminuatemperaturese o texto se desviar. As penalidades,top_p,stopeseedsão todas passadas. - Saída diferente a cada execução. Defina
seede umatemperaturebaixa quando precisar de resultados o mais repetíveis possível enquanto depura. - Requisição muito grande. O limite do corpo é 8 MB, e prompt mais conclusão devem caber em 100.000 tokens. Remova ou resuma o contexto antigo antes que atinja qualquer um dos limites.
Quando algo parece errado no lado do servidor, salve o cabeçalho X-Request-Id da resposta e envie-o para o contato da página de suporte junto com uma descrição curta. Isso permite que os operadores encontrem sua requisição exata.
Quanto custa e o que não pode fazer
A precificação é $0,15 por 1M de tokens de entrada e $0,50 por 1M de tokens de saída, a partir de crédito pré-pago sem assinatura. Requisições falhas ou recusadas não são cobradas, e o crédito nunca expira. Como exemplo prático, um script que envia 3.000 tokens e recebe 800 paga $0,00045 de entrada mais $0,0004 de saída, o que é $0,00085 por chamada, então 1.000 chamadas custam $0,85. Recargas vão de $20 a $500, com bônus de +5% a partir de $50, +10% a partir de $100 e +15% a partir de $200. Este site aceita apenas cripto, em USDT no TRC20 ou USDC na Base. Veja a página de preços para a tabela completa, e a documentação para a referência de endpoint.
Os limites são claros. É um único modelo de pesos abertos, executado por um provedor independente, e não a API oficial de nenhum fornecedor de modelo. A janela de contexto é de 100.000 tokens para prompt mais conclusão. Não gera imagens, áudio ou vídeo, não fornece embeddings e não oferece suporte a fine-tuning. Use apenas para uso adulto lícito, e note o limite rígido: sem conteúdo sexual envolvendo menores de idade. Os prompts não são usados para treinamento.
Mais guias
Perguntas e respostas
Preciso de um SDK especial para chamar esta API em Python?
Não. O pacote openai oficial funciona. Defina base_url para https://api.nsfwaiapi.com/v1, passe sua chave e use o ID do modelo uncensored.
O streaming funciona da mesma forma que com a OpenAI?
Sim. Passe stream=True e leia os eventos enviados pelo servidor chunk por chunk. O uso é reportado no final do stream.
O que devo fazer ao receber um erro 429?
Aguarde e tente novamente. Se a resposta tiver o cabeçalho Retry-After, respeite-o. O código de erro rate_limited significa muitas requisições por minuto, e concurrency significa muitas requisições simultâneas.
Posso testar sem pagar?
Sim. Após o cadastro, você pode resgatar $0,50 de crédito de teste na página da chave de API. Não precisa de detalhes de pagamento, é válido por 7 dias e é um por pessoa.
Sua chave está a um formulário de distância
Crie uma conta, copie a chave e altere a URL base. Essa é toda a configuração.
Obter chave de APINsfwAiApiAPI de LLM sem censura para geração de texto NSFW