--- title: "Konkurenci: ranking (`getRanking`)" source: https://docs.senuto.com/modules/rank_tracker/rt-competitors-getRanking api: POST /api/rank_tracker/reports/competitors/getRanking --- # Konkurenci: ranking (`getRanking`) **`POST /api/rank_tracker/reports/competitors/getRanking`** Zwraca ranking domeny projektu oraz jej konkurentów według statystyk pozycji i widoczności — porównanie dwóch snapshotów pomiarowych. Dla każdej domeny otrzymujesz zestaw około 26 metryk (rozkład pozycji TOP3/TOP10/TOP50, widoczność organiczna, potencjał, budżet itd.), a każda metryka zawiera wartość starszą, nowszą, różnicę i zmianę procentową. Wynik jest stronicowany po domenach. | Domena | ID | Domena projektu | Widoczność org. | Średnia pozycja | TOP3 | TOP10 | TOP50 | Zyskane | Utracone | Potencjał org. | Koszt PPC | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | pies.pl | 87944 | true | 0 | 50 | 0 | 0 | 0 | 0 | 0 | 1100.0399999999984 | 0 | | fajnyzwierzak.pl | 55578 | | | 45.99 | | | 22 | 14 | 8 | | | _wiersze z data.data — podwójna koperta: domena projektu (project_domain: true) i konkurent. Pokazano identyfikatory i najważniejsze metryki (recent_value); komplet ~24 metryk (starsza/nowsza/różnica/%) jest w JSON i sekcji „Struktura odpowiedzi”._ --- ## Żądanie `POST` `/api/rank_tracker/reports/competitors/getRanking` Nagłówki: `Authorization: Bearer `, `Content-Type: application/json`. ### Struktura żądania **Podstawowy** ```jsonc filename="żądanie-podstawowe.jsonc" { "project_id": null, "date_min": "2026-06-20", "date_max": "2026-06-29" } ``` **Rozszerzony** ```jsonc filename="żądanie-rozszerzone.jsonc" { "project_id": null, "date_min": "2026-06-20", "date_max": "2026-06-29", "limit": 2 } ``` **cURL** ```bash curl --location --request POST 'https://api.senuto.com/api/rank_tracker/reports/competitors/getRanking' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer $YOUR_TOKEN_HERE' \ --data-raw '{ "project_id": null, "date_min": "2026-06-20", "date_max": "2026-06-29", "limit": 2 }' ``` ### Parametry ```ts type GetRankingRequest = { /** * **Wymagane** (walidator `CompetitorsValidator`). ID projektu Rank Tracker. * Musi należeć do użytkownika — w przeciwnym razie `418` z komunikatem `Unauthorized access`. * Realny `project_id` pobierzesz z `POST /api/rank_tracker/management/projects/getMyActiveProjects`. */ project_id: number; /** * **Wymagane**. Początek zakresu porównania w formacie `YYYY-MM-DD`. Nie może być późniejszy niż dziś * ani późniejszy niż `date_max`. Uwaga: jako starszy snapshot API bierze **poprzedni dostępny pomiar** * względem `date_max` (patrz pole `dates` w odpowiedzi), więc `date_min` nie zawsze będzie datą, * z którą realnie porównano wyniki. */ date_min: string; /** * **Wymagane**. Koniec zakresu porównania w formacie `YYYY-MM-DD` (≤ dziś, ≥ `date_min`). * Odpowiada `dates.last_date` w odpowiedzi. */ date_max: string; /** * Rozmiar strony paginacji — liczonej po **domenach** (projekt + konkurenci). * @default 10 */ limit?: number; /** * Numer strony paginacji. * @default 1 */ page?: number; } export default GetRankingRequest ``` > **Ostrzeżenie:** > **Znany bug walidatora:** gdy `date_min` jest późniejsze niż `date_max`, komunikaty błędów z `DateRangeRules` są **odwrócone** (komunikat o `date_min` dotyczy `date_max` i odwrotnie). Interpretuj wtedy błąd „na krzyż". > **Ostrzeżenie:** > **Pułapki tego endpointu:** > > 1. **Podwójna koperta** — zewnętrzne pole `data` zawiera **własny obiekt** z polami `{success, rows, dates, data[]}`. Lista domen siedzi więc w `data.data`, a nie bezpośrednio w `data`. > 2. **`dates` pokazuje faktycznie porównane snapshoty, a nie Twój zakres.** `last_date` odpowiada `date_max`, ale `previous_data` to **poprzedni dostępny pomiar** — w zwalidowanym przykładzie `2026-06-28`, mimo że w żądaniu podano `date_min: 2026-06-20`. Każde z pól to obiekt `{timestamp, formatted}`. > 3. **Wiersz domeny projektu różni się od wierszy konkurentów** — ma `project_domain: true` i `selectable: false`, natomiast wiersze konkurentów mają zamiast tego tablice `categories[]` i `technologies[]`. > 4. **Duplikaty i równoległe konwencje w `statistics`** — metryki `out_50` i `out50` występują jednocześnie, podobnie jak przedziały w dwóch notacjach (`top3`/`top4_10`/`top11_20`/`top21_50` obok `1_3`/`4_10`/`11_20`/`21_50`). > 5. **Paginacja działa na poziomie zewnętrznym** i liczy **domeny** (`count: 4` = domena projektu + 3 konkurentów), a nie frazy. ## Odpowiedź Zwróć uwagę na **podwójną kopertę**: zewnętrzne `data` to obiekt z własnymi polami `success`, `rows` (łączna liczba domen), `dates` (faktycznie porównane snapshoty) i `data` (lista domen). Metadane `pagination` leżą na poziomie zewnętrznym, obok koperty. W skróconym przykładzie poniżej drugi wiersz (konkurent) pokazuje tylko część metryk — w oryginalnej odpowiedzi każdy wiersz zawiera **komplet tych samych około 26 metryk** co wiersz domeny projektu, każda w formacie `{older_value, recent_value, diff, percent}`. **Skrócona** ```json filename="przykładowa-odpowiedź (skrócona)" { "success": true, "data": { "success": true, "rows": 4, "dates": { "last_date": { "timestamp": 1782691200, "formatted": "2026-06-29" }, "previous_data": { "timestamp": 1782604800, "formatted": "2026-06-28" } }, "data": [ { "id": 87944, "domain": "pies.pl", "project_domain": true, "selectable": false, "statistics": { "organic_potential": { "older_value": 1100.0399999999984, "recent_value": 1100.0399999999984, "diff": 0, "percent": 0 }, "avg_pos": { "older_value": 50, "recent_value": 50, "diff": 0, "percent": 0 }, "out_50": { "older_value": 94, "recent_value": 94, "diff": 0, "percent": 0 } } } ] }, "pagination": { "page_count": 2, "current_page": 1, "has_next_page": true, "has_prev_page": false, "count": 4, "limit": 2 } } ``` **Pełna** ```json filename="przykładowa-odpowiedź (zwalidowana, 200; 2 z 4 domen)" { "success": true, "data": { "success": true, "rows": 4, "dates": { "last_date": { "timestamp": 1782691200, "formatted": "2026-06-29" }, "previous_data": { "timestamp": 1782604800, "formatted": "2026-06-28" } }, "data": [ { "id": 87944, "domain": "pies.pl", "project_domain": true, "selectable": false, "statistics": { "organic_potential": { "older_value": 1100.0399999999984, "recent_value": 1100.0399999999984, "diff": 0, "percent": 0 }, "ad_keywords": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "ad_pos_avg": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "ad_visibility": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "lost": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "no_change": { "older_value": 94, "recent_value": 94, "diff": 0, "percent": 0 }, "out_50": { "older_value": 94, "recent_value": 94, "diff": 0, "percent": 0 }, "avg_pos": { "older_value": 50, "recent_value": 50, "diff": 0, "percent": 0 }, "ppc_cost": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "top10": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "top11_20": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "top21_50": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "top3": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "top4_10": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "top50": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "organic_visibility": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "wins": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "1_3": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "4_10": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "11_20": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "21_50": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "out50": { "older_value": 94, "recent_value": 94, "diff": 0, "percent": 0 }, "budget": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 }, "organic_potential_coverage": { "older_value": 0, "recent_value": 0, "diff": 0, "percent": 0 } } }, { "id": 55578, "domain": "fajnyzwierzak.pl", "categories": [], "technologies": [], "statistics": { "lost": { "older_value": 8, "recent_value": 8, "diff": 0, "percent": 0 }, "no_change": { "older_value": 72, "recent_value": 72, "diff": 0, "percent": 0 }, "out_50": { "older_value": 75, "recent_value": 72, "diff": -3, "percent": -0.04 }, "avg_pos": { "older_value": 46.33, "recent_value": 45.99, "diff": -0.34, "percent": -0.0073 }, "top11_20": { "older_value": 4, "recent_value": 2, "diff": -2, "percent": -0.5 }, "top21_50": { "older_value": 15, "recent_value": 20, "diff": 5, "percent": 0.3333 }, "top50": { "older_value": 19, "recent_value": 22, "diff": 3, "percent": 0.1579 }, "wins": { "older_value": 14, "recent_value": 14, "diff": 0, "percent": 0 } } } ] }, "pagination": { "page_count": 2, "current_page": 1, "has_next_page": true, "has_prev_page": false, "count": 4, "limit": 2 } } ``` ### Struktura odpowiedzi ```ts type GetRankingResponse = { /** Flaga przetworzenia żądania (poziom zewnętrzny) */ success: boolean; /** Wewnętrzna koperta — uwaga na podwójne zagnieżdżenie */ data: { /** Flaga przetworzenia (poziom wewnętrzny) */ success: boolean; /** Łączna liczba domen w rankingu (projekt + konkurenci) */ rows: number; /** * Faktycznie porównane snapshoty. `last_date` = `date_max` z żądania; * `previous_data` = poprzedni dostępny pomiar (niekoniecznie `date_min`!). */ dates: { last_date: SnapshotDate; previous_data: SnapshotDate; }; /** Wiersze rankingu — jedna pozycja na domenę */ data: RankingRow[]; }; /** Paginacja na poziomie zewnętrznym; `count` = liczba domen */ pagination: { page_count: number; current_page: number; has_next_page: boolean; has_prev_page: boolean; count: number; limit: number | null; }; } type SnapshotDate = { /** Uniksowy znacznik czasu snapshotu */ timestamp: number; /** Data w formacie YYYY-MM-DD */ formatted: string; } type RankingRow = { /** ID projektu (dla domeny projektu) lub ID konkurenta */ id: number; /** Nazwa domeny */ domain: string; /** `true` tylko w wierszu domeny projektu */ project_domain?: boolean; /** `false` w wierszu domeny projektu (nie można jej odznaczyć) */ selectable?: boolean; /** Tylko w wierszach konkurentów */ categories?: string[]; /** Tylko w wierszach konkurentów */ technologies?: string[]; /** * Około 26 metryk: organic_potential, ad_keywords, ad_pos_avg, ad_visibility, lost, no_change, * out_50, avg_pos, ppc_cost, top10, top11_20, top21_50, top3, top4_10, top50, organic_visibility, * wins, 1_3, 4_10, 11_20, 21_50, out50, budget, organic_potential_coverage. * Uwaga: `out_50`/`out50` to duplikaty, a przedziały pozycji występują równolegle * w notacji `topX` i `X_Y`. */ statistics: Record; } type MetricComparison = { /** Wartość w starszym snapshocie (`dates.previous_data`) */ older_value: number; /** Wartość w nowszym snapshocie (`dates.last_date`) */ recent_value: number; /** Różnica: recent_value - older_value */ diff: number; /** Zmiana procentowa jako ułamek (np. -0.04 = -4%) */ percent: number; } export default GetRankingResponse ``` ## Błędy ```ts type ErrorResponse = { success: false; data: { error: { /** np. invalid_data, unknown */ type: string; message: string; /** pole -> reguła -> komunikat (dla invalid_data) */ params?: Record>; }; }; } export default ErrorResponse ``` > **Błąd:** > Błędy walidacji zwracane są jako **`418`**. Brak `project_id`, `date_min` lub `date_max` → `invalid_data`. Daty muszą być w formacie `YYYY-MM-DD`, nie późniejsze niż dziś, a `date_min` ≤ `date_max` — przy odwróconym zakresie pamiętaj o **zamienionych komunikatach** z `DateRangeRules`. Cudzy lub nieistniejący `project_id` → `418` z `Unauthorized access`, a nie `404`. ## Powiązane akcje - `getRanking` — ranking domen wg statystyk pozycji/widoczności dla dwóch snapshotów (ta strona) - `getMatrix` — macierz fraza × domena z pozycjami projektu i konkurentów w dwóch datach (`POST`, te same wymagane parametry) - Lista konkurentów projektu (domeny widoczne w obu raportach) pochodzi z `GET /api/rank_tracker/management/competitors/list`