API ทั้งหมดใน curl เดียว
ลืม SDK ไปก่อนสักครู่ ตัวอย่างด้านล่างทั้งหมดส่งสิ่งเดียวกัน: body แบบ JSON ไปยัง POST /v1/chat/completions พร้อม bearer token หากคุณส่งสิ่งนั้นได้ ก็ถือว่าเสร็จแล้ว รับคีย์จาก หน้าลงทะเบียน (เครดิตทดลองใช้ไม่ต้องใส่รายละเอียดการชำระเงิน) แล้ว export เป็น 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 บอกคุณว่าคำขอนั้นใช้โทเคนไปเท่าไหร่ มีโมเดลเดียวคือ uncensored ดังนั้นฟิลด์ model จะไม่เปลี่ยนแปลง คุณสามารถยืนยันสิ่งนี้ได้ด้วย GET /v1/models
curl https://api.unrestrictedaiapi.com/v1/models -H "Authorization: Bearer $API_KEY"ข้อจำกัดสองประการที่ควรจำก่อนเขียนโค้ด: หน้าต่างบริบทคือ 100,000 โทเคนที่ใช้ร่วมกันระหว่างพรอมต์และการตอบ และ max_tokens เริ่มต้นที่ 2048 เว้นแต่คุณจะเพิ่มค่า (สูงสุด 32,000) หากการตอบหยุดกลางประโยค ค่าเริ่มต้นนี้มักเป็นสาเหตุ
Python ด้วย httpx
httpx มอบไคลเอนต์แบบซิงโครนัสที่มีค่า timeout ที่สมเหตุสมผล และไคลเอนต์ async คู่เมื่อคุณต้องการ ตั้งค่า timeout ให้ชัดเจน การ generate ยาวๆ อาจใช้เวลาสักพัก และค่าเริ่มต้นที่ห้าวินาทีจะตัดการเชื่อมต่อเหล่านั้น
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."}]))เปลี่ยน httpx.post เป็น httpx.AsyncClient().post ภายใน async def และคุณจะได้เวอร์ชันอะซิงโครนัสพร้อม payload เดียวกัน
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 จะมาในรูปแบบของ response ปกติ ให้ตรวจสอบ res.ok ด้วยตนเอง และอ่าน body JSON ของข้อผิดพลาด ซึ่งมีโครงสร้าง {"error":{"code":...,"message":...}} เสมอ
Go ด้วย net/http
Go ต้องการบรรทัดเพิ่มเติมเล็กน้อยแต่ไม่ต้องพึ่ง dependencies กำหนดเฉพาะฟิลด์ของ struct ที่คุณอ่าน; encoding/json จะละเว้นส่วนที่เหลือ การอ่านบอดี้เป็น bytes ก่อนตรวจสอบสถานะช่วยให้คุณพิมพ์ข้อความข้อผิดพลาดจากเซิร์ฟเวอร์เมื่อเกิดข้อผิดพลาด
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} แล้วใช้ซ้ำ ไคลเอนต์ค่าเริ่มต้นไม่มี timeout เลย ซึ่งเป็นสาเหตุที่ทำให้คำขอที่ค้างกลายเป็น goroutine ที่ค้าง
PHP ด้วย cURL และ Ruby ด้วย Net::HTTP
ส่วนขยาย cURL ของ PHP มีอยู่แล้วในโฮสต์ส่วนใหญ่ สร้างบอดี้ด้วย json_encode คืนค่าการถ่ายโอนเป็นสตริง แล้วอ่านสถานะด้วย curl_getinfo การข้ามการตรวจสอบสถานะเป็นข้อผิดพลาดคลาสสิก: บอดี้ 402 สามารถ decode เป็น 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 ให้ต่ำหรือย้ายการเรียกไปยัง worker คิว
ไลบรารีมาตรฐานก็เพียงพอที่นี่เช่นกัน ENV.fetch จะเกิดข้อผิดพลาดทันทีหากตัวแปรหายไป ซึ่งดีกว่าการส่ง bearer token ว่างเปล่าและดีบัก 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 ในภายหลัง body เดียวกันนี้ใช้งานได้จาก job ในพื้นหลัง เก็บการเรียกไว้ภายนอกวงจรคำขอ; การสร้างข้อความช้ากว่าการสอบถามฐานข้อมูล
ตัวอย่างทั้งสองส่ง body เดียวกันกับที่ Python ส่ง ซึ่งเป็นจุดประสงค์: หากภาษาหนึ่งใช้งานได้แต่อีกภาษาไม่ได้ ให้เปรียบเทียบ JSON ไม่ใช่ไคลเอนต์ พิมพ์ body คำขอแบบ raw ครั้งเดียวแล้วเปรียบเทียบทีละไบต์ เก้าในสิบครั้ง สาเหตุคือขาด header Content-Type หรือชื่อโมเดลพิมพ์ด้วยตัวพิมพ์ใหญ่
สตรีมมิงโดยไม่ใช้ไลบรารี
สตรีมมิงเป็นจุดเดียวที่ HTTP ธรรมดาต้องการให้คุณทำมากขึ้น ด้วย stream: true เซิร์ฟเวอร์จะตอบกลับด้วย server-sent events: แต่ละบรรทัดขึ้นต้นด้วย data: พร้อม JSON chunk และสตรีมจะสิ้นสุดด้วย data: [DONE] chunk usage สุดท้ายจะถูกเพิ่มโดยอัตโนมัติ ดังนั้นคุณจึงได้จำนวนโทเคนแม้เมื่อสตรีมมิง chunk นี้ไม่มีรายการ 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 ด้วย reader และแยกตามบรรทัดใหม่ ใน Go ห่อหุ้ม resp.Body ด้วย bufio.Scanner ใน Ruby ส่งบล็อกไปยัง http.request และใช้ read_body ใน PHP ตั้งค่า callback CURLOPT_WRITEFUNCTION ตรรกะไม่เปลี่ยนแปลง: ตัด prefix ออก หยุดที่ sentinel แยกวิเคราะห์ JSON พิมพ์ delta
การทดสอบเบื้องต้น 5 นาทีและการตรวจสอบงบประมาณ
ก่อนที่คุณจะสร้างบนพื้นฐานของตัวอย่างเหล่านี้ ให้ทำการตรวจสอบความถูกต้องอย่างรวดเร็ว ขั้นแรก เรียก /v1/models ด้วยคีย์ของคุณ; 200 พิสูจน์ว่าคีย์และเส้นทางเครือข่ายถูกต้อง ขั้นที่สอง ส่งคำขอแชทที่เล็กที่สุดเท่าที่จะทำได้โดยตั้งค่า max_tokens เป็น 20 ขั้นที่สาม ทำลายบางอย่างโดยเจตนา เช่น bearer token ว่างเปล่า และยืนยันว่าโค้ดของคุณแสดง 401 อย่างชัดเจนแทนที่จะล้มเหลวเนื่องจากฟิลด์ที่หายไป
จากนั้นคำนวณตัวเลขเพียงครั้งเดียวเพื่อให้ไม่มีอะไรมาทำให้คุณประหลาดใจ ราคาคือ $0.25 ต่อหนึ่งล้าน input tokens และ $1.00 ต่อหนึ่งล้าน output tokens เพื่อเป็นตัวอย่างสมมติ ให้สมมติว่าคำขอส่ง 600 prompt tokens และได้รับ 400 tokens กลับมา นั่นคือ 600 x $0.25 / 1,000,000 = $0.00015 สำหรับ input บวก 400 x $1.00 / 1,000,000 = $0.0004 สำหรับ output รวมประมาณ $0.00055 ต่อการเรียกใช้ เครดิตทดลองใช้ $0.50 จะครอบคลุมการเรียกใช้ขนาดนั้นได้ประมาณ 900 ครั้ง พรอมต์จริงของคุณจะแตกต่างกัน ดังนั้นให้อ่านบล็อก usage ในบางรีสปอนส์แล้วคูณค่าจากตรงนั้น
เครดิตทดลองใช้ใช้งานได้เจ็ดวันและไม่ต้องใส่รายละเอียดการชำระเงิน ซึ่งเพียงพอสำหรับการทดสอบทั้งห้าภาษา หากคุณต้องการใช้เคสเป้าหมาย ดู คู่มือเนื้อหา NSFW เพื่อตัวอย่างที่สมบูรณ์ จำไว้ว่าคีย์หนึ่งอันเป็นของบัญชีหนึ่งบัญชี; หากคุณสร้างคีย์ใหม่ ให้อัปเดตสคริปต์ทั้งหมดพร้อมกัน เพราะคีย์เก่าจะหยุดทำงานทันที
ข้อผิดพลาดที่เหมือนกันในทุกภาษา
เนื่องจาก API เป็น HTTP ธรรมดา การจัดการข้อผิดพลาดจึงเหมือนกันไม่ว่าคุณจะเลือกภาษาใด จดจำตารางนี้แล้วคุณสามารถพอร์ตโค้ดตัวอย่างด้านบนไปยังภาษาอื่นได้
| สถานะ | ความหมาย | การดำเนินการ |
|---|---|---|
| 400 | คำขอไม่ถูกต้อง เช่น พรอมต์รวมกับ max_tokens เกิน 100k | ลดขนาดพรอมต์หรือลด max_tokens |
| 401 | คีย์หายไปหรือไม่ถูกต้อง | ตรวจสอบ API_KEY; คีย์ที่สร้างใหม่จะยกเลิกคีย์เก่าทันที |
| 402 | no_credit: ยอดเงินหมดหรือเครดิตทดลองใช้หมดอายุ | เติมเงินในยอดเงินแบบเติมเงินล่วงหน้า |
| 403 | content_blocked | อย่าลองใหม่; เนื้อหาทางเพศที่เกี่ยวข้องกับเด็กจะถูกบล็อกเสมอ |
| 429 | มากกว่า 300 คำขอต่อนาที | ถอยออกและลองใหม่ |
| 503 | upstream_busy | ลองใหม่ในอีกไม่กี่วินาที |
เมื่อพื้นฐานทำงานได้แล้ว ให้ตั้งค่า stream: true เพื่อเอาต์พุตทีละโทเคน และอ่านเหตุการณ์ที่ส่งจากเซิร์ฟเวอร์ทีละบรรทัด สร้างพรอมต์ที่ดีขึ้นด้วย คู่มือการเขียนพรอมต์ หรือเชื่อมต่อการเรียกใช้เดียวกันเข้ากับแพลตฟอร์มแชทโดยใช้ การแนะนำบอต อัตราการคิดค่าบริการอยู่ที่ หน้าราคา