Programmatic SEO bez kodu brzmi jak skrót, który kończy się karą od Google. Kończy się tak tylko wtedy, gdy pipeline nie ma bramek jakości. Jeśli dane są porządne, szablon zostawia miejsce na unikalną treść, a publikacja idzie partiami, do pierwszych stu stron naprawdę nie potrzebujesz programisty. Potrzebujesz arkusza Google, jednego narzędzia automatyzacyjnego i dyscypliny w odrzucaniu wierszy, które nie spełniają warunków.
Poniżej pokazuję pipeline, który działa w praktyce: od modelu danych w arkuszu, przez walidację i szablon, aż po wysyłkę do WordPressa i wycofanie całej partii, gdy coś pójdzie nie tak. To ta sama logika, co w automatycznym raporcie SEO w Make, tylko zamiast raportu na wyjściu masz opublikowaną stronę.
Model danych w arkuszu: jedna strona to jeden wiersz
Zasada jest jedna i nie ma od niej wyjątków: jeden wiersz arkusza odpowiada dokładnie jednej stronie w serwisie. Jeśli zaczniesz trzymać dwa warianty w jednej komórce, pipeline przestanie być przewidywalny i nie policzysz, ile stron faktycznie poszło na produkcję.
Minimalny zestaw kolumn, który wystarcza do 90 procent projektów:
- id: stały identyfikator wiersza, nigdy nie zmieniany po publikacji (to on wiąże wiersz z ID posta w WordPressie).
- status:
draft,ready,published,blocked. Sterujesz nim, co wchodzi do kolejnej partii. - encja i modyfikator: dwa wymiary, z których składa się temat strony (np. miasto i usługa, materiał i zastosowanie, integracja i platforma).
- slug, title, meta_title, meta_description: osobne kolumny, nie generowane w locie w szablonie.
- kategoria_id: liczbowe ID taksonomii z WordPressa, nie nazwa.
- unikalne_1 do unikalne_4: pola, które muszą się różnić między wierszami. To one decydują o tym, czy strona ma wartość.
- wp_post_id: wypełniane przez pipeline po publikacji, puste na wejściu.
Dwa wymiary to maksimum, jakie warto brać na start. Trzy wymiary podnoszą liczbę kombinacji sześciennie i prawie zawsze produkują wiersze, dla których nie masz żadnych realnych danych. Lepiej mieć 120 wierszy z pełnymi danymi niż 2000 wierszy, z których 1800 jest wypełnionych tą samą regułką.
Walidacja danych przed generowaniem
Walidacja idzie w arkuszu, w osobnej zakładce, i daje jedną kolumnę wynikową: valid albo lista powodów odrzucenia. Sprawdzaj cztery rzeczy.
Kompletność. Każde pole oznaczone jako wymagane musi być niepuste. Formuła typu =IF(COUNTBLANK(B2:J2)>0;"brak danych";"ok") wystarcza, nie potrzebujesz skryptu.
Unikalność. Kolumny slug, title i meta_description muszą być unikalne w całym arkuszu. =COUNTIF(C:C;C2)>1 wyłapie duplikaty, zanim trafią do WordPressa i wygenerują slugi z sufiksem -2.
Progi długości. Meta title do 60 znaków, meta description w przedziale 120–158 znaków, pola unikalne minimum 180 znaków każde. Wiersz, który nie przechodzi progu, nie jest gotowy, a nie „prawie gotowy”.
Podobieństwo między wierszami. To bramka, którą większość projektów pomija, i to ona najczęściej zabija cały katalog. Policz prosty wskaźnik: ile unikalnych słów (po usunięciu stop-words) ma pole unikalne_1 wiersza względem tego samego pola w pozostałych wierszach z tej samej kategorii. Jeśli nakładka przekracza 70 procent, wiersz jest szablonowy i nie powinien wejść do partii.
Jeśli nie chcesz liczyć tego formułami, przenieś cały arkusz do szablonu audytu treści w Arkuszach Google i użyj tych samych kolumn decyzyjnych, które służą do oceny istniejących tekstów. Logika jest identyczna, tylko stosujesz ją przed publikacją, nie po.
Szablon treści i pola, które muszą być unikalne
Szablon to szkielet HTML z placeholderami. Problem nie polega na tym, że szablon istnieje (każdy sklep ma szablon karty produktu), ale na tym, ile procent finalnego tekstu z niego pochodzi. Praktyczna granica: część wspólna nie powinna przekraczać 40 procent objętości strony.
Co może być wspólne: nagłówki H2, struktura sekcji, tabela z legendą, sekcja FAQ z tymi samymi pytaniami, blok nawigacyjny do kategorii nadrzędnej. Co musi być unikalne: pierwszy akapit (bez wstawiania nazwy encji w stałe zdanie), wartości liczbowe w tabeli, przynajmniej dwa akapity merytoryczne i odpowiedzi w FAQ.
Najczęstszy błąd w tym kroku to generowanie „unikalnej” treści przez podmianę jednego rzeczownika: „Najlepsze narzędzia SEO dla Krakowa” i „Najlepsze narzędzia SEO dla Gdańska” przy identycznym korpusie to dokładnie to, co Google opisuje jako nadużycie skalowania treści w swoich wytycznych antyspamowych. Granicą nie jest liczba stron, lecz to, czy każda z nich odpowiada na inne pytanie.
Połączenie arkusza z WordPressem przez API
Warstwa transportowa jest najprostszą częścią całego pipeline. W Make albo n8n wygląda to tak:
- Trigger: ręczny albo harmonogram. Nie podłączaj triggera na zmianę komórki, bo jedna poprawka literówki wyśle całą partię.
- Google Sheets: Search Rows, filtr
status = readyivalid = ok, limit równy rozmiarowi partii. - Iterator po wierszach.
- Mapowanie: składasz pole
contentz szablonu i kolumn arkusza. - HTTP POST na
/wp-json/wp/v2/postsz autoryzacją przez hasło aplikacji. Pola:title,slug,content,excerpt,status,categories,featured_media. Szczegóły kontraktu są w dokumentacji REST API WordPressa. - Zapis zwrotny: ID posta z odpowiedzi wraca do kolumny
wp_post_id, astatuszmienia się napublished.
Krok szósty jest nieoczywisty i absolutnie krytyczny. Bez zapisu zwrotnego nie masz pojęcia, które wiersze już poszły. Pipeline, który przerwie się w połowie partii, przy ponownym uruchomieniu opublikuje te same strony drugi raz, tym razem ze slugiem -2. Zapisuj ID do arkusza po każdym wierszu, nie zbiorczo na końcu.
Bramka jakości: co blokuje publikację wiersza
Bramka to lista warunków, z których każdy ma prawo zatrzymać wiersz. Jeden niespełniony warunek oznacza status = blocked, a nie publikację z ostrzeżeniem.
| Warunek | Próg | Co robi pipeline |
|---|---|---|
| Kompletność pól wymaganych | 100 procent | blokuje wiersz |
| Duplikat slug lub title | 0 wystąpień | blokuje wiersz |
| Nakładka treści z innym wierszem | poniżej 70 procent | blokuje wiersz |
| Objętość unikalnej części | min. 60 procent strony | blokuje wiersz |
| Linkowanie wewnętrzne | min. 2 linki wychodzące | blokuje wiersz |
| Obraz wyróżniający | obecny, alt niepusty | publikuje i oznacza do uzupełnienia |
Warunek linkowania wewnętrznego jest ważniejszy, niż się wydaje. Strony programmatic generowane partiami bardzo łatwo stają się sierotami: nic do nich nie linkuje poza mapą witryny, więc Googlebot albo ich nie pobiera, albo pobiera i zostawia w stanie wykryto, obecnie nie zaindeksowano. Każdy wiersz musi mieć w danych dwa linki: jeden do kategorii nadrzędnej, jeden do powiązanej strony z tej samej partii.
Publikacja partiami i harmonogram
Partia to 20–40 stron, nie 300. Rozkładaj je w czasie i zostawiaj okno na pomiar, bo to jedyny moment, w którym możesz jeszcze zawrócić.
- Partia 1 i 2: po 20 stron jako szkice, ręczna akceptacja, publikacja po przeglądzie.
- Partia 3 i dalsze: po 40 stron, publikacja automatyczna, ale dopiero po tym, jak partia 1 ma zmierzoną indeksację.
- Odstęp: minimum 7 dni między partiami. Nie chodzi o „oszukanie” Google, a o to, żeby mieć dane do decyzji.
Po 14 dniach od pierwszej partii policz odsetek stron zaindeksowanych i średnią pozycję. Jeśli indeksuje się mniej niż połowa, problem leży w danych albo w nakładce treści, nie w częstotliwości publikacji, i dosypanie kolejnych 200 stron tylko go powieli. Dane do takiego rachunku wygodnie trzymać poza interfejsem Search Console: w masowym eksporcie do BigQuery wystarczy jedno zapytanie, żeby porównać partie między sobą.
Jak wycofać całą partię, gdy coś pójdzie źle
Plan wycofania przygotuj przed pierwszą publikacją, nie po awarii. Kolumna wp_post_id plus kolumna partia dają ci wszystko, co potrzebne.
Scenariusz wycofania ma trzy poziomy. Pierwszy: zmiana statusu na draft przez POST /wp-json/wp/v2/posts/{id} z {"status":"draft"}. Strony znikają z serwisu, zwracają 404, ale zostają w bazie i możesz je poprawić. Drugi: ustawienie noindex na partii, gdy strony mają jakąś wartość dla użytkownika, ale nie powinny konkurować w wyszukiwarce. Trzeci: trwałe usunięcie z przekierowaniem 301 na kategorię nadrzędną, stosowane tylko wtedy, gdy strony zdążyły zebrać linki zewnętrzne.
W praktyce poziom pierwszy wystarcza w dziewięciu przypadkach na dziesięć i wykonuje się w dwie minuty, jeśli masz ID w arkuszu. Bez nich wycofanie partii to ręczne szukanie 40 postów po slugu i zwykle moment, w którym projekt umiera.
Co zabrać z tego pipeline
Programmatic SEO bez kodu jest wykonalne, bo trudną częścią nigdy nie była warstwa techniczna. Trudne jest utrzymanie dyscypliny: odrzucanie wierszy bez danych, trzymanie nakładki treści poniżej progu i publikowanie partiami, mimo że arkusz pozwala wysłać wszystko naraz. Pipeline, który nie potrafi zablokować wiersza, nie jest pipeline, tylko generatorem kopii.
FAQ
Ile stron programmatic mogę opublikować, zanim Google zareaguje negatywnie?
Liczba nie jest tu zmienną decydującą. Serwisy z tysiącami stron generowanych z danych (katalogi, porównywarki, bazy specyfikacji) radzą sobie bez problemu, a katalogi z 50 stronami wpadają w kłopoty, bo każda z nich mówi to samo. Patrz na odsetek unikalnej treści i na to, czy każda strona odpowiada na inne zapytanie, nie na licznik.
Make czy n8n do takiego pipeline?
Do 500 wierszy miesięcznie Make jest wygodniejszy: moduł Google Sheets działa bez konfiguracji, a limity operacji w tanim planie wystarczają. Powyżej tego progu n8n wychodzi taniej, bo rozliczasz wykonania workflow, nie pojedyncze operacje, a iteracja po 40 wierszach to w Make 240 operacji.
Czy mogę generować pola unikalne modelem językowym?
Tak, pod warunkiem że karmisz model danymi z wiersza, a nie samą nazwą encji. Prompt widzący tylko nazwę miasta wyprodukuje cztery warianty tego samego akapitu. Prompt widzący sześć konkretnych wartości z arkusza napisze sześć różnych tekstów. Wynik i tak przechodzi przez bramkę nakładki.
Jak ustawić obrazy wyróżniające dla stron generowanych partiami?
Najprościej: wgraj obrazy do biblioteki mediów wcześniej, zapisz ich ID w arkuszu i przekazuj w polu featured_media. Generowanie obrazu w locie wydłuża wykonanie workflow i jest pierwszą rzeczą, która się wywraca przy limitach API. Jeden obraz na kategorię jest lepszy niż brak obrazu na stronie.
