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

# שימוש ב-API הציבורי של 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** עבור סקריפטים, או אשרו לקוח OAuth עם PKCE S256. הגבילו את הפרופילים וה-scopes לנדרש. שלחו את האסימון כ-Bearer. קובצי Cookie של הדפדפן ואסימוני Supabase אינם מתאימים ל-API הזה.

לאפליקציה הפועלת בשם משתמש, התחילו ב-`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`. יצירת לוח שנה דורשת גם הרשאות יצירה ופרסום. האפליקציה, REST ו-MCP חולקים מכסה אחת; מפתחות נוספים אינם מגדילים אותה.

הרשאות הקריאה **הצגת מוצרים** (`products:read`), **הצגת ניתוחים** (`analytics:read`), **הצגת נראות AI** (`geo:read`) ו-**הצגת הגדרות** (`settings:read`) לעולם אינן צורכות מכסה. **עריכת הגדרות** (`settings:write`) משנה את אותן הגדרות כמו האפליקציה; העניקו אותה רק אם אינטגרציה צריכה לשנות אותן.

## בדיקת זכאות

ה-API זמין לאחר שהשלמתם את תהליך ההצטרפות ב-Sorank ולאתר יש מנוי בתוקף. משתף פעולה של סוכנות משתמש במנוי של בעל האתר. Sorank בודקת זאת ביצירת מפתח או באישור חיבור OAuth, ושוב בכל בקשה.

* `listProfiles` מחזירה רק אתרים זכאים.
* תהליך הצטרפות שלא הושלם מחזיר `403 onboarding_required`.
* אתר ללא מנוי בתוקף מחזיר `402 subscription_required`, עם `details.reason` (`paused`, `past_due`, `incomplete` או `inactive`) כשהוא ידוע.

השלימו את תהליך ההצטרפות או עדכנו את המנוי ב-**הגדרות → חיוב**. המפתח הקיים שלכם יפעל שוב מהבקשה הבאה.

## קריאת מוצרים, ניתוחים, נראות AI והגדרות

קריאות אלה מחזירות את מה שאפליקציית Sorank מציגה לכם. הן לעולם אינן מפעילות ניתוח, סנכרון או יצירת תוכן.

* **מוצרים** (`products:read`): `listProducts` עוברת על קטלוג המוצרים עם סמן. לאחר `409 product_catalog_cursor_stale`, התחילו שוב מהעמוד הראשון.
* **ניתוחים** (`analytics:read`): ביצועים, שאילתות, עמודים, מדינות ומכשירים מ-Search Console, ותנועה מ-Google Analytics שמגיעה מעוזרי AI. `start` ו-`end` (`YYYY-MM-DD`) הם חובה. כל קריאה פונה ל-Google בזמן אמת ומוגבלת בקצב נמוך יותר. חברו קודם את Google ב-Sorank; אחרת תקבלו `409 gsc_not_connected` או `409 ga_not_connected`, ו-`403 google_access_lost` אם הגישה אבדה.
* **נראות AI** (`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.