--- title: "Historia fraz: wzrosty (`getWins`)" source: https://docs.senuto.com/modules/visibility_analysis/va-history-keywords-getWins api: POST /api/visibility_analysis/reports/history/keywords/getWins --- # Historia fraz: wzrosty (`getWins`) **`POST /api/visibility_analysis/reports/history/keywords/getWins`** Zwraca frazy, których pozycja **poprawiła się** między `date_min` a `date_max` (tryb `MODE_INCREASE` tego samego komponentu danych co `getData`). W danych poprawa oznacza **ujemny** `statistics.position.diff` — np. przejście z pozycji 3 na 1 daje `diff: -2`. Struktura żądania i odpowiedzi jest identyczna jak w `getData`; zmienia się wyłącznie zestaw zwracanych wierszy. > **Ostrzeżenie:** > **Ta akcja zawiera też frazy nowo pozyskane — i nie jest symetryczna wobec `getLosses`.** > > Frazy, na które domena nie rankowała na `date_min`, a rankuje na `date_max`, **trafiają również tutaj** — jako skok z pozycji `51` (sentinel „poza TOP50"), np. `{"current": 26, "previous": 51, "diff": -25}`. Nie są odfiltrowane. Jeśli potrzebujesz wyłącznie ruchu **wewnątrz** TOP50, odrzuć wiersze z `statistics.position.previous === 51`. > > Siostrzana [`getLosses`](/modules/visibility_analysis/va-history-keywords-getLosses) działa **odwrotnie** — frazy utracone są z niej wykluczone i występują tylko w [`getLost`](/modules/visibility_analysis/va-history-keywords-getLost). Konsekwencje przy liczeniu: > > - **Pełny obraz zysków** = samo `getWins`. Doklejenie [`getAcquired`](/modules/visibility_analysis/va-history-keywords-getAcquired) **zdubluje** wiersze — to podzbiór tej akcji. > - **Pełny obraz strat** wymaga sklejenia `getLosses` + `getLost` (te zbiory są rozłączne). > - Zestawianie `count` z `getWins` i `getLosses` porównuje dwie różnie zdefiniowane wielkości — zyski są zawyżone o pozyskane, straty zaniżone o utracone. > > Zachowanie może się w przyszłości ujednolicić — na dziś traktuj powyższe jako obowiązujący kontrakt. | Fraza | ID frazy | KID | Domena | Liczba słów | Pozycja | Pozycja poprz. | Zmiana pozycji | Wzrosty | Spadki | Bez zmian | Widoczność | Widoczność poprz. | Δ widoczności | Widoczność % | URL bieżący | URL poprzedni | URL zmiana | CPC | Wyszukiwania/mies. | Trend (12 mies.) | Szczyt trendu | Trudność | Snippety SERP | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | nie air max | 1421737 | 1344167bcc1aac3b96cfe7420ae6244e | zalando.pl | 3 | 1 | 3 | -2 | 0 | 0 | 0 | 21538 | 11825 | 9713 | 0.8214 | zalando.pl/obuwie/?q=nike+air+max | zalando.pl/obuwie/?q=nike+air+max | 0 | 0.83 | 60500 | [90500,74000,60500,74000,60500,60500,49500,40500,40500,49500,110000,74000] | | 57 | ["image_thumbs","people_also_ask","spell"] | | uggs buty | 5591577 | 4bb5c4ba39dc62e9d6d6e1a03dcb344a | zalando.pl | 2 | 1 | 4 | -3 | 1 | 0 | 0 | 21538 | 4295.5 | 17242.5 | 4.0141 | zalando.pl/obuwie/ugg/ | zalando.pl/obuwie/ugg/ | 0 | 0.58 | 60500 | [0,0,0,0,0,0,0,0,0,0,0,0] | | 52 | ["image_thumbs","people_also_ask"] | _zalando.pl, sort: widoczność malejąco — frazy, których pozycja się poprawiła (ujemny „diff”). Wszystkie pola wiersza (poza mapą historii `statistics.position.history`, która ma zmienne klucze-daty — jest w JSON i sekcji „Struktura odpowiedzi”)._ --- ## Żądanie `POST` `/api/visibility_analysis/reports/history/keywords/getWins` Nagłówki: `Authorization: Bearer `, `Content-Type: application/json`. ### Struktura żądania **Podstawowy** ```jsonc filename="żądanie-podstawowe.jsonc" { "domain": "zalando.pl", "fetch_mode": "topLevelDomain", "date_min": "2026-06-20", "date_max": "2026-06-29", "country_id": 1, "order": { "prop": "statistics.visibility.current", "dir": "desc" } } ``` **Rozszerzony** ```jsonc filename="żądanie-rozszerzone.jsonc" { "domain": "zalando.pl", "fetch_mode": "topLevelDomain", "date_min": "2026-06-20", "date_max": "2026-06-29", "country_id": 1, "order": { "prop": "statistics.visibility.current", "dir": "desc" }, "limit": 10, "page": 1 } ``` **cURL** ```bash curl --location --request POST 'https://api.senuto.com/api/visibility_analysis/reports/history/keywords/getWins' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer $YOUR_TOKEN_HERE' \ --data-raw '{ "domain": "zalando.pl", "fetch_mode": "topLevelDomain", "date_min": "2026-06-20", "date_max": "2026-06-29", "country_id": 1, "order": { "prop": "statistics.visibility.current", "dir": "desc" } }' ``` ### Parametry ```ts type HistoryKeywordsGetWinsRequest = { /** * **Wymagane**. Domena, subdomena, katalog lub URL do analizy — interpretowane zgodnie z `fetch_mode`. */ domain: string; /** * **Wymagane**. Sposób interpretacji `domain`. * - `topLevelDomain` — cała domena (najczęstszy przypadek) * - `subdomain` — pojedyncza subdomena * - `catalog` — ścieżka/katalog * - `url` — dokładny URL * * Wartość `domain` nie istnieje. */ fetch_mode: 'topLevelDomain' | 'subdomain' | 'catalog' | 'url'; /** * **Wymagane**. Początek zakresu dat, `YYYY-MM-DD`. Musi być nie późniejszy niż `date_max`. * Dostępne daty pobierzesz akcją `getDates`. */ date_min: string; /** * **Wymagane**. Koniec zakresu dat, `YYYY-MM-DD`. Musi być nie wcześniejszy niż `date_min`. */ date_max: string; /** * **Wymagane**. Id kraju (bazy danych). Polska = `1`. Nieznana wartość → `418` "Unknown country_id". */ country_id: number; /** * **Wymagane**. **Pojedyncza** dyrektywa sortowania (jeden obiekt — nie tablica). * `prop` to jedna z dozwolonych właściwości sortowalnych; `dir` to kierunek. * * Dozwolone wartości `prop`: * - `keyword` * - `statistics.position.current` * - `statistics.position.previous` * - `statistics.position.diff` * - `statistics.visibility.current` * - `statistics.visibility.previous` * - `statistics.visibility.diff` * - `statistics.difficulty.current` * - `statistics.searches.current` * - `statistics.cpc.current` * - `statistics.url.is_change` */ order: { prop: | 'keyword' | 'statistics.position.current' | 'statistics.position.previous' | 'statistics.position.diff' | 'statistics.visibility.current' | 'statistics.visibility.previous' | 'statistics.visibility.diff' | 'statistics.difficulty.current' | 'statistics.searches.current' | 'statistics.cpc.current' | 'statistics.url.is_change'; dir: 'asc' | 'desc'; }; /** * Liczba wierszy na stronę. * @default 10 */ limit?: number; /** * Numer strony. * @default 1 */ page?: number; } export default HistoryKeywordsGetWinsRequest ``` > **Ostrzeżenie:** > **`order` to pojedynczy obiekt** (`{ prop, dir }`) — nie tablica. Błędna wartość `prop` zwraca `418` `invalid_data` z komunikatem (dosłownym): `This value is not allow. Please use correct colum name`. > **Ostrzeżenie:** > Pięć parametrów jest **wymaganych**: **`domain`**, **`fetch_mode`**, **`date_min`**, **`date_max`** oraz **`country_id`**, a także pojedynczy obiekt **`order`**. Wartość `fetch_mode` musi być jedną z `topLevelDomain` / `subdomain` / `catalog` / `url` — wartość `domain` **nie istnieje**. `order` to **pojedynczy obiekt** `{ prop, dir }`, nie tablica. Pominięcie któregokolwiek wymaganego pola zwraca `418` z `invalid_data`; nieznane `country_id` → `418` z komunikatem `Unknown country_id`. ## Filtrowanie Opcjonalny parametr `filtering` (tablica grup filtrów) zawęża wyniki. Endpoint korzysta z **tego samego mechanizmu i tych samych kluczy filtrów** co `positions/getData` oraz `history/keywords/getData` — szczegóły i pełną listę kluczy znajdziesz na stronie [typów filtrów](/types/filter). ## Odpowiedź W przypadku powodzenia otrzymujesz `data` (tablicę wierszy z frazami, których pozycja się poprawiła) oraz `pagination`. Pozycja `51` to wartość specjalna oznaczająca frazę poza TOP50. **Skrócona** ```json filename="przykładowa-odpowiedź (skrócona)" { "success": true, "data": [ { "keyword_id": 1421737, "keyword": "nie air max", "statistics": { "position": { "current": 1, "previous": 3, "diff": -2 }, "visibility": { "current": 21538 } /* … */ } } ], "pagination": { "page_count": 4115, "current_page": 1, "has_next_page": true, "has_prev_page": false, "count": 8229, "limit": 2 } } ``` **Pełna** ```json filename="przykładowa-odpowiedź (200)" { "success": true, "data": [ { "keyword_id": 1421737, "kid": "1344167bcc1aac3b96cfe7420ae6244e", "domain": "zalando.pl", "keyword": "nie air max", "words_count": 3, "statistics": { "position": { "current": 1, "previous": 3, "diff": -2, "changes": { "wins": 0, "losses": 0, "no_changes": 0 }, "history": { "2026-06-24": { "position": 1, "has_serp": true } } }, "visibility": { "current": 21538, "previous": 11825, "diff": 9713, "percent": 0.8214, "history": null }, "url": { "current": "zalando.pl/obuwie/?q=nike+air+max", "previous": "zalando.pl/obuwie/?q=nike+air+max", "is_change": 0 }, "cpc": { "current": 0.83 }, "searches": { "current": 60500 }, "trends": { "history": [90500, 74000, 60500, 74000, 60500, 60500, 49500, 40500, 40500, 49500, 110000, 74000], "peak": null }, "difficulty": { "current": 57 }, "snippets": { "current": ["image_thumbs", "people_also_ask", "spell"] } } }, { "keyword_id": 5591577, "kid": "4bb5c4ba39dc62e9d6d6e1a03dcb344a", "domain": "zalando.pl", "keyword": "uggs buty", "words_count": 2, "statistics": { "position": { "current": 1, "previous": 4, "diff": -3, "changes": { "wins": 1, "losses": 0, "no_changes": 0 }, "history": { "2026-05-24": { "position": 4, "has_serp": true }, "2026-06-25": { "position": 1, "has_serp": true } } }, "visibility": { "current": 21538, "previous": 4295.5, "diff": 17242.5, "percent": 4.0141, "history": null }, "url": { "current": "zalando.pl/obuwie/ugg/", "previous": "zalando.pl/obuwie/ugg/", "is_change": 0 }, "cpc": { "current": 0.58 }, "searches": { "current": 60500 }, "trends": { "history": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "peak": null }, "difficulty": { "current": 52 }, "snippets": { "current": ["image_thumbs", "people_also_ask"] } } } ], "pagination": { "page_count": 4115, "current_page": 1, "has_next_page": true, "has_prev_page": false, "count": 8229, "limit": 2 } } ``` ### Struktura odpowiedzi ```ts type HistoryKeywordsGetWinsResponse = { /** `true` przy powodzeniu; przy błędzie `false` i koperta z `error` */ success: boolean; /** Zwrócone wiersze z frazami, których pozycja się poprawiła */ data: KeywordRow[]; /** Metadane paginacji */ pagination: { page_count: number; current_page: number; has_next_page: boolean; has_prev_page: boolean; count: number; limit: number; }; } type KeywordRow = { keyword_id: number; kid: string; domain: string; keyword: string; words_count: number; statistics: { /** Dla wzrostów `diff` jest ujemny (np. 3 → 1 daje diff = -2). Wartość 51 oznacza pozycję poza TOP50. */ position: { current: number; previous: number; diff: number; changes: { wins: number; losses: number; no_changes: number }; history: Record }; visibility: { current: number; previous: number; diff: number; percent: number; history: null }; /** `is_change` jest liczbowe: 0 lub 1 */ url: { current: string; previous: string; is_change: 0 | 1 }; cpc: { current: number }; searches: { current: number }; trends: { history: number[]; peak: number | null }; difficulty: { current: number }; snippets: { current: string[] }; }; } export default HistoryKeywordsGetWinsResponse ``` ## Błędy ```ts type ErrorResponse = { success: false; data: { error: { /** np. invalid_data */ type: string; message: string; /** pole -> reguła -> komunikat (dla invalid_data) */ params?: Record>; }; }; } export default ErrorResponse ``` > **Błąd:** > **`418`** zwracany jest również dla błędów walidacji. Brak wymaganego pola → `invalid_data`; nieznane `country_id` → `Unknown country_id`; błędny `order.prop` → `This value is not allow. Please use correct colum name`. > > **Znany błąd — odwrócony komunikat.** Przy `date_min > date_max` komunikat reguły `DateRangeRules` jest odwrócony i brzmi `"date_max must be less or equal than date_min"`. Należy go odczytywać jako niepowodzenie reguły zakresu dat, a nie jako dosłowną instrukcję. ## Powiązane akcje - [`getData`](/modules/visibility_analysis/va-history-keywords) — frazy w zakresie dat (`MODE_DATA`) - `getWins` — frazy, które poprawiły pozycje (ta strona) - [`getLosses`](/modules/visibility_analysis/va-history-keywords-getLosses) — frazy, które straciły pozycje (taka sama struktura żądania) - `getAcquired` / `getLost` — frazy nowo pozyskane / całkowicie utracone w zakresie (taka sama struktura żądania) - `getDates` — dostępne daty (POST, wymaga tylko `country_id`)