نتایج گوگل

احراز هویت

ارسال کلید API در هدر X-API-Key، جدول کامل دسترسی‌ها، مالکیت داده، و تفاوت دقیق ۴۰۱، ۴۰۳ و ۴۰۴ در API نتایج گوگل.

هر درخواستی که به این سرویس می‌زنید باید کلید 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:writePOST /queries
results:readGET /queries، GET /queries/{id}، GET /results، GET /results/{id} و خروجی‌های serpapi / markdown / raw آن
targets:readGET /targets، GET /targets/{id}، GET /targets/{id}/history
targets:writePOST /targets، PATCH /targets/{id}
targets:deleteDELETE /targets/{id}، POST /targets/{id}/enable و POST /targets/{id}/disable، و PATCH /targets/{id} وقتی بدنه enabled را ست می‌کند
usage:readGET /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 روی همهٔ پاسخ‌ها هست — چه موفق چه ناموفق. اگر کش خودتان نتیجه را نگه دارد، مسئولیت به‌روز بودنش با شماست.