一个 curl 搞定整个 API
先忘掉 SDK。下面的每个示例都发送相同的内容:向 POST /v1/chat/completions 发送 JSON 主体和 Bearer 令牌。如果你能发送这个,你就完成了。从 注册页面获取密钥(试用额度无需支付信息),将其导出为 API_KEY,然后运行此代码。
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
}'响应是一个 OpenAI 风格的对象。文本位于 choices[0].message.content,usage 块会告诉你该调用消耗的 token 数量。只有一个模型 uncensored,因此 model 字段不会改变。你可以通过 GET /v1/models 进行确认。
curl https://api.unrestrictedaiapi.com/v1/models -H "Authorization: Bearer $API_KEY"在编写任何代码之前,有两个限制值得记住:上下文窗口为 100,000 个 token,提示词和补全内容共享该窗口;max_tokens 默认为 2048,除非你将其调高(最高至 32,000)。如果你的输出总是在句子中间停止,这个默认值通常是罪魁祸首。
Python 使用 httpx
httpx 提供具有合理超时设置的同步客户端,以及你需要的异步版本。显式设置超时。长生成可能需要一段时间,默认的 5 秒超时会将其截断。
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() 将任何 4xx 或 5xx 状态码转换为异常,这对于脚本来说没问题。对于任何长时间运行的任务,请包装调用,以便让两个状态码获得重试机会:429(你达到了每个密钥每分钟 300 次请求的限制)和 503(upstream_busy,几秒后会自动恢复)。其他所有状态码,包括 401、402 和 403,在重试时不会自动修复。
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."}]))在 async def 中将 httpx.post 替换为 httpx.AsyncClient().post,即可得到具有相同负载的异步版本。
JavaScript 使用 fetch
Node 18 及更高版本内置了 fetch,无需安装。将文件保存为 .mjs(或设置 "type": "module")以支持顶层 await。浏览器也提供相同的 API,但请勿将密钥放在前端代码中。请通过你自己的后端代理该请求。
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);注意检查顺序。fetch 仅在网络失败时拒绝,因此 402 或 403 会作为正常响应到达。自行测试 res.ok 并读取错误 JSON,其结构始终为 {"error":{"code":...,"message":...}}。
Go 使用 net/http
Go 需要更多的代码行,但无需依赖项。仅定义你读取的结构体字段;encoding/json 会忽略其余部分。在检查状态之前将正文读取为字节,以便在出错时打印服务器的错误消息。
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)
}对于生产环境,构建一个 http.Client{Timeout: 60 * time.Second} 并重复使用它。默认客户端没有任何超时,这就是卡住的请求变成卡住的 goroutine 的原因。
PHP 使用 cURL 和 Ruby 使用 Net::HTTP
PHP 的 cURL 扩展已在大多数主机上启用。使用 json_encode 构建请求体,将传输作为字符串返回,然后使用 curl_getinfo 读取状态。跳过状态检查是经典错误:402 响应体可以顺利解码为 JSON,然后你的代码会因缺少 choices 键而失败。
<?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";在具有低 max_execution_time 的共享主机上,保持 max_tokens 适中,或将调用移至队列工作者。
标准库也足够了。ENV.fetch 如果变量缺失会立即抛出异常,这比发送空的 Bearer 令牌并调试 10 分钟的 401 错误要好得多。
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")如果你后来迁移到 Rails,相同的请求体也适用于后台作业。将调用保持在请求周期之外;生成过程比数据库查询慢。
这两个代码片段发送的正文与 Python 代码发送的完全相同,这正是重点:如果一种语言有效而另一种无效,请比较 JSON 而不是客户端。打印一次原始请求正文并按字节进行比较。十有八九的原因是缺少 Content-Type 标头或模型名称输入时使用了大写字母。
无库流式输出
流式输出是纯 HTTP 需要你多做一点的地方。使用 stream: true,服务器通过服务器发送事件(SSE)响应:每行以 data: 开头,携带一个 JSON 块,流以 data: [DONE] 结束。会自动添加最终的 usage 块,因此即使进行流式输出也能获得 token 计数。该块没有 choices 条目,这就是下面的循环在索引之前进行检查的原因。
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()相同模式可直接移植。在 JavaScript 中,使用读取器读取 res.body 并按换行符分割。在 Go 中,将 resp.Body 包装在 bufio.Scanner 中。在 Ruby 中,向 http.request 传递一个块并使用 read_body。在 PHP 中,设置一个 CURLOPT_WRITEFUNCTION 回调。逻辑永远不会改变:剥离前缀,在哨兵处停止,解析 JSON,打印差异。
五分钟冒烟测试与预算检查
在任何这些代码片段之上构建之前,先运行一次快速检查。首先,使用你的密钥调用 /v1/models;200 状态码证明密钥和网络路径有效。其次,发送你能发送的最小聊天请求,并将 max_tokens 设置为 20。第三,故意破坏某些内容,例如空的 Bearer 令牌,并确认你的代码清晰地显示 401 错误,而不是因缺少字段而崩溃。
然后做一次算术计算,以免出现意外。价格为每百万输入 token 0.25 美元,每百万输出 token 1.00 美元。作为说明性假设,假设一个请求发送 600 个提示词 token 并接收 400 个 token。即 600 x $0.25 / 1,000,000 = $0.00015 输入,加上 400 x $1.00 / 1,000,000 = $0.0004 输出,每次调用约 $0.00055。0.50 美元的免费试用额度大约可以覆盖 900 次该规模的调用。你的实际提示词会有所不同,因此请读取一些响应中的 usage 块并据此进行计算。
试用额度有效期为七天,无需提供支付信息,足以测试所有五种语言。如果你需要一个用例作为目标,NSFW 内容指南 提供了一个完整的示例。请记住,一个密钥对应一个账户;如果重新生成密钥,请同时更新所有脚本,因为旧密钥会立即失效。
所有语言中相同的错误
由于 API 是纯 HTTP,无论你选择哪种语言,故障处理都是相同的。记住这个表,你就可以移植上面的任何代码片段。
| 状态 | 含义 | 操作 |
|---|---|---|
| 400 | 请求错误,例如提示词加 max_tokens 超过 10 万 | 缩短提示词或降低 max_tokens |
| 401 | 密钥缺失或无效 | 检查 API_KEY;重新生成的密钥会立即使旧密钥失效 |
| 402 | no_credit:余额已耗尽或试用已过期 | 为预付额度充值 |
| 403 | content_blocked | 不要重试;涉及未成年人的色情内容始终会被拦截 |
| 429 | 每分钟超过 300 次请求 | 退避并重试 |
| 503 | upstream_busy | 等待几秒后重试 |
基础功能就绪后,设置 stream: true 以实现逐 token 流式输出,并逐行读取服务器发送的事件。使用 提示词指南 优化提示词,或通过 机器人指南 将调用接入聊天平台。费率详见 定价页面。