ن
مقاله آموزشی

وب‌سرویس چت جی‌پی‌تی از ایران؛ از خطای ۴۰۳ تا اولین پاسخ

نویسنده: تیم نِت اَرز 1405/07/01 ۱۰ دقیقه مطالعه ۱۷ بازدید

کد آماده است. روی لپ‌تاپ، پشت ابزار عبور، جواب می‌دهد. روی سرور که می‌رود، همان درخواست با یک خط جواب می‌گیرد: 403 Country, region, or territory not supported. اولین واکنش همه این است که کلید را عوض کنند، کتابخانه را به‌روز کنند یا نسخهٔ دیگری از نمونه‌کد را امتحان کنند. هیچ‌کدام کار نمی‌کند، چون مشکل اصلاً در کد نیست.

این راهنما مسیر عملی یک توسعه‌دهندهٔ ایرانی را از همان خطا تا اولین پاسخ موفق می‌رود: چرا این خطا می‌آید، چطور در سی ثانیه مطمئن شوید ریشه‌اش همین است، درگاه سازگار چه چیزی را عوض می‌کند، سوییچ نشانی پایه در چند زبان، و مهار خرج از روز اول.

چرا نشانی رسمی از ایران ۴۰۳ می‌دهد

وقتی برنامهٔ شما به نشانی رسمی وصل می‌شود، پیش از آنکه کلید بررسی شود، IP مبدأ درخواست دیده می‌شود. اگر آن IP در فهرست محدودشده باشد، پاسخ این است:

{
  "error": {
    "message": "Country, region, or territory not supported",
    "type": "request_forbidden",
    "param": null,
    "code": "unsupported_country_region_territory"
  }
}

سه نکته را از همین بدنه بخوانید. اول اینکه code اسم واقعی مشکل را می‌گوید و ربطی به اعتبار یا کلید ندارد. دوم اینکه این خطا دائمی است و تلاش مجدد فقط لاگ را پر می‌کند. سوم اینکه تصمیم بر اساس مکان سرور گرفته می‌شود، نه مکان شما؛ برای همین کد روی لپ‌تاپ کار می‌کند و روی سرور نه. ریشهٔ همین پیام را برای کاربر عادی هم در خطای «کشور شما پشتیبانی نمی‌شود» توضیح داده‌ایم.

در سی ثانیه مطمئن شوید ریشه همین است

پیش از هر تغییری در کد، یک بار از خودِ سرور و بدون واسطهٔ کتابخانه درخواست بفرستید. کتابخانه‌ها خطا را در لایه‌های خودشان می‌پیچند و گاهی پیام اصلی گم می‌شود:

curl -i https://api.openai.com/v1/models \
  -H "Authorization: Bearer $OPENAI_API_KEY"

کد وضعیتی که برمی‌گردد تکلیف را روشن می‌کند. ۴۰۳ با کد unsupported_country_region_territory یعنی مسیر شبکه رد شده و کلید اصلاً خوانده نشده. ۴۰۱ یعنی درخواست رسیده ولی کلید ایراد دارد؛ معمولاً متغیر محیطی روی سرور لود نشده و رشتهٔ «Bearer undefined» ساخته شده. ۴۲۹ یعنی درخواست پذیرفته شده و مسئله سهمیه یا اعتبار است. سه مسیر کاملاً متفاوت‌اند و راه‌حلشان هیچ شباهتی به هم ندارد. همین یک دستور را روی خود سرور اجرا کنید و خروجی کاملش را نگه دارید؛ در بیشتر تیکت‌هایی که به دست ما می‌رسد، همین چند خط جواب را در خودش دارد و ساعت‌ها حدس زدن را حذف می‌کند.

چیزهایی که این مشکل را حل نمی‌کنند

  • عوض کردن کتابخانه یا نسخهٔ SDK. خطا پیش از رسیدن به منطق کتابخانه رخ می‌دهد.
  • روشن بودن ابزار عبور روی لپ‌تاپ. درخواست نهایی از سرور می‌رود، نه از سیستم شما.
  • ساختن حساب با شمارهٔ خارجی. حساب ساخته می‌شود، ولی همان درخواست باز هم رد می‌شود.
  • تلاش مجدد با تأخیر. ۴۰۳ از جنس شلوغی نیست که با صبر برطرف شود.
  • خریدن کلید دست‌دوم از کانال‌ها. کلیدی که ده نفر دیگر هم دارند، هر لحظه ممکن است باطل شود و مصرف بقیه به حساب شما بنشیند.

تنها دو راه واقعی می‌ماند: کد را روی سروری خارج از محدودهٔ مسدود اجرا کنید، یا از یک درگاه سازگار استفاده کنید که خودش درخواست را از بیرون ارسال می‌کند. راه اول سرور خارجی، کارت ارزی برای شارژ حساب و نگهداری جداگانه می‌خواهد؛ راه دوم همان مسیری است که بیشتر تیم‌های ایرانی انتخاب می‌کنند.

درگاه سازگار دقیقاً چه چیزی را عوض می‌کند

«سازگار با OpenAI» یعنی همان مسیرها، همان بدنهٔ درخواست و همان قالب پاسخ. برنامهٔ شما نمی‌فهمد طرف مقابلش عوض شده؛ فقط نشانی پایه و کلید فرق می‌کنند. در عمل سه چیز جابه‌جا می‌شود:

  • مبدأ درخواست: تماس با ارائه‌دهنده از سمت سرور واسط برقرار می‌شود، پس خطای منطقه‌ای اصلاً به شما نمی‌رسد.
  • مسیر پرداخت: اعتبار را به تومان می‌خرید و به دلار در حساب می‌نشیند؛ مالیات بر ارزش افزوده پیش از پرداخت جدا نشان داده می‌شود.
  • کلید: کلید درگاه جای کلید رسمی را می‌گیرد. کلیدهای ما با sk-ntz-v1- شروع می‌شوند و فقط با نشانی پایهٔ خودمان کار می‌کنند.

آنچه عوض نمی‌شود هم مهم است: استریم، ابزاردهی (function calling)، خروجی JSON، ورودی تصویر و شمارش توکن‌ها همان‌اند. اگر پیش‌تر با نشانی رسمی کد نوشته‌اید، تقریباً هیچ‌چیز را بازنویسی نمی‌کنید. یک تفاوت کوچک اما مفید هم اضافه می‌شود: با یک کلید به چند خانوادهٔ مدل می‌رسید و برای هر کار می‌توانید سراغ مناسب‌ترینش بروید، بدون اینکه حساب و صورت‌حساب تازه‌ای باز کنید.

سوییچ یک‌خطی نشانی پایه

در Python فقط یک آرگومان به سازندهٔ کلاینت اضافه می‌شود:

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://netarz.ir/api/ai/v1",   # تنها خطی که عوض می‌شود
    api_key=os.environ["NETARZ_API_KEY"],
)

r = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "یک ایمیل کوتاه خوش‌آمد به مشتری تازه بنویس"}],
    max_tokens=300,
)
print(r.choices[0].message.content)

در Node.js همان یک خط، با نام baseURL:

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://netarz.ir/api/ai/v1",
  apiKey: process.env.NETARZ_API_KEY,
});

در لاراول، اگر با Http کار می‌کنید، فقط نشانی درخواست عوض می‌شود:

$response = Http::withToken(env('NETARZ_API_KEY'))
    ->timeout(120)
    ->post('https://netarz.ir/api/ai/v1/chat/completions', [
        'model' => 'gpt-4o-mini',
        'messages' => [['role' => 'user', 'content' => $question]],
        'max_tokens' => 300,
    ]);

$answer = $response->json('choices.0.message.content');

اگر کتابخانهٔ رسمی را در جایی صدا می‌زنید که به کدش دسترسی ندارید، بیشتر ابزارها متغیر محیطی OPENAI_BASE_URL را می‌خوانند و با همان هم کار می‌کنند.

ابزارهای بدون کد هم همین دو فیلد را می‌خواهند. در n8n و Make یک اعتبارنامهٔ سازگار با OpenAI می‌سازید و نشانی پایه را عوض می‌کنید؛ در ادیتورهایی مثل Cursor و Cline همان دو فیلد در تنظیمات مدل نشسته‌اند؛ افزونه‌های هوش مصنوعی وردپرس هم تقریباً همیشه جایی برای «Custom base URL» دارند. مسیر ادیتورها را در کرسر و کلاین را به API دلخواه وصل کنید قدم‌به‌قدم نوشته‌ایم.

چه چیزی عوض می‌شود و چه چیزی نه

موردنشانی رسمیدرگاه سازگار نِت اَرز
درخواست از سرور ایران۴۰۳ منطقه‌ایپاسخ عادی
پرداختکارت ارزیکیف پول یا درگاه بانکی، اعتبار دلاری
قالب درخواست و پاسخاستاندارد OpenAIهمان استاندارد
کتابخانهٔ رسمیکار می‌کندبا یک خط تغییر کار می‌کند
پیشوند کلیدsk-sk-ntz-v1-
فهرست مدل‌هامدل‌های همان شرکتچند ارائه‌دهنده با یک کلید
سقف خرجسقف حسابسقف هر کلید و بودجهٔ ماهانهٔ هر پروژه

نکتهٔ فهرست مدل‌ها را جدی بگیرید: با همان یک کلید، شناسهٔ مدل را عوض می‌کنید و به خانوادهٔ دیگری می‌رسید. مثلاً gpt-4o-mini برای کارهای سبک و پرتکرار، gemini-3.5-flash برای ورودی‌های طولانی و openrouter/anthropic/claude-sonnet-5 برای نوشتن و بازنویسی. فهرست به‌روز و نرخ هر مدل را در صفحهٔ وب‌سرویس هوش مصنوعی منتشر می‌کنیم؛ همیشه فهرست را از همان‌جا یا از مسیر GET /models بگیرید، نه از حافظه.

مهار خرج از همان روز اول

وب‌سرویس برخلاف اشتراک سقف خودکار ندارد؛ هر درخواست پول دارد و یک حلقهٔ معیوب در کد می‌تواند در چند دقیقه موجودی را خالی کند. چهار کاری که پیش از اولین انتشار انجام می‌دهیم:

  • max_tokens را بفرستید. پیش از هر درخواست مبلغی برای بیشترین خروجی ممکن رزرو می‌شود؛ با سقف مشخص، رزرو کوچک می‌ماند و بعد از پاسخ فقط مصرف واقعی کسر می‌شود.
  • برای هر کلید سقف هزینه بگذارید. با رسیدن به سقف، همان کلید خطای ۴۰۲ می‌دهد و بقیهٔ پروژه‌ها سالم می‌مانند.
  • برای هر پروژه بودجهٔ ماهانه تعریف کنید. ربات آزمایشی و محصول اصلی نباید از یک کیسه خرج کنند.
  • مدل را با کار جور کنید. دسته‌بندی یک پیام کوتاه با مدل کوچک همان نتیجه را می‌دهد و کسری از هزینه دارد.

یک تلهٔ رایج هم اینجاست: تاریخچهٔ گفت‌وگو. هر پیام قدیمی در هر نوبت دوباره به‌عنوان ورودی شمرده می‌شود، پس یک چت طولانی بدون کوتاه‌سازی، هزینه‌اش با هر نوبت بالا می‌رود. چند پیام آخر را نگه دارید یا قدیمی‌ها را یک بار خلاصه کنید و خلاصه را جایگزین کنید. موجودی و محدودیت‌های مؤثر روی هر کلید را هم با یک درخواست ساده می‌بینید:

curl https://netarz.ir/api/ai/v1/me -H "Authorization: Bearer $NETARZ_API_KEY"

الگوهای عملی بیشتر را در کنترل هزینهٔ API هوش مصنوعی جمع کرده‌ایم.

چک‌لیست پیش از انتشار

  • کلید فقط روی سرور و در متغیر محیطی است، نه در کد فرانت‌اند و نه در مخزن.
  • خطاهای موقتی (۴۲۹ از جنس سرعت، ۵۰۰ و ۵۰۳) تلاش مجدد با تأخیر نمایی دارند و خطاهای دائمی ندارند. فهرست تفکیک‌شده در خطاهای رایج API هوش مصنوعی.
  • مقدار finish_reason بررسی می‌شود تا پاسخ نیم‌بند به کاربر نرسد.
  • سربرگ X-Request-Id در لاگ خودتان ذخیره می‌شود تا پیگیری با پشتیبانی ساده باشد.
  • برای متن حساس کاربران سیاست نگهداری داده را خوانده‌اید: نزد ما متن پیام‌ها و پاسخ‌ها ذخیره نمی‌شود و فقط فراداده برای صورت‌حساب و امنیت می‌ماند.

قدم بعدی

مسیر کوتاه این است: یک حساب بسازید، اعتبار بخرید، در پنل یک پروژه و یک کلید با سقف هزینه بسازید، و همان نمونهٔ Python بالا را با مدل کوچک روی سرور اجرا کنید. اگر پاسخ آمد، سخت‌ترین قسمت تمام شده و بقیه‌اش همان توسعهٔ عادی نرم‌افزار است.

برای مرور مفاهیم پایه وب‌سرویس هوش مصنوعی چیست را ببینید، مسیر خرید کلید در خرید API چت‌جی‌پی‌تی در ایران آمده، و مستندات فنی کامل با فهرست خطاها و نمونه‌های بیشتر در مستندات وب‌سرویس هوش مصنوعی در دسترس است.

پرسش‌های پرتکرار

چرا کدم روی لپ‌تاپ کار می‌کند ولی روی سرور خطای 403 می‌دهد؟

چون تصمیم بر اساس IP همان ماشینی گرفته می‌شود که درخواست را می‌فرستد. لپ‌تاپ شما از مسیر دیگری بیرون می‌رود و سرور از مسیر خودش. تا وقتی درخواست از محدودهٔ مسدود ارسال شود، هیچ تغییری در کد این خطا را برطرف نمی‌کند.

برای استفاده از درگاه سازگار باید کدم را بازنویسی کنم؟

نه. قالب درخواست و پاسخ همان استاندارد OpenAI است، پس فقط نشانی پایه و کلید را عوض می‌کنید؛ در Python آرگومان base_url و در Node.js آرگومان baseURL. استریم، ابزاردهی، خروجی JSON و ورودی تصویر همان‌طور کار می‌کنند.

کلید رسمی OpenAI را می‌توانم روی نشانی پایهٔ شما بفرستم؟

نه. هر کلید فقط با درگاه خودش کار می‌کند و ترکیب کلید یک سرویس با نشانی سرویس دیگر خطای invalid_api_key می‌دهد. کلید نِت اَرز را از پنل می‌سازید و با پیشوند sk-ntz-v1- شناخته می‌شود.

اگر وسط کار اعتبارم تمام شود، درخواست‌ها چه می‌شوند؟

درخواست پیش از ارسال به ارائه‌دهنده با کد ۴۰۲ و خطای insufficient_credit رد می‌شود و هزینه‌ای ثبت نمی‌شود. با شارژ اعتبار از پنل، همان کلید بدون تغییر دوباره کار می‌کند.

محصولات مرتبط با این مقاله

مطالب مشابه

نظر خوانندگان

هنوز نظری ثبت نشده. اگر این مقاله پرسشتان را جواب داد یا جای چیزی در آن خالی ماند، همین‌جا بنویسید.

نظرتان را بنویسید

این مقاله چقدر به کارتان آمد؟ (اختیاری)

نظرها را پیش از انتشار بررسی می‌کنیم. نقد صریح مشکلی ندارد؛ تبلیغ و توهین منتشر نمی‌شود.