> ## Documentation Index
> Fetch the complete documentation index at: https://sorank.com/llms.txt
> Use this file to discover all available pages before exploring further.

# استخدام واجهة SORANK API العامة

> المصادقة والحصص وإنشاء المحتوى ومتابعة العمليات.

## عنوان الإنتاج وأول طلب

سجّل الدخول إلى التطبيق على `https://app.sorank.com`. تُدار المفاتيح على `https://app.sorank.com/settings/api`، والعنوان الأساسي لطلبات REST هو `https://app.sorank.com/api/public/v1`. لا تحتاج إلى إنشاء خادم API.

1. افتح **الإعدادات ← الوصول إلى API** وأنشئ مفتاحًا باسم وموقع واحد على الأقل وصلاحية **عرض المواقع** (`profiles:read`) وتاريخ انتهاء. أضف **عرض الحصص** (`quotas:read`) للطلب الثاني.
2. انسخ المفتاح الكامل فورًا؛ يظهر مرة واحدة فقط. البادئة المعروضة لاحقًا لا تكفي للمصادقة.
3. اختر مثالًا. في cURL أدخل المفتاح عند المطالبة به. في JavaScript أو Go اضبط `SORANK_API_KEY` في بيئة عملية الخادم. تتضمن علامة تبويب HTML خادم Node.js محليًا: شغّله والمفتاح في بيئته ثم افتح `http://127.0.0.1:3100`. لا تضع المفتاح داخل HTML أو JavaScript في المتصفح. أضف مصادقة تطبيقك قبل استخدام هذا النمط خارج العرض المحلي.

> [tab] cURL
>
> ```sh theme={null}
> printf 'SORANK API key: '; read -rs SORANK_API_KEY; echo
> curl --fail-with-body --silent --show-error \
>   -H "Authorization: Bearer $SORANK_API_KEY" \
>   'https://app.sorank.com/api/public/v1/profiles'
>
> # Copy an authorized data[].id from the response above.
> PROFILE_ID='<profile_id>'
> curl --fail-with-body --silent --show-error \
>   -H "Authorization: Bearer $SORANK_API_KEY" \
>   "https://app.sorank.com/api/public/v1/profiles/$PROFILE_ID/quotas"
> unset SORANK_API_KEY
> ```

> [tab] HTML
>
> ```html index.html theme={null}
> <button id="load">Load my SORANK websites</button>
> <pre id="result"></pre>
> <script>
>   document.querySelector('#load').addEventListener('click', async () => {
>     const output = document.querySelector('#result');
>     try {
>       // Your own backend authenticates to SORANK. Keep the key out of this page.
>       const response = await fetch('/api/sorank/profiles');
>       if (!response.ok) throw new Error(`HTTP ${response.status}`);
>       const profiles = await response.json();
>       output.textContent = JSON.stringify(profiles, null, 2);
>     } catch (error) {
>       output.textContent = String(error);
>     }
>   });
> </script>
> ```
>
> ```js server.mjs theme={null}
> // Local demo backend. Run with SORANK_API_KEY in the environment.
> import { createServer } from 'node:http';
> import { readFile } from 'node:fs/promises';
>
> const key = process.env.SORANK_API_KEY;
> if (!key) throw new Error('Set SORANK_API_KEY before running this file');
> const html = await readFile(new URL('./index.html', import.meta.url));
>
> createServer(async (request, response) => {
>   if (request.method === 'GET' && request.url === '/') {
>     response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
>     response.end(html);
>     return;
>   }
>   if (request.method === 'GET' && request.url === '/api/sorank/profiles') {
>     try {
>       const upstream = await fetch('https://app.sorank.com/api/public/v1/profiles', {
>         headers: { Authorization: `Bearer ${key}` },
>       });
>       response.writeHead(upstream.status, {
>         'Content-Type': 'application/json; charset=utf-8',
>         'Cache-Control': 'no-store',
>       });
>       response.end(await upstream.text());
>     } catch {
>       response.writeHead(502).end('Upstream unavailable');
>     }
>     return;
>   }
>   response.writeHead(404).end('Not found');
> }).listen(3100, '127.0.0.1');
> // Open http://127.0.0.1:3100 after starting this server.
> ```

> [tab] JavaScript
>
> ```js profiles.mjs theme={null}
> // Run on Node.js 20+ with SORANK_API_KEY set in the process environment.
> const token = process.env.SORANK_API_KEY;
> if (!token) throw new Error('Set SORANK_API_KEY before running this file');
> const base = 'https://app.sorank.com/api/public/v1';
>
> async function get(path) {
>   const response = await fetch(`${base}${path}`, {
>     headers: { Authorization: `Bearer ${token}` },
>   });
>   const body = await response.json();
>   if (!response.ok) throw new Error(`HTTP ${response.status}: ${JSON.stringify(body)}`);
>   return body;
> }
>
> const profiles = await get('/profiles');
> console.log(profiles);
> const profileId = profiles.data[0]?.id;
> if (profileId) console.log(await get(`/profiles/${encodeURIComponent(profileId)}/quotas`));
> ```

> [tab] Go
>
> ```go main.go theme={null}
> package main
>
> import (
>   "encoding/json"
>   "fmt"
>   "io"
>   "net/http"
>   "os"
>   "time"
> )
>
> func main() {
>   key := os.Getenv("SORANK_API_KEY")
>   if key == "" { panic("set SORANK_API_KEY before running this file") }
>   client := &http.Client{Timeout: 15 * time.Second}
>   get := func(path string) []byte {
>     req, err := http.NewRequest(http.MethodGet, "https://app.sorank.com/api/public/v1"+path, nil)
>     if err != nil { panic(err) }
>     req.Header.Set("Authorization", "Bearer "+key)
>     resp, err := client.Do(req)
>     if err != nil { panic(err) }
>     defer resp.Body.Close()
>     body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
>     if err != nil { panic(err) }
>     if resp.StatusCode != http.StatusOK { panic(fmt.Sprintf("HTTP %d: %s", resp.StatusCode, body)) }
>     return body
>   }
>
>   body := get("/profiles")
>   fmt.Println(string(body))
>   var profiles struct { Data []struct { ID string `json:"id"` } `json:"data"` }
>   if err := json.Unmarshal(body, &profiles); err != nil { panic(err) }
>   if len(profiles.Data) > 0 {
>     fmt.Println(string(get("/profiles/"+profiles.Data[0].ID+"/quotas")))
>   }
> }
> ```

تضم الاستجابة المواقع المسموح بها في `data`. استخدم قيمة `data[].id` بوصفها `profile_id` للطلبات اللاحقة. إذا كانت القائمة فارغة فتحقق من المواقع المحددة وصلاحياتك. تقرأ أمثلة cURL وJavaScript وGo حصة أول ملف شخصي أيضًا مع `quotas:read`.

يوضح **مرجع API العام** في تنقل المطورين المسارات والصلاحيات والاستجابات.

## المصادقة

أنشئ مفتاح API شخصيًا من **الإعدادات ← الوصول إلى API** للبرامج النصية، أو فوّض عميل OAuth باستخدام PKCE S256. حدّد الملفات الشخصية والنطاقات اللازمة فقط. أرسل الرمز بترويسة Bearer. لا تصلح ملفات تعريف الارتباط أو رموز Supabase لهذه الواجهة.

للتطبيقات التي تعمل نيابة عن المستخدم، ابدأ من `https://app.sorank.com/.well-known/oauth-protected-resource/api/public/v1` و`https://app.sorank.com/.well-known/oauth-authorization-server`. اكتشف نقاط OAuth المعلنة وسجّل عميلًا عامًا عند الحاجة واستخدم authorization code مع PKCE S256 والنطاقات اللازمة والمورد المحدد `https://app.sorank.com/api/public/v1`. رمز OAuth الخاص بـMCP لا يصلح لطلبات REST.

## الأذونات والحصة

افحص `getQuotas` قبل الإنشاء. يتطلب المثال `profiles:read` و`quotas:read` و`calendar:read` و`calendar:write` و`articles:read` و`articles:generate` و`articles:publish`. ويتطلب إنشاء التقويم صلاحيات الإنشاء والنشر أيضًا. تتشارك الواجهة والتطبيق وMCP الحصة نفسها؛ لا تزيدها المفاتيح الإضافية.

صلاحيات القراءة **عرض المنتجات** (`products:read`) و**عرض التحليلات** (`analytics:read`) و**عرض الظهور في الذكاء الاصطناعي** (`geo:read`) و**عرض الإعدادات** (`settings:read`) لا تستهلك الحصة أبدًا. أما **تعديل الإعدادات** (`settings:write`) فتغيّر الإعدادات نفسها التي يغيّرها التطبيق؛ امنحها فقط عندما يلزم أن يغيّرها تكامل ما.

## التحقق من الأهلية

تتوفر واجهة API بعد إكمال الإعداد الأولي في Sorank وعندما يكون للموقع اشتراك صالح. يستخدم متعاون الوكالة اشتراك مالك الموقع. تتحقق Sorank من ذلك عند إنشاء مفتاح أو الموافقة على اتصال OAuth، ثم مع كل طلب.

* يعيد `listProfiles` المواقع المؤهلة فقط.
* يعيد الإعداد الأولي غير المكتمل `403 onboarding_required`.
* يعيد الموقع الذي لا يملك اشتراكًا صالحًا `402 subscription_required`، مع `details.reason` (`paused` أو `past_due` أو `incomplete` أو `inactive`) عند معرفته.

أكمل الإعداد الأولي أو حدّث الاشتراك في **الإعدادات ← الفوترة**. يعمل مفتاحك الحالي مجددًا بدءًا من الطلب التالي.

## قراءة المنتجات والتحليلات والظهور في الذكاء الاصطناعي والإعدادات

تعيد هذه القراءات ما يعرضه لك تطبيق Sorank. ولا تبدأ أبدًا تحليلًا أو مزامنة أو إنشاء محتوى.

* **المنتجات** (`products:read`): يتصفح `listProducts` كتالوج المنتجات بمؤشر. بعد `409 product_catalog_cursor_stale` ابدأ من الصفحة الأولى.
* **التحليلات** (`analytics:read`): الأداء والاستعلامات والصفحات والدول والأجهزة من Search Console، وزيارات Google Analytics القادمة من مساعدي الذكاء الاصطناعي. الحقلان `start` و`end` (`YYYY-MM-DD`) إلزاميان. كل استدعاء يقرأ من Google مباشرة وله حد معدل أقل. اربط Google في Sorank أولًا؛ وإلا تتلقى `409 gsc_not_connected` أو `409 ga_not_connected`، و`403 google_access_lost` إذا فُقد الوصول.
* **الظهور في الذكاء الاصطناعي** (`geo:read`): إحصاءات GEO وتطور الإشارات والجدول الزمني للاستشهادات والمصادر المستشهِدة والمنافسون المذكورون في تحليل.
* **الإعدادات** (`settings:read`): يعيد `getSettings` الموقع وملخص النشاط مع `revision` الخاص به والمنافسين وإعدادات المقالات الافتراضية والمرئيات وقناة YouTube.

## تغيير الإعدادات

باستخدام `settings:write` يمكنك تغيير اسم النشاط ولون العلامة والسوق، واستبدال ملخص النشاط وقائمة المنافسين، وتغيير الإعدادات الافتراضية لمقالات التقويم ووضع الصور وصور الغلاف، وربط قناة YouTube أو إيقافها مؤقتًا أو فصلها. لا يمكن تغيير عنوان الموقع عبر واجهة API (`400 field_not_writable`).

يتطلب كل تغيير `Idempotency-Key`. تتضمن الاستجابة الإعدادات الناتجة وإيصالًا وقائمة `effects` بالعمليات التي بدأت في الخلفية، كما في التطبيق: `geo_questions_regeneration_queued` أو `backlink_refresh_queued` أو `calendar_slots_shifted` أو `image_gallery_analysis_queued` أو `youtube_sync_queued` أو `youtube_purge_queued`.

لاستبدال ملخص النشاط، أرسل `expected_revision` المقروء من `getSettings`. إذا تغيّر الملخص في الأثناء، تتلقى `409 business_brief_revision_conflict`: اقرأ الإعدادات مجددًا قبل إعادة المحاولة.

## تجربة مسار REST كامل

ينشئ هذا المثال المتقدم محتوى وقد يستهلك الحصة. استبدل المعرّفات والتواريخ المستقبلية ومفاتيح منع تكرار الطلب وبيانات الاعتماد التجريبية قبل تشغيله. تستخدم تواريخ التقويم المنطقة الزمنية IANA المحددة. افحص `getCapabilities` قبل اختيار وجهة CMS أو الفئة.

```sh theme={null}
# استبدل معرفات UUID التجريبية بمعرفات حسابك.
API_ORIGIN="https://app.sorank.com"
PROFILE_ID="00000000-0000-4000-8000-000000000000"
ARTICLE_ID="11111111-1111-4111-8111-111111111111"
TOKEN="<PERSONAL_API_KEY>"

curl -H "Authorization: Bearer $TOKEN" "$API_ORIGIN/api/public/v1/profiles/$PROFILE_ID/quotas"
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: 22222222-2222-4222-8222-222222222222" \
  -d '{"settings":{"timezone":"Europe/Paris","local_time":"09:30"},"start_date":"2026-11-01","end_date":"2026-12-31","subjects":[{"subject":"Sustainable travel checklist"}]}' \
  "$API_ORIGIN/api/public/v1/profiles/$PROFILE_ID/calendar"
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: 33333333-3333-4333-8333-333333333333" \
  -d '{"seeds":[{"keyword":"sustainable travel tips","title":"Practical sustainable travel"}]}' \
  "$API_ORIGIN/api/public/v1/profiles/$PROFILE_ID/article-generations"
curl -H "Authorization: Bearer $TOKEN" "$API_ORIGIN/api/public/v1/operations/<OPERATION_UUID>"
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: 44444444-4444-4444-8444-444444444444" \
  -d '{"target":"draft"}' "$API_ORIGIN/api/public/v1/profiles/$PROFILE_ID/articles/$ARTICLE_ID/publications"
```

تعرض مجموعة مرجع API العامة جميع العمليات والحقول والاستجابات والأخطاء.

## المتابعة وإعادة المحاولة

يتطلب كل تغيير `Idempotency-Key` فريدًا. استخدم المفتاح نفسه عند تكرار الأمر نفسه. اقرأ `data.receipt.operation_id` ثم استعلم عن `getOperation` حتى `succeeded` أو `failed` أو `verification_pending`. قبول الإيصال لا يعني اكتمال النشر. تعيد دفعة إنشاء المقالات `202` مع إيصال في الحالة `succeeded` مسبقًا؛ تابع كل مقال مذكور في `selection.accepted_ids` باستخدام `getArticle`. يُعاد التحقق من التفويض في كل استعلام، لذا يسري الإلغاء فورًا.

## معالجة الأخطاء

لكل خطأ الشكل نفسه في REST وMCP: `{"error": {"code", "message", "request_id", "retryable", "details"}}`. اعتمد في منطقك على `code` لا على الرسالة. أعد المحاولة فقط عندما تكون قيمة `retryable` هي `true`، بعد `Retry-After` عند وجوده.

* `401 invalid_credential`: بيانات اعتماد غير صالحة أو منتهية أو ملغاة.
* `403 missing_scope` أو `403 onboarding_required`؛ `402 subscription_required` أو `402 insufficient_quota`.
* `404 not_found`: المورد غير موجود أو غير متاح لبيانات الاعتماد هذه.
* `409`: تعارض مثل `idempotency_conflict`؛ `429 rate_limited`: انتظر `Retry-After`.
* `503 auth_state_unavailable`: يُرفض الوصول حتى تتعافى حالة التفويض؛ أعد المحاولة لاحقًا.

يسرد مرجع واجهة API العامة رموز كل عملية. شارك `code` و`request_id` مع الدعم، ولا ترسل الرمز المميز أبدًا.

لتوصيل مساعد، راجع دليل MCP.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.