Case study — programmatic pages from real search demand

Status: RUNNING. Baseline measured. Pages built. Window open. Numbers below are filled in

automatically from the machine's own JSON snapshots — nothing here is typed by hand.

Format requested by most SEO/GEO roles: what you did → which metric you moved → how you achieved it.
This document is written in that order on purpose.

---

0) Baseline (measured, not estimated)

Source: reports/analitika-<day>.json, reports/pozicije-<day>.json — produced by the daily run from

Search Console and GA4 APIs. Date of baseline: 2026-10-08 (measured).

DomainGSC impressionsGSC clicksGA4 sessionsPosition movement (7d)
aicommandcenter.pro *(main case)*4804▲0 / ▼0
braincore.pro *(secondary)*002▲0 / ▼0
nomorequiet.com *(control)*200▲1 / ▼1
beoproproperties011.com *(control)*000▲0 / ▼0
automagistar.com *(control)*000▲0 / ▼0

Two domains carry the published experiment and are named (they are my own and the artifacts are

public). The other three are listed as controls with names withheld; their raw numbers are

included so the table stays verifiable inside the reports.

Read this honestly: these are near-zero-traffic domains. There is no impressive number to hide

behind — which is exactly why it is a clean experiment: any movement is attributable.

---

1) What I did

Built a programmatic page pipeline driven by *real* demand data instead of keyword guesses:

1. Demand extraction — *correction (2026-10-08): the first version of this line oversold it.*

Search Console gave the two domains that actually got pages (automagistar.com, beoproproperties011.com)

zero queries — the pozicije table holds 25 queries for aicommandcenter.pro and 2 for

nomorequiet.com, and none for those two. So the pages could not have come from GSC data.

What they actually came from:

(reports/konkurencija-upiti.json: automagistar.com → "auto magistar", "polovni automobili beograd";

beoproproperties011.com → "beopro properties", "izdavanje stanova beograd");

hl=sr&gl=rs — Serbian language, Serbia region), plus People-Also-Ask harvesting

(dodaci/pitanja.mjs) and orphan-page detection.

*Second correction (2026-10-08):* the 22 published pages were not in the measurement rotation at

all — pages, indeks and onpage held zero of them. A "21-day measurement" would have reported

nothing for these pages and looked like "no effect" instead of "not measured". They are now added,

and the baseline is recorded: all 22 report "URL is unknown to Google" in Search Console, with

canonical 11/11 and JSON-LD 11/11 on both domains.

That is still real demand data — but it is autocomplete + a human-declared seed, not "queries

pulled from Search Console", and there was no paid keyword tool involved.

*Second correction (2026-10-08):* the 22 published pages were not in the measurement rotation at

all — the pages, indeks and onpage tables held zero of them. A "21-day measurement" would

have reported nothing for these pages and looked like "no effect" instead of "not measured". They are

now registered, and the baseline is recorded: all 22 report "URL is unknown to Google" in Search

Console, with canonical 11/11 and JSON-LD 11/11 on both domains.

*Third correction (2026-10-08, later the same day):* **the number "22" is Serbian-only and no longer

the whole picture. A second generator was built for English demand** (pseo-generator-en.mjs) and

35 English pages were published — 23 on the two genuinely English-language sites

(aicommandcenter.pro, braincore.pro) and 12 on nomorequiet.com under an explicit bilingual

override (that site's html lang is still sr-RS; the English pages sit under /pseo-en/).

Total programmatic pages are now 22 Serbian + 35 English = 57, all in the measurement rotation.

Why this matters more than the count: a reader who added up the numbers on this page would have

concluded "22 pages" while the machine was serving 57. The published proof has to state its scope

and its date, or it drifts the moment the machine moves — which is the entire failure mode this

case study documents.

2. Template generation — dodaci/pseo-generator.mjs produced complete pages from that demand:

unique H1, FAQ block, FAQPage JSON-LD, canonical, internal links between the generated pages,

and a dedicated sitemap (sitemap-pseo.xml).

*Honest limit:* the pages link to each other, not into the domain's pre-existing pages. Verified

2026-10-08 on the published pages: 3–6 links per page, all of them /pseo/….

3. Batch quality gate before publishing — this is the publishing guard inside

dodaci/objavi-pseo.mjs, which refuses to publish a page unless it has: a canonical pointing at

its own domain, JSON-LD, an H1, and a language that matches the live site's html lang

(the site's language is read from the live page, not typed into the code). After the upload, every

page is re-fetched and must return HTTP 200, and the package is verified with SHA256 on the server.

*Correction (2026-10-08):* an earlier version of this document credited preflight.mjs and

objava-bezbjedno.mjs here. Those are real modules, but they serve a different job — preflight.mjs

decides which queued *fixes* can be published and which need a code change. They did not gate this

pSEO batch. The gate described above is the one that actually ran.

4. Indexing push — the pSEO sitemap is declared in the site's robots.txt, and the URLs are pushed

to Bing/Yandex via IndexNow. *Correction (2026-10-08):* the first version of this page implied this had

happened; it had not — a break in indexnow.mjs stopped it from reading the pSEO sitemap, so the

tool reported "no change" while 22 pages were never submitted. Fixed and verified: the URL list went

58 → 69 (automagistar.com) and 13 → 24 (beoproproperties011.com). The Google side had the

same defect in a different file: sitemap.mjs returned only the first sitemap from robots.txt,

so neither pSEO sitemap was ever submitted to Search Console. Fixed — submissions went from 4 to 7,

including both pSEO sitemaps (0 errors). Two tools, same mistake, both reporting success.

5. Measurement — the same daily run records impressions, clicks, positions, AI crawler hits and

AI referral traffic per domain, so the before/after is computed by the machine, not by me.

Pages built — language measured, not assumed (checked 2026-10-08):

DomainPages generatedSite's actual language (html lang)Page languageLive?
beoproproperties011.com11sr (Serbian)SRyes — 11/11 HTTP 200
automagistar.com11sr (Serbian)SRyes — 11/11 HTTP 200
aicommandcenter.pro *(main case)*17en (English)SRno — held back
braincore.pro *(secondary)*12en (English)SRno — held back
nomorequiet.com *(control)*0sr-RS (Serbian-first, with an SR/EN/RU/TR/AR/中文 switcher)—nothing to publish
14 misfiled pages (were in nomorequiet.com)14—SR about a different companyno — moved aside

Correction made during review — twice, and both are the point:

1. The first batch contained pages for the wrong company in the wrong folder: 14 Serbian pages about a

concrete/construction business were sitting in the nomorequiet.com folder (they belonged to a

different domain's topic set — **these are misfiled draft pages, not published pSEO pages, and not

part of the "22 pages" count**). The publishing guard

(dodaci/objavi-pseo.mjs) refused to publish them. They were moved to

predlozi/_pogresno-smjesteno/, not deleted.

2. This document previously described nomorequiet.com and braincore.pro as "English" and

aicommandcenter.pro as "mixed (EN + FR)" without measuring. When the language check was made

automatic (it now reads the html lang attribute from each live site instead of trusting a

hardcoded list), the real picture was: aicommandcenter.pro and braincore.pro are en, and

nomorequiet.com is sr-RS (Serbian default, despite the language switcher). The earlier

"French pages" claim did not hold up and is withdrawn.

Why this belongs in a case study: the first version of the guard had three domain names typed into

the code. That is exactly the kind of hardcoded assumption that produces language mismatches at scale.

It was replaced with a measurement of the live site. The machine now cannot publish a page whose

language does not match the site it is going onto — and it says so instead of guessing.

---

2) Which metric I expect to move (and the exact source)

MetricSource of truthBaselineDay 21Change
GSC impressions (per domain)Search Console API via analitika-<day>.json48 / 0 / 2 / 0 / 0*pending**pending*
GSC clickssame0*pending**pending*
Indexed pages (site: count / URL Inspection API)dodaci/indeks.mjs*pending**pending**pending*
Average position for target queriesdodaci/pozicije.mjs (7-day windows)▲0 / ▼0*pending**pending*
AI crawler hits (GPTBot, ClaudeBot, PerplexityBot)Cloudflare analytics via dodaci/cf-analitika.mjs*pending**pending**pending*
AI referral sessionsGA4 via analitika-<day>.json4*pending**pending*
GEO score per pagedodaci/geo.mjs*pending**pending**pending*
PageSpeed / Core Web VitalsPSI API via dodaci/brzina.mjs*pending**pending**pending*

Rule for this document: if the numbers do not move, this table will say so. A case study that

only contains wins is marketing, not evidence.

---

3) How I achieved it (the pipeline, concretely)


GSC queries + autocomplete
        │
        ▼
  demand → template  ──►  page (H1, FAQ, FAQPage JSON-LD, canonical, internal links)
        │
        ▼
  preflight + objava-bezbjedno   (content verified BEFORE publishing, rollback point kept)
        │
        ▼
  sitemap-pseo.xml + IndexNow    (discovery push)
        │
        ▼
  daily run measures: impressions, clicks, positions, AI crawlers, AI referrals, GEO score
        │
        ▼
  before/after written to dated artifacts (JSON + Markdown), not to a slide by hand

Every arrow is a module in the machine with a one-line description in code, a test where it makes

sense, and a dated artifact when it runs.

---

4) What this proves (and what it does not)

Proves: demand-driven generation, a real quality gate before publishing, and measurement that

comes from the platforms rather than from my own reporting.

Does not prove (yet): organic growth at scale. One window on five low-traffic domains is a

starting point, not a track record. The next iteration will run on a domain with existing traffic.

---

5) Timeline

DateEvent
2026-10-08Baseline measured (this document)
2026-10-09Pages published to production (static, no application rebuild)
2026-10-11Indexing check + first impressions
2026-10-29Full before/after, added to this document (21 days)
2027-01-06Trend + which queries drive clicks (90 days)

*This document is regenerated from the machine's JSON; the links above point to the raw artifacts.*

Start async — no calls, no meetings. Write the domain and what you want measured; you get a written answer with the artifacts.

Open an issue (fastest, nothing to install)