اگر آخرین درخواستتان با 401 Unauthorized برگشت و مطمئنید توکن را درست کپی کردهاید، احتمالاً مشکل جای دیگری است: هدر Authorization را با پیشوند اشتباه فرستادهاید، یا توکن را از محیطی استفاده میکنید که IP آن در allowlist نیست. این متن برای کسی نوشته شده که میداند چه میخواهد و فقط مقدار دقیق و رفتار رسمی را لازم دارد.
احراز هویت با توکن: چیزی که واقعاً در هدر میرود
هیچ کوکی، هیچ session، هیچ امضای HMAC. هر درخواست یک هدر دارد و بس:
Authorization: Bearer snc_live_9f3a...c41d
Content-Type: application/json
پیشوند Bearer حساس به حروف بزرگ و کوچک است. bearer با حرف کوچک، 401 میدهد و در بدنه پاسخ هم چیزی نمیگوید که به شما بفهماند مشکل از حروف است. این را در لاگهای پشتیبانی زیاد دیدهام.
توکنها دو نوعاند: snc_live_ برای عملیات واقعی روی منابع، و snc_test_ که فقط در برابر محیط sandbox کار میکند و روی منابع واقعی 403 برمیگرداند. توکن test را در CI بگذارید، نه در سرور production.
هر توکن یک scope دارد. توکنی که فقط compute:read دارد، برای POST /v1/instances پاسخ 403 Forbidden میگیرد، نه 401. تفاوت این دو کد مهم است: 401 یعنی هویت تو را نشناختم، 403 یعنی شناختم ولی اجازه نداری. اگر مانیتورینگتان این دو را یکی میگیرد، هشدارهای بیفایده میسازید.
چرخش توکن بدون قطع سرویس
هر حساب میتواند همزمان دو توکن فعال داشته باشد. ترتیب درست این است: توکن جدید بسازید، سرویس را با آن بالا بیاورید، ترافیک را جابهجا کنید، بعد توکن قدیمی را revoke کنید. اگر اول revoke کنید، درخواستهای در پرواز با 401 میافتند و اگر retry logic ندارید، یک بازهٔ خطا در نمودار میبینید.
توکن revoke شده فوراً بیاعتبار میشود؛ کش سمت سرور وجود ندارد. این را در طراحی retry در نظر بگیرید.
نقاط پایانی: چه چیزی زیر /v1 هست
همهچیز نسخهدار است. مسیر بدون /v1 وجود ندارد و 404 میدهد، نه redirect. این عمدی است.
| متد و مسیر | کار | نکته |
|---|---|---|
GET /v1/instances | فهرست سرورها | صفحهبندی با ?page= و ?per_page= (حداکثر ۱۰۰) |
POST /v1/instances | ساخت سرور | پاسخ 202 است، نه 201 |
GET /v1/instances/{id} | وضعیت یک سرور | فیلد status تا اتمام provisioning روی building میماند |
POST /v1/instances/{id}/actions | روشن/خاموش/ریاستارت | بدنه: {"type":"reboot"} |
GET /v1/volumes | دیسکهای جدا | فقط NVMe در پلنهای فعلی |
GET /v1/snapshots | اسنپشاتها | ساخت اسنپشات از سرور در حال اجرا مجاز است |
GET /v1/regions | مناطق موجود | بدون احراز هویت هم پاسخ میدهد |
نکتهای که وقت آدم را میخورد: POST /v1/instances با 202 Accepted برمیگردد و بدنهاش id را دارد، ولی سرور هنوز وجود خارجی ندارد. اگر بلافاصله GET بزنید، ممکن است 404 بگیرید. باید poll کنید تا status از building به running برسد. بازهٔ معمول بین ۳۰ تا ۹۰ ثانیه است و به منطقه و اندازهٔ پلن بستگی دارد.
برای کارهای زیرساختی سنگینتر، مثل بالا آوردن کلاستر یا اتصال شبکهٔ خصوصی بین دیتابیس و وب، این API فقط پوسته را میسازد؛ بقیهٔ کار در شبکه خصوصی بین سرورها؛ ارتباط امن دیتابیس و وب بدون اینترنت توضیح داده شده.
محدودیت نرخ: عددها و هدرها
سقف پیشفرض ۶۰ درخواست در دقیقه بهازای هر توکن است، و ۶۰۰ درخواست در دقیقه بهازای هر IP. کدام اول میخورد؟ هر کدام که زودتر به سقفش برسد. اگر یک توکن را بین ده سرویس تقسیم کنید، عملاً سهم هرکدام شش درخواست در دقیقه میشود و اینجا جایی است که خیلیها غافلگیر میشوند.
هر پاسخ این هدرها را دارد:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 7
X-RateLimit-Reset: 1718032440
Retry-After: 12
X-RateLimit-Reset یک Unix timestamp است، نه تعداد ثانیه. اگر آن را بهعنوان «ثانیه تا ریست» بخوانید، منطق backoff شما بیمعنی میشود. Retry-After فقط روی پاسخ 429 میآید و همان چیزی است که باید مستقیم استفاده کنید.
عملیات نوشتن سقف جداگانه و سختگیرانهتری دارند: ۲۰ درخواست در دقیقه. دلیلش روشن است؛ ساخت سرور یک عملیات سنگین روی hypervisor است. اگر اسکریپت provisioning دارید که ۵۰ سرور را پشتسرهم میسازد، با 429 میخوابد. راه درست، صفبندی با فاصلهٔ حداقل سه ثانیه بین درخواستهای نوشتن است، نه افزایش موازیسازی.
اینجا اشتباه میکنند
خطای رایجی که در تیکتها میبینم: کسی 429 میگیرد، retry را با تأخیر صفر و در یک حلقه میگذارد، و بعد شکایت میکند که «API قطع است». چیزی که در لاگ سمت ما دیده میشود، انفجار درخواستهای همزمان است که پنجرهٔ نرخ را مدام پر نگه میدارد و عملاً شما را از سقف بیرون نمیآورد. علامتش این است: نمودار خطا صاف و پیوسته است، نه پرشی. اگر خطا پیوسته است، مشکل نرخ است نه شبکه.
راهحل: exponential backoff با jitter، و احترام به Retry-After. یک بار در کد بنویسید، همهجا استفاده کنید.
قالب پاسخ خطا: همان چیزی که پارس میکنید
همهٔ خطاها، بدون استثنا، یک شکل دارند:
{
"error": {
"code": "rate_limit_exceeded",
"message": "Too many write requests. Retry after 12 seconds.",
"request_id": "req_8c21f0a9b7e4",
"details": [
{ "field": "region", "issue": "unavailable" }
]
}
}
روی code شرط بگذارید، نه روی message. متن پیام ممکن است تغییر کند؛ کد پایدار است. request_id را در لاگ خودتان ذخیره کنید. بدون آن، وقتی تیکت میزنید، ما نمیتوانیم درخواست مشخص شما را پیدا کنیم و مکالمه دو روز طول میکشد.
خطاهای اعتبارسنجی 422 برمیگردانند، نه 400. آرایهٔ details دقیقاً میگوید کدام فیلد مشکل دارد. اگر پارسر شما فقط message را میخواند، این اطلاعات را دور میریزید.
کدام کد را کجا انتظار داشته باشید
400— بدنهٔ JSON خراب است یاContent-Typeغلط است.401— توکن نیست، منقضی است، یا پیشوندBearerاشتباه است.403— توکن معتبر است ولی scope کافی ندارد.404— منبع وجود ندارد، یا نسخهٔ مسیر (/v1) جا افتاده.409— تداخل وضعیت؛ مثلاً ریاستارت سروری که در حال خاموش شدن است.422— اعتبارسنجی فیلدها رد شده.429— سقف نرخ.5xx— سمت ما. retry با backoff مجاز است.
یک تلهٔ ظریف: 409 را retry نکنید. اگر سرور در حالت گذار است، تکرار درخواست وضعیت را بدتر میکند. باید وضعیت را poll کنید تا گذار تمام شود.
محدودیتهایی که در عمل میخورید
API برای خودکارسازی طراحی شده، نه برای مانیتورینگ لحظهای. اگر میخواهید هر ۵ ثانیه وضعیت ۲۰۰ سرور را بپرسید، این کار را نکنید؛ بهجایش webhook یا پایش سمت خود سرور را راه بیندازید. راهنمای مانیتورینگ ابری؛ راهنمای عملی پایش و هشدار در زیرساخت ابری دقیقاً برای همین حالت نوشته شده و از سقف نرخ API عبور نمیکند.
محدودیت دوم: صفحهبندی. GET /v1/instances حداکثر ۱۰۰ آیتم در هر صفحه میدهد و فیلتر «همه را بده» ندارد. اگر بیش از ۱۰۰ منبع دارید، باید حلقه بزنید. ترتیب پیشفرض بر اساس زمان ساخت نزولی است، پس منابع جدید همیشه در صفحهٔ اولاند.
محدودیت سوم و مهمتر: عملیات حذف برگشتپذیر نیست. DELETE /v1/instances/{id} فوراً اجرا میشود و دیسک هم پاک میشود. اگر قبل از تغییرات پرخطر اسنپشات نگرفته باشید، چیزی برای بازگرداندن نیست. روش کار با اسنپشات در فضای ابری؛ نجاتبخش قبل از تغییرات پرخطر را قبل از اولین اسکریپت حذف بخوانید، نه بعدش.
کدام روش را انتخاب کنم: API خام یا ابزار رسمی
اگر فقط چند سرور دارید و کارهایتان تکراری است، CLI را انتخاب کنید. سریعتر به نتیجه میرسید و لازم نیست مدیریت توکن و backoff را خودتان بنویسید. اگر provisioning بخشی از یک pipeline است، یا باید بین دهها منبع هماهنگی ایجاد کنید، API خام را انتخاب کنید؛ ولی از روز اول لایهٔ retry و صفبندی را جدی بگیرید.
حالتی که در آن انتخاب واضح اشتباه است: استفاده از API برای کارهایی که یک بار در ماه انجام میشوند. نوشتن و نگهداشتن کد برای اینها هزینهاش از سه کلیک در پنل بیشتر است.
برای بارهای کاری که خودشان مقیاس میگیرند، بهجای نوشتن منطق مقیاسدهی روی API، از کوبرنتیز مدیریتشده استفاده کنید و مقیاسدهی را به آن بسپارید. اگر هم فقط میخواهید منابع را بالا و پایین ببرید بدون قطعی، مقیاسدهی منابع ابری بدون قطعی؛ راهنمای عملی ارتقای زیرساخت مسیر دقیقتری میدهد. و اگر کل زیرساختتان را از صفر میچینید، زیرساخت ابری (IaaS) نقطهٔ شروع منطقیتری از ساختن همهچیز روی API خام است.
پرسشهای پرتکرار
چرا با وجود توکن درست، پاسخ ۴۰۱ میگیرم؟
سه علت شایع دارد. اول، پیشوند Bearer با حرف کوچک نوشته شده. دوم، توکن snc_test_ روی منبع واقعی استفاده شده که پاسخ درستش 403 است ولی بعضی واسطها آن را 401 نشان میدهند. سوم، توکن revoke شده و کش سمت سرور وجود ندارد، پس فوراً بیاعتبار است.
اگر هر سه مورد را رد کردید، request_id پاسخ را بردارید و با آن پیگیری کنید. بدون این شناسه، پیدا کردن درخواست مشخص شما عملاً غیرممکن است.
محدودیت نرخ API سرورنت چقدر است و چطور آن را دور بزنم؟
سقف پیشفرض ۶۰ درخواست در دقیقه بهازای هر توکن و ۶۰۰ درخواست در دقیقه بهازای هر IP است. عملیات نوشتن سقف جداگانهٔ ۲۰ درخواست در دقیقه دارند. دور زدنش راه ندارد و تلاش برای دور زدن، پنجره را پر نگه میدارد و وضعیت را بدتر میکند.
کاری که باید بکنید: هدر Retry-After را مستقیم بخوانید، بین درخواستهای نوشتن حداقل سه ثانیه فاصله بگذارید، و توکن را بین سرویسها تقسیم نکنید. برای پایش مداوم هم بهجای poll کردن API، از webhook استفاده کنید.
آیا حذف سرور از طریق API قابل بازگشت است؟
نه. DELETE /v1/instances/{id} فوراً اجرا میشود و دیسک سرور هم پاک میشود. هیچ سطل زباله یا دورهٔ نگهداری وجود ندارد.
قبل از هر اسکریپت حذف، یک اسنپشات بگیرید. ساخت اسنپشات از سرور در حال اجرا مجاز است و همین کار، تفاوت بین یک حادثهٔ دهدقیقهای و یک روز از دست رفته است.
پاسخ ساخت سرور چرا ۲۰۲ است و نه ۲۰۱؟
چون ساخت سرور یک عملیات ناهمگام است. پاسخ 202 یعنی درخواست پذیرفته شد، نه اینکه منبع ساخته شد. بدنه شامل id است ولی سرور هنوز در وضعیت building قرار دارد.
باید با GET /v1/instances/{id} وضعیت را poll کنید تا به running برسد. این بازه معمولاً بین ۳۰ تا ۹۰ ثانیه است. اگر بلافاصله بعد از 202 درخواست GET بزنید، ممکن است 404 بگیرید و این طبیعی است.