Toda a API em um curl
Deixe o SDK de lado por um momento. Todos os exemplos abaixo enviam a mesma coisa: um corpo JSON para POST /v1/chat/completions com um token bearer. Se você consegue enviar isso, está pronto. Obtenha uma chave na página de cadastro (o crédito de teste não exige detalhes de pagamento), exporte-a como API_KEY e execute este primeiro.
curl https://api.unrestrictedaiapi.com/v1/chat/completions \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "uncensored",
"messages": [{"role": "user", "content": "Write a two-line noir opening set in a laundromat."}],
"max_tokens": 120
}'A resposta é um objeto estilo OpenAI. O texto está em choices[0].message.content, e um bloco usage informa o custo da chamada em tokens. Há um único modelo, uncensored, então o campo model nunca muda. Você pode confirmar isso com GET /v1/models.
curl https://api.unrestrictedaiapi.com/v1/models -H "Authorization: Bearer $API_KEY"Dois limites que valem a pena memorizar antes de escrever qualquer código: a janela de contexto é de 100.000 tokens compartilhados entre prompt e conclusão, e max_tokens tem o valor padrão de 2048, a menos que você o aumente (até 32.000). Se a sua saída continua parando no meio da frase, esse valor padrão é o principal suspeito.
Python com httpx
O httpx oferece um cliente síncrono com tempos de espera razoáveis e uma versão assíncrona quando você precisa. Defina o tempo de espera explicitamente. Gerações longas podem demorar e o padrão de cinco segundos as interromperá.
import os
import httpx
resp = httpx.post(
"https://api.unrestrictedaiapi.com/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
json={
"model": "uncensored",
"messages": [
{"role": "system", "content": "You are a blunt, vivid fiction writer."},
{"role": "user", "content": "Pitch a heist where the vault is a night market."},
],
"temperature": 0.9,
"max_tokens": 300,
},
timeout=60.0,
)
resp.raise_for_status()
print(resp.json()["choices"][0]["message"]["content"])raise_for_status() converte qualquer 4xx ou 5xx em uma exceção, o que é bom para um script. Para qualquer coisa de execução longa, envolva a chamada para que dois status tenham outra chance: 429 (você atingiu o limite por chave de 300 requisições por minuto) e 503 (upstream_busy, que se resolve em alguns segundos). Tudo o mais, incluindo 401, 402 e 403, não se corrigirá na nova tentativa.
import os
import time
import httpx
def chat(messages, retries=3):
for attempt in range(retries + 1):
r = httpx.post(
"https://api.unrestrictedaiapi.com/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
json={"model": "uncensored", "messages": messages, "max_tokens": 400},
timeout=60.0,
)
if r.status_code in (429, 503) and attempt < retries:
time.sleep(2 ** attempt)
continue
r.raise_for_status()
return r.json()["choices"][0]["message"]["content"]
print(chat([{"role": "user", "content": "Name five cursed objects found in a thrift store."}]))Substitua httpx.post por httpx.AsyncClient().post dentro de um async def e você terá a versão assíncrona com a mesma carga útil.
JavaScript com fetch
O Node 18 e versões mais recentes incluem o fetch, então não há nada para instalar. Salve o arquivo como .mjs (ou defina "type": "module") para que o await de nível superior funcione. Os navegadores têm a mesma API, mas não coloque a chave no código do cliente. Faça o proxy da chamada através do seu próprio back-end.
const res = await fetch("https://api.unrestrictedaiapi.com/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "uncensored",
messages: [{ role: "user", content: "Describe a villain's morning routine in four bullets." }],
max_tokens: 250,
}),
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(`HTTP ${res.status}: ${err?.error?.message ?? "unknown error"}`);
}
const data = await res.json();
console.log(data.choices[0].message.content);Observe a ordem das verificações. fetch apenas rejeita em falha de rede, então um 402 ou 403 chega como resposta normal. Teste res.ok você mesmo e leia o JSON de erro, que sempre tem o formato {"error":{"code":...,"message":...}}.
Go com net/http
Go precisa de algumas linhas a mais, mas sem dependências. Defina apenas os campos de struct que você lê; encoding/json ignora o resto. Ler o corpo em bytes antes de verificar o status permite imprimir a mensagem de erro do servidor quando algo dá errado.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
type reply struct {
Choices []struct {
Message struct {
Content string `json:"content"`
} `json:"message"`
} `json:"choices"`
}
func main() {
payload, _ := json.Marshal(map[string]any{
"model": "uncensored",
"messages": []map[string]string{
{"role": "user", "content": "Give me a ghost story in exactly three sentences."},
},
"max_tokens": 200,
})
req, _ := http.NewRequest("POST", "https://api.unrestrictedaiapi.com/v1/chat/completions", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+os.Getenv("API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
panic(fmt.Sprintf("HTTP %d: %s", resp.StatusCode, body))
}
var out reply
if err := json.Unmarshal(body, &out); err != nil {
panic(err)
}
fmt.Println(out.Choices[0].Message.Content)
}Para produção, crie um http.Client{Timeout: 60 * time.Second} e reutilize-o. O cliente padrão não possui tempo limite, o que faz com que uma requisição pendurada se torne uma goroutine pendurada.
PHP com cURL e Ruby com Net::HTTP
A extensão cURL do PHP já está na maioria dos hosts. Construa o corpo com json_encode, retorne a transferência como string e leia o status com curl_getinfo. Pular a verificação de status é o erro clássico: um corpo 402 decodifica felizmente como JSON e seu código falha depois em uma chave choices ausente.
<?php
$payload = json_encode([
"model" => "uncensored",
"messages" => [
["role" => "user", "content" => "Write a villanelle opening about a broken vending machine."],
],
"max_tokens" => 200,
]);
$ch = curl_init("https://api.unrestrictedaiapi.com/v1/chat/completions");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => $payload,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($body === false || $status !== 200) {
fwrite(STDERR, "HTTP $status: $body\n");
exit(1);
}
$data = json_decode($body, true);
echo $data["choices"][0]["message"]["content"], "\n";Em um host compartilhado com um max_execution_time baixo, mantenha max_tokens moderado ou mova a chamada para um worker de fila.
A biblioteca padrão é suficiente aqui também. ENV.fetch levanta imediatamente se a variável estiver ausente, o que é melhor do que enviar um token bearer vazio e depurar um 401 por dez minutos.
require "net/http"
require "json"
require "uri"
uri = URI("https://api.unrestrictedaiapi.com/v1/chat/completions")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch('API_KEY')}"
req["Content-Type"] = "application/json"
req.body = JSON.generate(
model: "uncensored",
messages: [{ role: "user", content: "Invent a tavern rumor that sounds too specific to be fake." }],
max_tokens: 200
)
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true, read_timeout: 60) { |http| http.request(req) }
abort("HTTP #{res.code}: #{res.body}") unless res.is_a?(Net::HTTPSuccess)
puts JSON.parse(res.body).dig("choices", 0, "message", "content")Se você mais tarde migrar para Rails, o mesmo corpo funciona de um job em segundo plano. Mantenha a chamada fora do ciclo de requisição; gerações são mais lentas que uma consulta ao banco de dados.
Ambos os trechos enviam exatamente o corpo que o código Python envia, que é o ponto: se uma linguagem funciona e outra não, compare o JSON, não o cliente. Imprima o corpo bruto da requisição uma vez e compare byte a byte. Em nove casos de dez, o culpado é a ausência do cabeçalho Content-Type ou um nome de modelo digitado com letra maiúscula.
Streaming sem biblioteca
Streaming é o único lugar onde o HTTP simples pede um pouco mais de você. Com stream: true, o servidor responde com eventos enviados pelo servidor: cada linha começa com data: , carrega um chunk JSON e o stream termina com data: [DONE]. Um chunk de uso final é adicionado automaticamente, então você obtém contagens de tokens mesmo quando faz streaming. Esse chunk não tem entradas choices, é por isso que o loop abaixo verifica antes de indexar.
import json
import os
import httpx
body = {
"model": "uncensored",
"messages": [{"role": "user", "content": "Tell a campfire story about a lighthouse keeper."}],
"stream": True,
"max_tokens": 400,
}
headers = {"Authorization": f"Bearer {os.environ['API_KEY']}"}
with httpx.stream("POST", "https://api.unrestrictedaiapi.com/v1/chat/completions",
headers=headers, json=body, timeout=60.0) as r:
r.raise_for_status()
for line in r.iter_lines():
if not line.startswith("data: "):
continue
data = line[6:]
if data == "[DONE]":
break
chunk = json.loads(data)
if chunk.get("choices"):
print(chunk["choices"][0]["delta"].get("content") or "", end="", flush=True)
print()O mesmo padrão é portado diretamente. Em JavaScript, leia res.body com um leitor e divida em novas linhas. Em Go, envolva resp.Body em um bufio.Scanner. Em Ruby, passe um bloco para http.request e use read_body. Em PHP, defina um callback CURLOPT_WRITEFUNCTION. A lógica nunca muda: remova o prefixo, pare no sentinel, analise o JSON, imprima o delta.
Teste de fumaça de cinco minutos e verificação de orçamento
Antes de construir sobre qualquer um desses snippets, execute uma verificação rápida de sanidade. Primeiro, chame /v1/models com sua chave; um 200 prova a chave e o caminho de rede. Segundo, envie a menor requisição de chat possível com max_tokens definido para 20. Terceiro, quebre algo deliberadamente, como um token bearer vazio, e confirme que seu código expõe o 401 claramente em vez de falhar em um campo ausente.
Depois, faça a aritmética uma vez para que nada te surpreenda. Os preços são $0,25 por milhão de tokens de entrada e $1,00 por milhão de tokens de saída. Como uma suposição para ilustração, digamos que uma requisição envia 600 tokens de prompt e recebe 400 tokens de volta. Isso é 600 x $0,25 / 1.000.000 = $0,00015 de entrada, mais 400 x $1,00 / 1.000.000 = $0,0004 de saída, cerca de $0,00055 por chamada. O crédito de teste de $0,50 cobriria aproximadamente 900 chamadas desse tamanho. Seus prompts reais serão diferentes, então leia o bloco usage em algumas respostas e multiplique a partir daí.
O crédito de teste dura sete dias e não precisa de detalhes de pagamento, o que o torna ótimo para testar todos os cinco idiomas. Se você quiser um caso de uso para mirar, o guia de conteúdo NSFW mostra um exemplo completo. Lembre-se de que uma chave pertence a uma conta; se você a regenerar, atualize todos os scripts de uma vez, porque a chave antiga deixa de funcionar imediatamente.
Erros que parecem iguais em todas as linguagens
Como a API é HTTP simples, o tratamento de falhas é idêntico independente da linguagem que você escolher. Memorize esta tabela e você pode adaptar qualquer snippet acima.
| Status | Significado | Ação |
|---|---|---|
| 400 | Requisição inválida, como prompt mais max_tokens além de 100k | Reduza o prompt ou diminua max_tokens |
| 401 | Chave ausente ou inválida | Verifique API_KEY; uma chave regenerada invalida a antiga instantaneamente |
| 402 | no_credit: saldo esgotado ou crédito de teste expirado | Recarregue o saldo pré-pago |
| 403 | content_blocked | Não tente novamente; conteúdo sexual envolvendo menores é sempre bloqueado |
| 429 | Mais de 300 requisições por minuto | Reduza a taxa e tente novamente |
| 503 | upstream_busy | Tente novamente após alguns segundos |
Uma vez que o básico funcione, defina stream: true para saída token por token e leia os eventos enviados pelo servidor linha por linha. Crie prompts melhores com o guia de engenharia de prompt ou conecte a mesma chamada a uma plataforma de chat usando o tutorial do bot. As taxas estão na página de preços.