PT ▾

API LLM sem censura compatível com OpenAIuncensoredapis.com

InícioErros

Erros e Limites de Requisição: Um Manual Prático para o Uncensored API

O código de produção passa mais tempo em caminhos de falha do que no caminho principal. Este guia lista cada código de status que o Uncensored API retorna, indica quais merecem uma retentativa e oferece controle de taxa no lado do cliente para o limite de 300 requisições por minuto no Python asyncio e Node, além de uma forma calma de exibir um 402 aos seus próprios usuários.

Atualizado

Pontos principais

  1. Tente novamente apenas 429, 503 e falhas de transporte; corrija 400, 401, 403, 404 e 402 na causa.
  2. Erros chegam como JSON com um objeto de erro contendo código e mensagem, então ramifique pelo status primeiro, depois pelo código.
  3. Um balde de tokens que recarrega 5 tokens por segundo mantém uma chave abaixo de 300 requisições por minuto sem backoff do servidor.
  4. Um 402 significa que o saldo pré-pago acabou ou o teste gratuito expirou; trate-o como um estado do produto, não como uma falha.

O envelope de erro

Cada falha retorna um corpo JSON com um único objeto error no nível superior. Dois campos importam: code, uma string de máquina curta, e message, texto destinado a logs e humanos.

{
  "error": {
    "code": "no_credit",
    "message": "Balance used up or trial expired."
  }
}

Decida nesta ordem. Observe primeiro o status HTTP, pois ele indica a classe do problema. Use code apenas para separar casos que compartilham um status, e nunca analise message para lógica, já que a redação pode mudar. Para o formato de respostas bem-sucedidas, consulte a referência da API.

Códigos de status, causas e ações

StatusCódigoCausa típicaTentar novamente?Ação
400solicitação inválidaJSON malformado, ou tokens do prompt mais max_tokens acima de 100.000NãoCorrija a carga útil; reduza o histórico ou diminua max_tokens
401chave inválidaCabeçalho ausente, erro de digitação ou uma chave que foi regeneradaNãoRecarregue a chave do seu repositório de segredos
402no_creditSaldo esgotado ou teste gratuito expiradoNãoRecarregue o saldo pré-pago e depois retome
403content_blockedA solicitação envolve conteúdo sexual envolvendo menoresNãoNão reenvie; exiba sua própria mensagem de política
404não encontradoCaminho incorreto, como a ausência do prefixo /v1NãoVerifique a URL base
429rate limitMais de 300 requisições por minuto em uma chaveSimEspere com jitter; adicione controle de taxa no cliente
503upstream_busyPressão temporária de capacidadeSimTente novamente após alguns segundos

O 403 merece destaque: é uma decisão de política, não uma falha transitória. Retentar a mesma requisição desperdiça cota e retornará a mesma resposta. O caso 400 que mais surpreende as pessoas é a soma do contexto. Um prompt de 60.000 tokens com max_tokens de 8.000 transborda em 4.000 e falha, embora cada número seja válido isoladamente.

Uma política de retry que se comporta

Use uma política pequena e limitada e aplique-a apenas onde o retry pode ajudar.

  1. Liste as falhas retryable: 429, 503, resetes de conexão e timeouts. Tudo o mais gera erro imediatamente.
  2. Backoff exponencial com jitter: aguarde 1, 2, 4, 8 segundos mais uma fração aleatória de segundo, limitado a 30. O jitter evita que muitos processos tentem novamente em sincronia.
  3. Limite tentativas em cinco. Além disso, uma interrupção é mais provável do que uma má sorte, e o trabalho enfileirado deve falhar de forma visível.
  4. Desative as retentativas automáticas do SDK (max_retries=0) ao executar seu próprio loop, caso contrário, as tentativas se multiplicam e seu controle de taxa perde a contagem.
  5. Retransmita os streams do início. Um stream que falha no meio não pode ser retomado; descarte o texto parcial e reenvie a requisição, ou mantenha-o e solicite uma continuação.

Para um 503, a orientação é aguardar alguns segundos, então inicie seu primeiro atraso perto de um ou dois segundos em vez de pressionar imediatamente. Para um 429, a solução é reduzir a velocidade, o que a próxima seção automatiza.

Permanecer abaixo de 300 requisições por minuto

O limite é de 300 requisições por minuto por chave, o que equivale a cinco por segundo em média. Um bucket de tokens aplica exatamente isso: ele armazena até uma pequena explosão de tokens, recarrega a cinco por segundo e faz cada requisição consumir um token. Se o bucket estiver vazio, o chamador dorme até que exista um token.

A versão Python usa asyncio. Todas as vinte tarefas compartilham um bucket e um lock, então as corrotinas concorrentes não podem ultrapassá-lo. Observe que o bucket conta todas as tentativas, incluindo retries.

import asyncio, os, random, time
from openai import AsyncOpenAI, APIStatusError, APIConnectionError

class TokenBucket:
    """Allows `rate` acquisitions per `per` seconds, with a small burst."""
    def __init__(self, rate=300, per=60.0, burst=10):
        self.fill = rate / per          # tokens added per second (5.0)
        self.cap = burst
        self.tokens = burst
        self.stamp = time.monotonic()
        self.lock = asyncio.Lock()

    async def acquire(self):
        async with self.lock:
            while True:
                now = time.monotonic()
                self.tokens = min(self.cap, self.tokens + (now - self.stamp) * self.fill)
                self.stamp = now
                if self.tokens >= 1:
                    self.tokens -= 1
                    return
                await asyncio.sleep((1 - self.tokens) / self.fill)

client = AsyncOpenAI(base_url="https://api.uncensoredapis.com/v1",
                     api_key=os.environ["API_KEY"], max_retries=0)
bucket = TokenBucket()

RETRYABLE = {429, 503}

async def ask(prompt, attempts=5):
    for n in range(attempts):
        await bucket.acquire()
        try:
            r = await client.chat.completions.create(
                model="uncensored",
                messages=[{"role": "user", "content": prompt}],
                max_tokens=200,
            )
            return r.choices[0].message.content
        except APIStatusError as e:
            if e.status_code not in RETRYABLE or n == attempts - 1:
                raise
        except APIConnectionError:
            if n == attempts - 1:
                raise
        await asyncio.sleep(min(30, 2 ** n) + random.uniform(0, 1))

async def main():
    prompts = [f"Give one-line fact number {i} about tides." for i in range(1, 21)]
    results = await asyncio.gather(*(ask(p) for p in prompts))
    print(len(results), "answers; first:", results[0])

asyncio.run(main())

Execute-o com API_KEY exportado. Vinte requisições cabem na explosão mais alguns segundos de recarga, então você verá o término rápido; aumente a contagem para 600 e a execução se estende por cerca de dois minutos, que é o limite fazendo seu trabalho.

A versão Node, usando o pacote oficial em um módulo ES, encadeia as aquisições por uma fila de promessas para que os chamadores sejam atendidos em ordem.

import OpenAI from "openai";

class TokenBucket {
  constructor(rate = 300, perMs = 60000, burst = 10) {
    this.fillPerMs = rate / perMs;
    this.cap = burst;
    this.tokens = burst;
    this.stamp = Date.now();
    this.queue = Promise.resolve();
  }
  acquire() {
    // Serialize callers so the bucket math never races.
    this.queue = this.queue.then(async () => {
      for (;;) {
        const now = Date.now();
        this.tokens = Math.min(this.cap, this.tokens + (now - this.stamp) * this.fillPerMs);
        this.stamp = now;
        if (this.tokens >= 1) { this.tokens -= 1; return; }
        await new Promise((r) => setTimeout(r, Math.ceil((1 - this.tokens) / this.fillPerMs)));
      }
    });
    return this.queue;
  }
}

const client = new OpenAI({
  baseURL: "https://api.uncensoredapis.com/v1",
  apiKey: process.env.API_KEY,
  maxRetries: 0,
});
const bucket = new TokenBucket();

async function ask(prompt, attempts = 5) {
  for (let n = 0; n < attempts; n++) {
    await bucket.acquire();
    try {
      const r = await client.chat.completions.create({
        model: "uncensored",
        messages: [{ role: "user", content: prompt }],
        max_tokens: 200,
      });
      return r.choices[0].message.content;
    } catch (err) {
      const retryable = err.status === 429 || err.status === 503 || err.status === undefined;
      if (!retryable || n === attempts - 1) throw err;
      await new Promise((r) => setTimeout(r, Math.min(30000, 2 ** n * 1000) + Math.random() * 1000));
    }
  }
}

const prompts = Array.from({ length: 20 }, (_, i) => `Give one-line fact number ${i + 1} about tides.`);
const answers = await Promise.all(prompts.map((p) => ask(p)));
console.log(answers.length, "answers; first:", answers[0]);

Dimensione a explosão de forma conservadora. Uma explosão de 10 permite um pico curto enquanto a taxa sustentada permanece em cinco por segundo. Se vários processos compartilham uma chave, divida a taxa entre eles, por exemplo, 100 por minuto cada em três workers, ou direcione todo o tráfego por uma única fila.

Tratamento gracioso do 402 no seu produto

Um 402 com código no_credit significa que o saldo pré-pago chegou a zero ou o crédito de teste grátis expirou. É o único erro que seus usuários finais nunca devem ver como um rastreamento de pilha, e geralmente indica algo que só você pode corrigir: recarregar.

Construa três camadas. Primeiro, centralize detect em um único wrapper para que nenhum ponto de chamada precise lembrar. Segundo, translate: o navegador ou o aplicativo móvel recebe um estado neutro como capacity_paused e mostra uma tela amigável, nunca a mensagem do serviço upstream. Terceiro, alert você mesmo, porque todos os usuários são afetados ao mesmo tempo; envie uma notificação ao proprietário da conta e ative uma flag de recurso para que a interface pare de enviar requisições.

# server side: translate the upstream 402 into a product state
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from openai import AsyncOpenAI, APIStatusError
import os

app = FastAPI()
client = AsyncOpenAI(base_url="https://api.uncensoredapis.com/v1", api_key=os.environ["API_KEY"])

@app.post("/api/reply")
async def reply(body: dict):
    try:
        r = await client.chat.completions.create(
            model="uncensored", messages=body["messages"], max_tokens=400)
        return {"text": r.choices[0].message.content}
    except APIStatusError as e:
        if e.status_code == 402:
            # Tell the client app to show a "temporarily unavailable" screen,
            # and page yourself to top up the prepaid balance.
            return JSONResponse({"state": "capacity_paused"}, status_code=503)
        if e.status_code == 429 or e.status_code == 503:
            return JSONResponse({"state": "busy_retry"}, status_code=503)
        raise

Adicione também proteção proativa. Some usage.total_tokens por requisição no seu próprio registro e avise em um limite que você escolher, para que uma recarga aconteça antes que um usuário seja bloqueado. Lembre-se de que o crédito de teste grátis é de $0,50 e dura 7 dias, então um protótipo pode atingir 402 apenas por expiração, mesmo com dinheiro não gasto. As tarifas para planejamento estão na página de preços.

Assim que o saldo voltar a ser positivo, replique o trabalho que você colocou em fila enquanto estava pausado. Seu wrapper deve limpar a flag pausada no primeiro sucesso.

O que registrar e alertar

Registros bons tornam os incidentes curtos. Registre, por requisição: seu próprio id de requisição, o completion id quando presente, status HTTP, erro code, número da tentativa e uso de tokens. Nunca registre o cabeçalho Authorization ou prompts completos que contenham dados pessoais.

Alertas úteis, em ordem de prioridade: qualquer 402 (imediato), uma série sustentada de 401s (uma chave regenerada não foi implantada em todos os lugares), taxa 429 acima de alguns por cento das chamadas (seu controle de taxa está dimensionado incorretamente) e clusters 503 (reduza a velocidade, considere uma fila). Um painel de contagens de status por minuto torna os quatro visíveis de uma vez. Bem tratados, esses códigos tornam-se sinais rotineiros em vez de interrupções.

Testando seus caminhos de falha antes do lançamento

Tratamento de erros que nunca foi executado é um palpite. Você pode exercitar a maioria dos ramos sem esperar por uma interrupção real, usando requisições baratas e seus próprios testes simulados.

  • 401: chame GET /v1/models com uma chave errada deliberadamente e confirme que seu app mostra um erro de configuração para operadores, não para clientes.
  • 404: requisição /v1/chat/completion (singular) uma vez e confirme que a linha de log nomeia o caminho, para que um erro de digitação em uma base URL seja óbvio em minutos.
  • 400: envie max_tokens de 32.000 com um prompt preenchido até aproximadamente 50.000 tokens. A soma ultrapassa 100.000, e você pode observar o caminho de validação ser acionado.
  • 429 e 503: você não pode invocá-los sob demanda, então simule-os. Envolva seu cliente HTTP em um fake que levante o status para as duas primeiras chamadas, depois tenha sucesso, e verifique se seu loop dorme, retenta e retorna a resposta final.
  • 402: simule também, e confirme que a UI mostra a tela pausada, o alerta dispara e nada tenta novamente em um loop apertado.

Escreva-os como testes automatizados que executam na integração contínua. Um teste orientado a tabelas com uma linha por status da tabela acima leva minutos para ser escrito e protege a política contra refatorações bem-intencionadas. Finalmente, execute um teste de carga curto contra seu próprio controle de taxa com a API simulada: dispare 1.000 aquisições e verifique que nenhuma janela de 60 segundos contenha mais de 300 concessões mais o burst. Isso prova a aritmética antes que o tráfego real dependa dela. Quando você mais tarde ler o guia do loop de agente, reutilize o mesmo wrapper lá, já que agentes multiplicam as contagens de requisições e atingem esses limites primeiro.

Perguntas e respostas

O 429 conta contra meu saldo?

Verifique os dados de uso no seu próprio registro em vez de supor. De qualquer forma, a solução é reduzir a velocidade e tentar novamente com backoff.

Devo tentar novamente uma resposta 403 content_blocked?

Não. É um bloqueio de política e uma requisição idêntica retorna o mesmo resultado. Mostre sua própria mensagem e siga em frente.

Minha chave repentinamente retorna 401. O que mudou?

Na maioria das vezes, a chave foi regenerada, o que invalida a antiga imediatamente. Copie a nova chave da sua conta e reimplante-a.

Quantos workers podem compartilhar uma chave?

Todos compartilham o limite de 300 requisições por minuto. Divida a taxa entre workers ou canalize chamadas por uma única fila.

Sua chave está a um formulário de distância

Crie uma conta, copie a chave, altere a base URL. Essa é toda a configuração.