Penzy AI Gateway · v1

مستندات درگاه هوش مصنوعی پنزی

درگاه پنزی یک API واحد و سازگار با OpenAI است که درخواست‌های شما را به OpenAI، Anthropic و OpenRouter هدایت می‌کند. با یک کلید به همه مدل‌ها دسترسی دارید و نیازی به ساخت حساب جداگانه نزد هر ارائه‌دهنده نیست.

موردمقدار
Base URLhttps://ai.penzy.net/v1
احراز هویتAuthorization: Bearer sk-pz-… یا x-api-key: sk-pz-…
فرمتJSON — کاملاً سازگار با OpenAI Chat Completions؛ به‌علاوه Anthropic Messages بومی
استریمServer-Sent Events با "stream": true
نام مدلprovider/model — مثلاً openai/gpt-4o-mini

نکته: اگر کدی دارید که با OpenAI کار می‌کند، تنها کافی است base_url و api_key را عوض کنید. همه چیز دیگر — حتی برای Claude و Gemini — همان‌طور کار می‌کند.

شروع سریع

۱. کلید API خود را از مدیر سامانه دریافت کنید (با sk-pz- شروع می‌شود) و آن را در متغیر محیطی قرار دهید:

export PENZY_API_KEY="sk-pz-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

۲. اولین درخواست را ارسال کنید:

curl https://ai.penzy.net/v1/chat/completions \
  -H "Authorization: Bearer $PENZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-4o-mini",
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "پایتخت ایران کجاست؟"}
    ]
  }'
# pip install openai
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://ai.penzy.net/v1",
    api_key=os.environ["PENZY_API_KEY"],
)

resp = client.chat.completions.create(
    model="openai/gpt-4o-mini",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "پایتخت ایران کجاست؟"},
    ],
)
print(resp.choices[0].message.content)
// npm install openai
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://ai.penzy.net/v1",
  apiKey: process.env.PENZY_API_KEY,
});

const resp = await client.chat.completions.create({
  model: "openai/gpt-4o-mini",
  messages: [
    { role: "system", content: "You are a helpful assistant." },
    { role: "user", content: "پایتخت ایران کجاست؟" },
  ],
});
console.log(resp.choices[0].message.content);

۳. برای استفاده از Claude یا هر مدل دیگری، فقط مقدار model را عوض کنید:

resp = client.chat.completions.create(
    model="anthropic/claude-sonnet-4-5",          # Claude
    # model="openrouter/google/gemini-2.5-flash", # Gemini via OpenRouter
    # model="openrouter/deepseek/deepseek-chat",  # DeepSeek via OpenRouter
    messages=[{"role": "user", "content": "Hello!"}],
)

پاسخ نمونه:

{
  "id": "chatcmpl-9f2c...",
  "object": "chat.completion",
  "created": 1762950000,
  "model": "openai/gpt-4o-mini",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "پایتخت ایران تهران است." },
      "finish_reason": "stop"
    }
  ],
  "usage": { "prompt_tokens": 24, "completion_tokens": 9, "total_tokens": 33 }
}

احراز هویت

هر درخواست باید کلید API را در هدر Authorization به‌صورت Bearer ارسال کند. برای سازگاری با SDK رسمی Anthropic، هدر x-api-key نیز پذیرفته می‌شود.

POST /v1/chat/completions HTTP/1.1
Host: ai.penzy.net
Authorization: Bearer sk-pz-xxxxxxxxxxxxxxxx
Content-Type: application/json

امنیت: کلید خود را هرگز در کد سمت کلاینت (مرورگر یا اپلیکیشن موبایل) یا مخزن Git قرار ندهید. در صورت افشا، فوراً به مدیر سامانه اطلاع دهید تا کلید باطل و کلید جدید صادر شود.

هر کلید می‌تواند این محدودیت‌ها را داشته باشد (توسط مدیر تعیین می‌شود):

  • مدل‌های مجاز — مثلاً فقط openai/* یا anthropic/claude-*
  • سقف بودجه — دلاری، به‌صورت روزانه، ماهانه یا کل
  • محدودیت نرخ — حداکثر تعداد درخواست در دقیقه (RPM)
  • تاریخ انقضا

وضعیت و مصرف کلید خود را در داشبورد مصرف یا با GET /v1/me ببینید.

نام‌گذاری مدل‌ها

شناسه هر مدل به شکل provider/model است. پیشوند، ارائه‌دهنده‌ای را که درخواست به آن ارسال می‌شود تعیین می‌کند:

پیشوندارائه‌دهندهنمونه
openai/OpenAI (مستقیم)openai/gpt-4o، openai/o4-mini، openai/text-embedding-3-small
anthropic/Anthropic (مستقیم)anthropic/claude-sonnet-4-5، anthropic/claude-haiku-4-5
openrouter/OpenRouteropenrouter/google/gemini-2.5-pro، openrouter/x-ai/grok-4

میان‌برها

  • نام بدون پیشوند که با gpt-، o1/o3/o4، text-embedding- یا dall-e شروع شود ← OpenAI. مثال: gpt-4o-mini
  • نام بدون پیشوند که با claude شروع شود ← Anthropic. مثال: claude-sonnet-4-5
  • نام دوبخشی که پیشوندش یک ارائه‌دهنده نیست ← OpenRouter. مثال: meta-llama/llama-3.3-70b-instruct

فهرست کامل و به‌روز مدل‌ها همراه قیمت هر میلیون توکن در صفحه مدل‌ها و از طریق GET /v1/models در دسترس است.

Chat Completions

POST/v1/chat/completions

اصلی‌ترین endpoint برای گفت‌وگو با همه مدل‌ها. درخواست و پاسخ دقیقاً مطابق OpenAI Chat Completions است. برای مدل‌های Anthropic، درگاه به‌طور خودکار پیام‌ها، ابزارها، تصاویر و استریم را به فرمت Claude تبدیل می‌کند.

پارامترهای بدنه

پارامترنوعتوضیح
modelrequiredstringشناسه مدل، مثلاً anthropic/claude-sonnet-4-5
messagesrequiredarrayآرایه پیام‌ها با نقش‌های system، developer، user، assistant، tool
streambooleanدریافت پاسخ به‌صورت تدریجی (SSE). پیش‌فرض false
stream_optionsobject{"include_usage": true} برای دریافت مصرف توکن در آخرین chunk
max_tokens / max_completion_tokensintegerحداکثر توکن خروجی. برای Claude در صورت عدم تعیین ۸۱۹۲ در نظر گرفته می‌شود
temperaturenumber۰ تا ۲ (برای Claude حداکثر ۱ اعمال می‌شود)
top_pnumberنمونه‌برداری هسته‌ای
stopstring | arrayتوالی‌های توقف
toolsarrayتعریف توابع قابل فراخوانی — جزئیات
tool_choicestring | objectauto، none، required یا {"type":"function","function":{"name":"…"}}
parallel_tool_callsbooleanاجازه فراخوانی هم‌زمان چند ابزار
response_formatobject{"type":"json_object"} یا json_schema — جزئیات
reasoning_effortstringlow، medium، high برای مدل‌های استدلالی — جزئیات
userstringشناسه کاربر نهایی شما (برای ردیابی سوءاستفاده)

سایر پارامترهای استاندارد OpenAI (مانند seed، logprobs، frequency_penalty) برای مدل‌های OpenAI و OpenRouter بدون تغییر منتقل می‌شوند. پارامترهای اختصاصی OpenRouter (مثل provider، transforms، models) نیز پشتیبانی می‌شوند.

گفت‌وگوی چندمرحله‌ای

messages = [{"role": "system", "content": "تو یک دستیار برنامه‌نویسی هستی."}]

while True:
    user = input("you: ")
    messages.append({"role": "user", "content": user})
    r = client.chat.completions.create(model="anthropic/claude-sonnet-4-5", messages=messages)
    answer = r.choices[0].message.content
    messages.append({"role": "assistant", "content": answer})
    print("ai:", answer)

استریم (Server-Sent Events)

با "stream": true پاسخ به‌صورت قطعات chat.completion.chunk و در قالب SSE ارسال می‌شود و با data: [DONE] پایان می‌یابد. این قابلیت برای همه ارائه‌دهندگان یکسان است.

stream = client.chat.completions.create(
    model="anthropic/claude-sonnet-4-5",
    messages=[{"role": "user", "content": "یک داستان کوتاه بنویس"}],
    stream=True,
    stream_options={"include_usage": True},
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
    if chunk.usage:
        print("\n\ntokens:", chunk.usage.total_tokens)
const stream = await client.chat.completions.create({
  model: "anthropic/claude-sonnet-4-5",
  messages: [{ role: "user", content: "یک داستان کوتاه بنویس" }],
  stream: true,
});
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
curl -N https://ai.penzy.net/v1/chat/completions \
  -H "Authorization: Bearer $PENZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"openai/gpt-4o-mini","stream":true,
       "messages":[{"role":"user","content":"Count to 5"}]}'
// فقط در محیط سرور یا با کلید محدود استفاده کنید
const res = await fetch("https://ai.penzy.net/v1/chat/completions", {
  method: "POST",
  headers: { "Authorization": `Bearer ${KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({ model: "openai/gpt-4o-mini", stream: true,
    messages: [{ role: "user", content: "Hi" }] }),
});
const reader = res.body.getReader();
const dec = new TextDecoder();
let buf = "";
while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  buf += dec.decode(value, { stream: true });
  const lines = buf.split("\n"); buf = lines.pop();
  for (const line of lines) {
    if (!line.startsWith("data: ") || line === "data: [DONE]") continue;
    const delta = JSON.parse(line.slice(6)).choices[0]?.delta?.content;
    if (delta) output.textContent += delta;
  }
}

فرمت خام رویدادها:

data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":""}}]}

data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"سلام"}}]}

data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: [DONE]

Tool / Function Calling

ابزارها را با فرمت استاندارد OpenAI تعریف کنید. این قابلیت برای مدل‌های OpenAI، Claude و بیشتر مدل‌های OpenRouter کار می‌کند.

import json

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get current weather for a city",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]

messages = [{"role": "user", "content": "هوای تهران چطوره؟"}]
r = client.chat.completions.create(model="anthropic/claude-sonnet-4-5",
                                   messages=messages, tools=tools)
msg = r.choices[0].message

if msg.tool_calls:
    messages.append(msg)  # assistant message with tool_calls
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        result = {"city": args["city"], "temp_c": 18, "sky": "clear"}  # your code
        messages.append({"role": "tool", "tool_call_id": call.id,
                         "content": json.dumps(result)})
    final = client.chat.completions.create(model="anthropic/claude-sonnet-4-5",
                                           messages=messages, tools=tools)
    print(final.choices[0].message.content)
const tools = [{
  type: "function",
  function: {
    name: "get_weather",
    description: "Get current weather for a city",
    parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"] },
  },
}];

const messages = [{ role: "user", content: "هوای تهران چطوره؟" }];
const r = await client.chat.completions.create({ model: "openai/gpt-4o", messages, tools });
const msg = r.choices[0].message;

if (msg.tool_calls) {
  messages.push(msg);
  for (const call of msg.tool_calls) {
    const args = JSON.parse(call.function.arguments);
    messages.push({ role: "tool", tool_call_id: call.id,
      content: JSON.stringify({ city: args.city, temp_c: 18 }) });
  }
  const final = await client.chat.completions.create({ model: "openai/gpt-4o", messages, tools });
  console.log(final.choices[0].message.content);
}

در حالت استریم، آرگومان‌های ابزار به‌صورت تکه‌تکه در delta.tool_calls[i].function.arguments می‌آیند و finish_reason برابر tool_calls خواهد بود.

ورودی تصویر (Vision)

تصویر را به‌صورت URL یا Data URL با base64 در محتوای پیام بفرستید. برای Claude، درگاه به‌طور خودکار آن را به بلوک image تبدیل می‌کند.

import base64

with open("invoice.png", "rb") as f:
    b64 = base64.b64encode(f.read()).decode()

r = client.chat.completions.create(
    model="anthropic/claude-sonnet-4-5",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": "مبلغ کل این فاکتور چقدر است؟"},
            {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}},
            # or: {"type": "image_url", "image_url": {"url": "https://example.com/a.jpg"}}
        ],
    }],
)

خروجی JSON و Structured Output

با response_format خروجی را به JSON محدود کنید. برای مدل‌های OpenAI از json_schema با strict استفاده کنید. برای Claude، اسکیما به دستورالعمل سیستمی تبدیل می‌شود (یا از tool calling برای خروجی ساخت‌یافته کاملاً تضمین‌شده استفاده کنید).

r = client.chat.completions.create(
    model="openai/gpt-4o-mini",
    messages=[{"role": "user", "content": "Extract: Ali Rezaei, 32, Tehran"}],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "person",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "age": {"type": "integer"},
                    "city": {"type": "string"},
                },
                "required": ["name", "age", "city"],
                "additionalProperties": False,
            },
        },
    },
)
print(r.choices[0].message.content)  # {"name":"Ali Rezaei","age":32,"city":"Tehran"}

استدلال (Reasoning / Extended Thinking)

برای مدل‌های استدلالی OpenAI (سری o و GPT-5) از reasoning_effort استفاده کنید. برای Claude همین پارامتر به extended thinking با بودجه متناظر تبدیل می‌شود و متن استدلال در فیلد reasoning_content برگردانده می‌شود. همچنین می‌توانید پارامتر بومی thinking را مستقیماً ارسال کنید.

r = client.chat.completions.create(
    model="anthropic/claude-sonnet-4-5",
    messages=[{"role": "user", "content": "اثبات کن جذر ۲ گنگ است."}],
    reasoning_effort="medium",          # low | medium | high
    # extra_body={"thinking": {"type": "enabled", "budget_tokens": 10000}},
)
print(r.choices[0].message.content)
reasoning_effortبودجه thinking در Claude
low2,048 توکن
medium8,192 توکن
high24,576 توکن

Anthropic Messages API (بومی)

POST/v1/messages

اگر از SDK رسمی Anthropic یا ابزارهایی مانند Claude Code استفاده می‌کنید، این endpoint درخواست را بدون هیچ تبدیلی به Anthropic ارسال می‌کند؛ بنابراین همه قابلیت‌ها (prompt caching، extended thinking، PDF، citations، ابزارهای سمت سرور و…) در دسترس هستند. هدرهای anthropic-version و anthropic-beta منتقل می‌شوند.

این endpoint برای مدل‌های anthropic/* (یا نام‌های بدون پیشوند claude-…) طراحی شده است. برای استفاده از سایر مدل‌ها از Chat Completions استفاده کنید. شمارش توکن نیز از طریق POST /v1/messages/count_tokens در دسترس است.

curl https://ai.penzy.net/v1/messages \
  -H "x-api-key: $PENZY_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "Hello, Claude"}]
  }'
# pip install anthropic
import anthropic

client = anthropic.Anthropic(
    base_url="https://ai.penzy.net",   # بدون /v1 — SDK خودش اضافه می‌کند
    api_key="sk-pz-...",
)
msg = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello, Claude"}],
)
print(msg.content[0].text)
// npm install @anthropic-ai/sdk
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({ baseURL: "https://ai.penzy.net", apiKey: process.env.PENZY_API_KEY });
const msg = await client.messages.create({
  model: "claude-sonnet-4-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Hello, Claude" }],
});
console.log(msg.content[0].text);

Responses API

POST/v1/responses

API جدید OpenAI برای مدل‌های openai/* (و مدل‌های OpenRouter که آن را پشتیبانی می‌کنند). درخواست بدون تغییر به ارائه‌دهنده منتقل می‌شود.

r = client.responses.create(
    model="openai/gpt-4o-mini",
    input="سه نکته برای نوشتن کد تمیز بگو",
)
print(r.output_text)

Embeddings

POST/v1/embeddings

تولید بردار برای جست‌وجوی معنایی و RAG. مدل‌های openai/* و مدل‌های embedding در OpenRouter.

e = client.embeddings.create(
    model="openai/text-embedding-3-small",
    input=["سلام دنیا", "Hello world"],
)
print(len(e.data[0].embedding))  # 1536
curl https://ai.penzy.net/v1/embeddings \
  -H "Authorization: Bearer $PENZY_API_KEY" -H "Content-Type: application/json" \
  -d '{"model":"openai/text-embedding-3-small","input":"سلام دنیا"}'

تولید تصویر

POST/v1/images/generations

تولید تصویر با مدل‌های OpenAI مانند openai/gpt-image-1 و openai/dall-e-3.

img = client.images.generate(
    model="openai/gpt-image-1",
    prompt="A Persian garden at sunset, watercolor",
    size="1024x1024",
)
import base64; open("out.png", "wb").write(base64.b64decode(img.data[0].b64_json))

همچنین POST /v1/moderations و POST /v1/completions (legacy) برای مدل‌های سازگار پشتیبانی می‌شوند.

فهرست مدل‌ها

GET/v1/models

مدل‌هایی که کلید شما اجازه استفاده از آن‌ها را دارد، همراه با طول context و قیمت (دلار به ازای هر یک میلیون توکن).

curl https://ai.penzy.net/v1/models -H "Authorization: Bearer $PENZY_API_KEY"
for m in client.models.list():
    print(m.id)
{
  "object": "list",
  "data": [
    {
      "id": "anthropic/claude-sonnet-4-5-20250929",
      "object": "model",
      "owned_by": "anthropic",
      "name": "Claude Sonnet 4.5",
      "context_length": 1000000,
      "pricing": { "input_per_million": 3, "output_per_million": 15, "currency": "USD" }
    }
  ]
}

مصرف و اطلاعات کلید

GET/v1/me

اطلاعات کلید، بودجه باقی‌مانده، مجموع مصرف و ریز مصرف ۳۰ روز اخیر به تفکیک مدل و روز. همین اطلاعات در داشبورد مصرف به‌صورت گرافیکی نمایش داده می‌شود.

curl https://ai.penzy.net/v1/me -H "Authorization: Bearer $PENZY_API_KEY"
{
  "key": { "name": "backend-prod", "prefix": "sk-pz-AbCd…wxyz", "owner": "Team A", "rpm_limit": 60, "allowed_models": "*" },
  "budget": { "limit_usd": 50, "period": "monthly", "spent_usd": 12.41, "remaining_usd": 37.59 },
  "totals": { "requests": 1840, "prompt_tokens": 2210400, "completion_tokens": 512330, "cost_usd": 31.07 },
  "last_30_days": { "by_model": [ … ], "daily": [ … ] },
  "recent": [ … ]
}

Python

از کتابخانه رسمی openai استفاده کنید. می‌توانید به‌جای کد، متغیرهای محیطی را تنظیم کنید:

pip install openai
export OPENAI_BASE_URL="https://ai.penzy.net/v1"
export OPENAI_API_KEY="sk-pz-..."
import asyncio
from openai import AsyncOpenAI

client = AsyncOpenAI()  # reads OPENAI_BASE_URL / OPENAI_API_KEY

async def ask(model, q):
    r = await client.chat.completions.create(model=model, messages=[{"role": "user", "content": q}])
    return model, r.choices[0].message.content

async def main():
    models = ["openai/gpt-4o-mini", "anthropic/claude-haiku-4-5", "openrouter/google/gemini-2.5-flash"]
    for model, answer in await asyncio.gather(*(ask(m, "یک جمله درباره ایران") for m in models)):
        print(f"[{model}] {answer}\n")

asyncio.run(main())
import requests

r = requests.post(
    "https://ai.penzy.net/v1/chat/completions",
    headers={"Authorization": "Bearer sk-pz-..."},
    json={"model": "openai/gpt-4o-mini", "messages": [{"role": "user", "content": "Hi"}]},
    timeout=120,
)
r.raise_for_status()
print(r.json()["choices"][0]["message"]["content"])

Node.js / TypeScript

import OpenAI from "openai";

export const ai = new OpenAI({
  baseURL: "https://ai.penzy.net/v1",
  apiKey: process.env.PENZY_API_KEY!,
  timeout: 120_000,
  maxRetries: 2,
});

const r = await ai.chat.completions.create({
  model: "openrouter/deepseek/deepseek-chat",
  messages: [{ role: "user", content: "Explain closures in JS" }],
});
console.log(r.choices[0].message.content);
const r = await fetch("https://ai.penzy.net/v1/chat/completions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PENZY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "anthropic/claude-haiku-4-5",
    messages: [{ role: "user", content: "Hi" }],
  }),
});
const data = await r.json();
console.log(data.choices[0].message.content);

PHP

<?php
$ch = curl_init("https://ai.penzy.net/v1/chat/completions");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("PENZY_API_KEY"),
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "model" => "openai/gpt-4o-mini",
        "messages" => [["role" => "user", "content" => "سلام"]],
    ]),
]);
$res = json_decode(curl_exec($ch), true);
echo $res["choices"][0]["message"]["content"];
use Illuminate\Support\Facades\Http;

$res = Http::withToken(env('PENZY_API_KEY'))
    ->timeout(120)
    ->post('https://ai.penzy.net/v1/chat/completions', [
        'model' => 'anthropic/claude-sonnet-4-5',
        'messages' => [['role' => 'user', 'content' => 'سلام']],
    ])->throw()->json();

return $res['choices'][0]['message']['content'];
// composer require openai-php/client
$client = OpenAI::factory()
    ->withBaseUri('https://ai.penzy.net/v1')
    ->withApiKey(getenv('PENZY_API_KEY'))
    ->make();

$r = $client->chat()->create([
    'model' => 'openai/gpt-4o-mini',
    'messages' => [['role' => 'user', 'content' => 'Hello']],
]);
echo $r->choices[0]->message->content;

Anthropic SDK

SDK رسمی Anthropic را با base_url="https://ai.penzy.net" (بدون /v1) پیکربندی کنید. نمونه در بخش Anthropic Messages آمده است. متغیرهای محیطی معادل:

export ANTHROPIC_BASE_URL="https://ai.penzy.net"
export ANTHROPIC_API_KEY="sk-pz-..."

LangChain

# pip install langchain-openai
from langchain_openai import ChatOpenAI, OpenAIEmbeddings

llm = ChatOpenAI(
    base_url="https://ai.penzy.net/v1",
    api_key="sk-pz-...",
    model="anthropic/claude-sonnet-4-5",
    temperature=0.3,
)
print(llm.invoke("What is RAG?").content)

emb = OpenAIEmbeddings(base_url="https://ai.penzy.net/v1", api_key="sk-pz-...",
                       model="openai/text-embedding-3-small")
import { ChatOpenAI } from "@langchain/openai";

const llm = new ChatOpenAI({
  model: "openai/gpt-4o-mini",
  apiKey: process.env.PENZY_API_KEY,
  configuration: { baseURL: "https://ai.penzy.net/v1" },
});
const res = await llm.invoke("What is RAG?");

Vercel AI SDK

// npm install ai @ai-sdk/openai-compatible
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { streamText } from "ai";

const penzy = createOpenAICompatible({
  name: "penzy",
  baseURL: "https://ai.penzy.net/v1",
  apiKey: process.env.PENZY_API_KEY,
  includeUsage: true,
});

const result = streamText({
  model: penzy("anthropic/claude-sonnet-4-5"),
  prompt: "Write a haiku about Tehran",
});
for await (const text of result.textStream) process.stdout.write(text);

Claude Code

Claude Code از Anthropic Messages API استفاده می‌کند و می‌تواند مستقیماً به درگاه پنزی متصل شود:

export ANTHROPIC_BASE_URL="https://ai.penzy.net"
export ANTHROPIC_AUTH_TOKEN="sk-pz-..."
# اختیاری: انتخاب مدل
export ANTHROPIC_MODEL="claude-sonnet-4-5"
claude
// ~/.claude/settings.json
{
  "env": {
    "ANTHROPIC_BASE_URL": "https://ai.penzy.net",
    "ANTHROPIC_AUTH_TOKEN": "sk-pz-..."
  }
}

کلیدی که برای Claude Code استفاده می‌کنید باید به مدل‌های anthropic/* دسترسی داشته باشد.

Cursor، Continue و سایر ابزارها

هر ابزاری که «OpenAI-compatible endpoint» یا «Custom OpenAI base URL» را پشتیبانی کند، با درگاه پنزی کار می‌کند. مقادیر عمومی:

تنظیممقدار
Base URL / API Basehttps://ai.penzy.net/v1
API Keysk-pz-…
Modelیکی از شناسه‌های صفحه مدل‌ها

Continue (VS Code / JetBrains)

models:
  - name: Claude via Penzy
    provider: openai
    model: anthropic/claude-sonnet-4-5
    apiBase: https://ai.penzy.net/v1
    apiKey: sk-pz-...
  - name: GPT-4o via Penzy
    provider: openai
    model: openai/gpt-4o
    apiBase: https://ai.penzy.net/v1
    apiKey: sk-pz-...

Cursor

در Settings → Models گزینه Override OpenAI Base URL را روی https://ai.penzy.net/v1 قرار دهید، کلید را وارد و مدل دلخواه (مثلاً openai/gpt-4o) را اضافه کنید.

Open WebUI / LibreChat / n8n

در بخش اتصال OpenAI، آدرس https://ai.penzy.net/v1 و کلید خود را وارد کنید. فهرست مدل‌ها به‌طور خودکار از /v1/models خوانده می‌شود.

خطاها

خطاها با کد HTTP مناسب و بدنه‌ای سازگار با OpenAI بازگردانده می‌شوند (در /v1/messages با فرمت Anthropic):

{
  "error": {
    "message": "Budget exhausted for this key ($50.0012 of $50 – monthly).",
    "type": "insufficient_quota",
    "code": "budget_exceeded"
  }
}
HTTPcodeمعنا و راه‌حل
400invalid_request_errorبدنه نامعتبر یا پارامتر ناقص. پیام خطا را بررسی کنید.
401invalid_api_keyکلید ارسال نشده یا نامعتبر است.
402budget_exceededبودجه کلید در دوره جاری تمام شده است.
403key_disabled، key_expired، model_not_allowed، model_disabledکلید غیرفعال/منقضی است یا اجازه استفاده از این مدل را ندارد.
404model_not_foundمدل شناخته‌شده نیست. شناسه را از /v1/models بردارید.
429rate_limit_exceededاز سقف درخواست در دقیقه عبور کرده‌اید؛ با تأخیر نمایی دوباره تلاش کنید. ممکن است از سمت ارائه‌دهنده نیز صادر شود.
502upstream_errorخطا در ارتباط با ارائه‌دهنده (پیام با نام ارائه‌دهنده شروع می‌شود).
503provider_unavailableارائه‌دهنده در این درگاه فعال نیست.

هر پاسخ هدر x-request-id دارد؛ هنگام گزارش مشکل آن را برای پشتیبانی ارسال کنید.

محدودیت‌ها و بودجه

  • بودجه: هزینه هر درخواست بر اساس توکن‌های ورودی/خروجی و قیمت مدل (صفحه مدل‌ها) محاسبه و از بودجه کلید کسر می‌شود. دوره‌های روزانه و ماهانه بر اساس زمان UTC بازنشانی می‌شوند.
  • RPM: در صورت تعیین، درخواست‌های بیش از سقف در یک پنجره ۶۰ ثانیه‌ای با 429 رد می‌شوند.
  • حجم درخواست: حداکثر ۵۰ مگابایت (برای تصاویر base64 کافی است).
  • زمان پاسخ: حداکثر ۱۰ دقیقه برای هر درخواست؛ برای پاسخ‌های طولانی از استریم استفاده کنید.

بهترین روش‌ها

  • برای پاسخ‌های طولانی همیشه از استریم استفاده کنید تا تجربه کاربر بهتر و احتمال timeout کمتر شود.
  • برای خطاهای 429 و 5xx تلاش مجدد با exponential backoff پیاده کنید (SDKهای رسمی این کار را خودکار انجام می‌دهند).
  • برای هر سرویس یا محیط (dev/prod) کلید جداگانه بگیرید تا مصرف قابل ردیابی و کلید قابل ابطال باشد.
  • برای کارهای ساده از مدل‌های سبک‌تر (gpt-4o-mini، claude-haiku، gemini-flash) استفاده کنید؛ هزینه تا ده برابر کمتر است.
  • هرگز کلید را در فرانت‌اند قرار ندهید؛ درخواست‌ها را از طریق backend خود ارسال کنید.

سؤالات متداول

آیا داده‌های من ذخیره می‌شود؟

درگاه محتوای پیام‌ها را ذخیره نمی‌کند؛ تنها متادیتای مصرف (مدل، تعداد توکن، هزینه، زمان و وضعیت) برای گزارش‌گیری ثبت می‌شود. سیاست نگهداری داده هر ارائه‌دهنده بر درخواست‌ها اعمال می‌شود.

چرا نام مدل Claude در پاسخ با نام درخواستی متفاوت است؟

نام‌های بدون تاریخ (مثل claude-sonnet-4-5) نام مستعار آخرین نسخه هستند و ممکن است ارائه‌دهنده نسخه دقیق را برگرداند.

تفاوت anthropic/… و openrouter/anthropic/… چیست؟

اولی مستقیماً به API رسمی Anthropic می‌رود؛ دومی از طریق OpenRouter. هر دو مدل یکسانی هستند ولی قیمت، محدودیت‌ها و قابلیت‌های جانبی ممکن است اندکی متفاوت باشد.

کلید جدید یا افزایش بودجه می‌خواهم.

با مدیر سامانه تماس بگیرید. کلیدها تنها از پنل مدیریت صادر می‌شوند.