احراز هویت
هر درخواستی که به این سرویس میزنید باید کلید API داشته باشد. کلید در هدر میرود، نه در پارامتر مسیر و نه در بدنه:
X-API-Key: kserp_...
curl -s -H "X-API-Key: $KEY" \
https://serp.rasaad.ir/api/v1/operator/capabilities
X-API-Key، x-api-key و X-Api-Key
هر سه یکی حساب میشوند.کلید از کجا میآید
کلیدها از طریق پشتیبانی صادر میشوند، نه در پنل رصد. سه چیز را دربارهٔ عمر کلید بدانید:
| موضوع | رفتار |
|---|---|
| نمایش مقدار کامل | فقط یک بار، هنگام ساخت |
| آنچه ذخیره میشود | فقط هش SHA-256 |
| کلید باطلشده | بلافاصله رد میشود |
کلید را در متغیر محیطی (env) نگه دارید، نه داخل ریپوی گیت. هرکس این کلید را دارد، با همان دسترسیها و همان مالکیت به سرویس دسترسی دارد.
کلید هم «کی» است و هم «چه»
یک کلید دو چیز را با هم تعیین میکند:
| ویژگی | تعیین میکند |
|---|---|
owner | دادههایتان به نام چه کسی ثبت میشود و چه چیزی میبینید |
scopes | چه کارهایی مجاز است |
جدول دسترسیها
| دسترسی | چه چیزی را باز میکند |
|---|---|
queries:write | POST /queries |
results:read | GET /queries، GET /queries/{id}، GET /results، GET /results/{id} و خروجیهای serpapi / markdown / raw آن |
targets:read | GET /targets، GET /targets/{id}، GET /targets/{id}/history |
targets:write | POST /targets، PATCH /targets/{id} |
targets:delete | DELETE /targets/{id}، POST /targets/{id}/enable و POST /targets/{id}/disable، و PATCH /targets/{id} وقتی بدنه enabled را ست میکند |
usage:read | GET /usage |
targets:delete در دسترسی پیشفرض نیست. یک کلید میتواند هدف بسازد ولی نتواند
حذفش کند، مگر اینکه با همین دسترسی صادر شده باشد. دلیلش ساده است: حذف دادهٔ جمعشدهٔ
آن هدف را از بین میبرد، نه اینکه فقط چیزی را از آن بخواند. اگر کلاینت شما چیزی را
پاکسازی میکند، مطمئن شوید این دسترسی را دارد — وگرنه حذفها بیصدا با 403 رد میشوند.دیدن دسترسیهای کلید
GET /capabilities — فقط یک کلید معتبر میخواهد و هیچ دسترسی خاصی لازم ندارد. تنها اندپوینت بدون scope است:
curl -s -H "X-API-Key: $KEY" \
https://serp.rasaad.ir/api/v1/operator/capabilities
دلیلش این است که باید بتوانید بپرسید با کلیدتان چه کارهایی میتوانید بکنید، حتی وقتی هنوز نمیدانید چه دسترسیهایی دارید.
مالکیت داده
هر هدف، پرسوجو و نتیجه با یک owner ثبت میشود. یک کلید همیشه و بدون استثنا فقط ردیفهای
خودش را میبیند و هیچ پارامتری این را تغییر نمیدهد.
| میخواهید | جواب |
|---|---|
| نتایج همهٔ مشتریان یک کلید | بله، همه متعلق به یک owner هستند |
نتایج owner دیگر | هرگز |
| پارامتری برای دیدن دادهٔ دیگران | وجود ندارد |
task_id یا target_id یک حساب دیگر 404 میگیرد، نه 403. وجودش فاش نمیشود؛
403 تأیید میکرد که این شناسه واقعی است. این تصمیم آگاهانه است، نه یک اتفاق — به آن
در بخش بعد برمیگردیم.سه شکست، سه راهحل متفاوت
این مهمترین بخش این صفحه است. سه کد وضعیت نزدیک به هم، سه معنای کاملاً متفاوت دارند:
| وضعیت | یعنی چه | کار کلاینت |
|---|---|---|
401 | کلید نفرستادهاید، ناشناخته است یا باطل شده | کلید را درست کنید. تلاش دوباره هرگز کمک نمیکند |
403 | کلید معتبر است ولی دسترسی لازم را ندارد | مجوز را اصلاح کنید. هرگز 401 برنگردانید |
404 | مال شما نیست، یا اصلاً وجود ندارد | برای هر دو یکسان، و این عمدی است |
بدنهٔ خطا همیشه یک فیلد detail دارد:
{"detail": "missing scope targets:write"}
در پاسخهای خطا فیلد ok وجود ندارد. بر اساس کد وضعیت HTTP تصمیم بگیرید، نه متن بدنه.
403 را هرگز به 401 تبدیل نکنید. کلاینتی که 401 را با همان کلید دوباره تلاش
کند، تا ابد در یک حلقه میچرخد؛ چون تلاش مجدد هرگز کلید را معتبر نمیکند. اگر در کد
خودتان همهٔ خطاهای دسترسی را یکی میگیرید، مطمئن شوید 403 به مسیر «اصلاح مجوز» میرود
نه مسیر «تلاش دوباره».| پیادهسازی | رفتار درست |
|---|---|
| تلاش دوباره با همان کلید | 429، 503، خطای شبکه |
| توقف و نمایش خطا | 401، 403 |
| پایان کار، بدون نمایش جزئیات | 404 |
404 همیشه یک پاسخ یکسان است. نمیتوانید از روی آن تشخیص دهید شناسه وجود ندارد یا
مال دیگری است — و همین هدف است. اگر لازم دارید بدانید، از پشتیبانی
بپرسید، نه از تفاوت کد وضعیت.ساختار پاسخها
| موضوع | رفتار |
|---|---|
| موفقیت | همراه داده، ok: true میآید |
| خطا | {"detail": "..."} بدون فیلد ok |
| هر دو | هدر Cache-Control: no-store |
| نوع محتوا | application/json مگر خلافش گفته شود |
Cache-Control: no-store روی همهٔ پاسخها هست — چه موفق چه ناموفق. اگر کش خودتان نتیجه را نگه دارد، مسئولیت بهروز بودنش با شماست.