--- title: "Pozycje: wzrosty (`getWins`)" source: https://docs.senuto.com/modules/visibility_analysis/va-positions-getWins api: POST /api/visibility_analysis/reports/positions/getWins --- # Pozycje: wzrosty (`getWins`) **`POST /api/visibility_analysis/reports/positions/getWins`** Zwraca frazy kluczowe, na których pozycja domeny **wzrosła** w analizowanym okresie (tryb pracy = `increase`). Dla każdej frazy zwracany jest ten sam zestaw statystyk co w `getData` (pozycja, widoczność, URL, CPC, liczba wyszukiwań, trendy, trudność, snippety SERP), ograniczony do fraz z poprawą pozycji. Domyślnie posortowane według wielkości zmiany. > **Ostrzeżenie:** > **Okres jest zaszyty na sztywno — ok. tygodnia.** Ta akcja nie przyjmuje żadnych parametrów dat. Porównywany jest najświeższy dostępny snapshot pozycji z najstarszym snapshotem z ostatniego tygodnia. Jeśli potrzebujesz własnego zakresu, użyj [`history/keywords/getWins`](/modules/visibility_analysis/va-history-keywords-getWins) z `date_min`/`date_max`. > > **Wyniki zawierają też frazy nowo pozyskane.** Fraza, na którą domena wcześniej nie rankowała, trafia tutaj jako skok z pozycji `51` (sentinel „poza TOP50") — patrz przykładowa odpowiedź powyżej: `{"current": 26, "previous": 51, "diff": -25}`. Aby zawęzić wynik do ruchu **wewnątrz** TOP50, odrzuć wiersze z `statistics.position.previous === 51`. > > Siostrzana [`getLosses`](/modules/visibility_analysis/va-positions-getLosses) zachowuje się **odwrotnie** — frazy utracone są z niej wykluczone, a rodzina `positions/*` nie ma odpowiednika `getLost`, więc w ogóle ich stąd nie pobierzesz. Porównywanie `count` obu akcji zestawia dwie różnie zdefiniowane wielkości. 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 | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | vans zamszowe | 225329 | 030d9b596c2058c07801e2ec87e85a37 | zalando.pl | 2 | 1 | 15 | -14 | 0 | 0 | 0 | 39.16 | 0 | 39.16 | 1 | zalando.pl/wszystkie/vans/?q=zamsz | zalando.pl/obuwie-damskie-tenisowki-trampki/vans/ | 1 | 0.77 | 110 | [140,110,110,140,170,90,70,50,70,70,170,140] | | 49 | ["image_thumbs"] | | lakierowane balerinki | 7856664 | 6a890673b2ee1ab050e233749ab4d170 | zalando.pl | 2 | 1 | 2 | -1 | 0 | 0 | 0 | 60.52 | 29.78 | 30.74 | 1.0322 | zalando.pl/obuwie-damskie-baleriny/?q=baleriny+lakierowane | zalando.pl/obuwie-damskie-baleriny/?q=baleriny+lakierowane | 0 | 0.94 | 170 | [170,210,320,260,260,140,140,140,210,170,110,90] | | 52 | ["image_thumbs","spell"] | | śniegowce sorel | 6508374 | 583abba99a976ff8f6276ef42ebab4c7 | zalando.pl | 2 | 1 | 3 | -2 | 1 | 0 | 0 | 676.4 | 204.25 | 472.15 | 2.3116 | zalando.pl/buty-zimowe/sorel/ | zalando.pl/buty-zimowe/sorel/ | 0 | 1.31 | 1900 | [2900,1900,390,140,110,90,140,720,1900,1900,5400,8100] | | 43 | ["image_thumbs","people_also_ask"] | | żółty sweterek rozpinany | 7993203 | 6c5f97d43b98e882ad45d9108af77e43 | zalando.pl | 3 | 1 | 2 | -1 | 0 | 0 | 0 | 60.52 | 24.53 | 35.99 | 1.4672 | zalando.pl/kardigany/_zolty/ | zalando.pl/kardigany/_zolty/ | 0 | 1.08 | 170 | [0,0,0,0,0,0,0,0,0,0,0,0] | | 42 | ["image_thumbs"] | | quiksilver t-shirt | 8270210 | 702193e9391832d6a07401b1c1fdff47 | zalando.pl | 2 | 1 | 2 | -1 | 0 | 0 | 0 | 92.56 | 45.55 | 47.01 | 1.0321 | zalando.pl/odziez-meska-koszulki/quiksilver/ | zalando.pl/odziez-meska-koszulki/quiksilver/ | 0 | 0.1 | 260 | [210,210,260,260,260,390,320,320,170,110,170,170] | | 41 | ["image_thumbs","people_also_ask"] | _zalando.pl · 2026-07-04, limit: 5, sort: pozycja rosnąco — frazy, które zyskały pozycje. Wszystkie pola wiersza (poza mapą historii pozycji `statistics.position.history`, która ma zmienne klucze-daty — jest w JSON i sekcji „Struktura odpowiedzi”)._ --- ## Żądanie `POST` `/api/visibility_analysis/reports/positions/getWins` Nagłówki: `Authorization: Bearer `, `Content-Type: application/json`. ### Struktura żądania **Podstawowy** ```jsonc filename="żądanie-podstawowe.jsonc" { "domain": "zalando.pl", "fetch_mode": "topLevelDomain" } ``` **Rozszerzony** ```jsonc filename="żądanie-rozszerzone.jsonc" { "domain": "zalando.pl", "fetch_mode": "topLevelDomain", "limit": 10, "page": 1, "order": { "prop": "statistics.position.current", "dir": "asc" }, "filtering": [] } ``` **cURL** ```bash curl --location --request POST 'https://api.senuto.com/api/visibility_analysis/reports/positions/getWins' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer $YOUR_TOKEN_HERE' \ --data-raw '{ "domain": "zalando.pl", "fetch_mode": "topLevelDomain" }' ``` ### Parametry ```ts type PositionsGetWinsRequest = { /** * **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; "domena") * - `subdomain` — pojedyncza subdomena * - `catalog` — ścieżka/katalog * - `url` — dokładny URL */ fetch_mode: 'topLevelDomain' | 'subdomain' | 'catalog' | 'url'; /** * Liczba wierszy na stronę. Nieujemna liczba całkowita. * @default 10 */ limit?: number; /** * Numer strony. Nieujemna liczba całkowita. * @default 1 */ page?: number; /** * Sortowanie wyników — **pojedynczy obiekt**, nie tablica. * Dozwolone `prop`: `statistics.position.current|previous|diff`, * `statistics.visibility.current|previous|diff`, `statistics.searches.current`, * `statistics.cpc.current`, `statistics.difficulty.current`, * `statistics.url.is_change`, `words_count`. * Zły kształt lub nieznany klucz NIE zwraca błędu — API po cichu wraca * do sortu domyślnego (`keyword_id` rosnąco). */ order?: { prop: string; dir: 'asc' | 'desc' }; /** * Dyrektywy filtrowania. Pusta tablica = brak filtrowania. */ filtering?: unknown[]; } export default PositionsGetWinsRequest ``` > **Ostrzeżenie:** > Nazwy parametrów różnią się od starej dokumentacji: jest to **`filtering`** (nie `filters`) oraz **`order`** (nie `sort_by` / `sort_order`). Uwaga na kształt `order`: to **obiekt `{ "prop": …, "dir": … }`** — forma tablicowa `[{ field, direction }]` jest przez API **ignorowana po cichu** (zwraca 200 z sortem domyślnym po `keyword_id`). > **Ostrzeżenie:** > Zarówno **`domain`**, jak i **`fetch_mode`** są **wymagane**; pominięcie `fetch_mode` zwraca `418` z `invalid_data`. ## Odpowiedź Po pomyślnym żądaniu otrzymujesz `data` (tablicę fraz, które zyskały na pozycji) oraz `pagination`. Ujemna wartość `position.diff` oznacza poprawę — fraza przesunęła się w górę SERP (mniejszy numer pozycji). **Skrócona** ```json filename="przykładowa-odpowiedź (skrócona)" { "success": true, "data": [ { "keyword_id": 1097, "keyword": "my secret", "statistics": { "position": { "current": 26, "previous": 51, "diff": -25 } /* … */ } } ], "pagination": { "page_count": 3182, "current_page": 1, "has_next_page": true, "has_prev_page": false, "count": 6363, "limit": 2 } } ``` **Pełna** ```json filename="przykładowa-odpowiedź (200)" { "success": true, "data": [ { "keyword_id": 1097, "kid": "0003d04b8e93ae73189ea88a01b6a0b5", "domain": "zalando.pl", "keyword": "my secret", "words_count": 2, "statistics": { "position": { "current": 26, "previous": 51, "diff": -25, "changes": { "wins": 0, "losses": 0, "no_changes": 0 }, "history": { "2026-06-25": { "position": 26, "has_serp": true } } }, "visibility": { "current": 0, "previous": 0, "diff": 0, "percent": 0, "history": null }, "url": { "current": "zalando.pl/kobiety/my-white-secret/", "previous": "", "is_change": 1 }, "cpc": { "current": 0.64 }, "searches": { "current": 480 }, "trends": { "history": [480, 390, 480, 390, 390, 390, 390, 480, 590, 390, 480, 720], "peak": null }, "difficulty": { "current": 45 }, "snippets": { "current": ["image_thumbs", "people_also_ask"] } } } ], "pagination": { "page_count": 3182, "current_page": 1, "has_next_page": true, "has_prev_page": false, "count": 6363, "limit": 2 } } ``` ### Struktura odpowiedzi ```ts type PositionsResponse = { /** `true` przy powodzeniu; przy błędzie `false` i koperta z `error` */ success: boolean; /** Zwrócone wiersze fraz (z poprawą pozycji) */ data: PositionRow[]; /** Metadane paginacji */ pagination: { page_count: number; current_page: number; has_next_page: boolean; has_prev_page: boolean; count: number; limit: number; }; } type PositionRow = { keyword_id: number; kid: string; domain: string; keyword: string; words_count: number; statistics: { 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 }; 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 PositionsResponse ``` ## Błędy ```ts type ErrorResponse = { success: false; data: { error: { /** np. invalid_data, invalid_filtering, unreachable_data, timeout, database, unknown */ type: string; message: string; /** pole -> reguła -> komunikat (dla invalid_data) */ params?: Record>; }; }; } export default ErrorResponse ``` > **Błąd:** > **`418`** jest zwracane również w przypadku błędów walidacji — nie tylko przy ograniczaniu liczby żądań. Brak `fetch_mode` → > `{"success":false,"data":{"error":{"type":"invalid_data","params":{"fetch_mode":{"_required":"This field is required"}}}}}`. ## Powiązane akcje - `getData` — bieżące pozycje fraz (taki sam kształt żądania) - `getWins` — frazy, które zyskały pozycje (ta strona) - `getLosses` — frazy, które straciły pozycje (taki sam kształt żądania) - `getKeywordHistory` — pełna historia pozycji dla pojedynczej frazy (`keyword_id` + `kid` + `domain` + `fetch_mode`)