Sujan Studio is a boutique wedding and portrait photographer based in Adelaide. In late 2025 the site was pulling roughly 1,900 organic sessions a month across five service pages and a blog that had not been touched in eighteen months. Six months later the same site drove 8,400 organic sessions – a 340% lift – and, more importantly, a 5.6× increase in enquiry-form submissions from search.
This is the exact playbook we ran. It is not a viral case; it is a slow, mechanical one built on three moves – a programmatic city × service page tree, an internal-linking overhaul, and a weekly audit-fix loop. If you sell a service in more than one suburb, everything here transfers.
Where we started
Every case study should start with the honest baseline. Ours had four problems that anyone auditing a service business site will recognise.
| Metric | Nov 2025 | Notes |
|---|---|---|
| Indexed pages | 38 | Home, 5 services, 1 about, 1 contact, 30 archived blog posts. |
| Monthly organic sessions | 1,900 | GSC 90-day average. |
| Average position | 32.4 | Anything > 20 gets almost no clicks. |
| Monthly enquiries | 11 | Wedding + portrait combined. |
| Core Web Vitals | Failing on mobile | LCP 3.8s, INP 320ms, CLS 0.14. |
The site ranked for the studio name and one long-tail phrase ("adelaide hills wedding photographer"). Everything else was invisible. We had no coverage for the search patterns actual clients used, which – as it turns out – were always geographic: "[suburb] wedding photographer", "[suburb] family portrait", "[suburb] engagement shoot".
The three moves
We ran three plays in parallel across six months. Move one accounted for roughly 70% of the eventual traffic lift; move two for 20%; move three for the last 10% but arguably the most durable share, because it stopped the site from re-breaking.
Move 1 – Programmatic suburb × service pages
The core insight: a photographer serving greater Adelaide is really twelve local businesses. We built a page tree of `suburb × service` – 24 suburbs × 4 services = 96 pages, up from 5. Each page was hand-templated but data-driven, pulling suburb-specific portfolio images, price ranges, and travel time.
The template was not thin – that is the trap most programmatic SEO falls into. Every page had: a 400-word intro tailored to the suburb (recent shoot locations, local venues, travel considerations), 8-12 real portfolio images shot in or near that suburb, a suburb-specific FAQ block, and a schema-marked review lifted from the correct geographic Google Business Profile category.
Move 2 – Internal linking overhaul
The 96 new pages had zero authority on day one. We fixed that by wiring them into the site's existing crawl paths.
- The home page gained a "Where we shoot" section linking to 8 hub suburbs (the ones with the highest wedding-venue density).
- Each service page linked to the 12 suburbs relevant to that service (e.g. wedding page → 12 suburb-wedding pages).
- Each suburb page linked laterally to its 3 neighbouring suburbs and vertically to the 4 services in that suburb.
- The blog got a category system that surfaced blog posts from the relevant geographic tag on each suburb page.
Result: the average page went from 0 internal inbound links to 6. Crawl budget was no longer a story we had to tell Google – the site's own structure told it.
Move 3 – The weekly audit-fix loop
A 96-page site with programmatic templates breaks in a hundred small ways every week: images without alt text, missing schema, canonical drift, broken internal links after a slug rename, INP regressions after a Wix update. We ran the SEMOptimiser audit every Monday morning and closed every issue by Friday. That is the loop.
Roughly 60% of fixes were mechanical (alt text, meta descriptions, image compression). 30% were structural (a canonical tag pointing to the wrong URL, a page indexed that should not have been). The last 10% were investigative – a page that stopped ranking, a suburb where a competitor overtook us, a search term that started returning an AI Overview instead of blue links.
The numbers, month by month
The growth curve is the standard one for compounding programmatic SEO: nothing happens in month one, some things happen in month two, then it snowballs.
| Month | Indexed | Impressions | Sessions | Avg. pos. | Enquiries |
|---|---|---|---|---|---|
| Nov 2025 (baseline) | 38 | 18,000 | 1,900 | 32.4 | 11 |
| Dec 2025 | 82 | 24,000 | 2,100 | 31.1 | 13 |
| Jan 2026 | 96 | 41,000 | 2,600 | 27.6 | 17 |
| Feb 2026 | 96 | 68,000 | 3,400 | 22.1 | 24 |
| Mar 2026 | 96 | 104,000 | 5,100 | 17.8 | 38 |
| Apr 2026 | 96 | 148,000 | 6,900 | 14.2 | 52 |
| May 2026 | 96 | 192,000 | 8,400 | 11.6 | 62 |
Impressions moved first – month two – because Google started indexing the new pages and putting them into the SERP mix. Sessions lagged impressions by 6-8 weeks because the pages started around position 30 and had to earn their way up. Enquiries lagged sessions by another 3-4 weeks because visitors have to scroll, browse, and decide before they submit a form.
What did not work
Case studies that only tell you what worked are half-truths. Here is what we tried and reverted.
- AI-generated FAQ blocks. Ran them for two weeks in January. Impressions per page were similar but click-through rate was 30% lower – the copy did not read like the rest of the page. Reverted to hand-written.
- Doubling suburbs from 24 to 48. Tried in mid-February. The next 24 suburbs were places we had never actually shot in, which meant the "recent shoot locations" data point was thin. Google noticed – none of the extra 24 pages broke position 40. Rolled back and stayed at 24.
- Sitewide h1 rewrite for exact-match keywords. Improved rank on 3 pages, tanked rank on 8. The old titles worked because they read like a human wrote them. Reverted after 10 days.
The Core Web Vitals side quest
The audit-fix loop caught a Wix update in early March that broke INP on every gallery page. We saw it in the SEMOptimiser dashboard on the Monday, isolated it to a lazy-loading script on the Tuesday, disabled the script on the Wednesday, and INP recovered from 380ms to 190ms by the Thursday.
Without a weekly cadence we would have discovered this in the June Search Console CWV report – three months of lost rank on the highest-value pages. The audit-fix loop is not glamorous but it pays for itself the first time it catches something like this. See our 30-day INP migration plan for how to run the same check on your own site.
What we would do differently
- Start the internal-linking overhaul before the programmatic pages ship. The first 4 weeks of thin internal links meant the new pages sat at position 40+ for longer than they needed to.
- Track AI Overview citations from day one. By April we were being cited in AI Overviews for 6 suburb-service queries. We only started measuring this in month five – we lost three months of data.
- Instrument enquiries at the source page. We eventually tied enquiries back to the landing page via a hidden form field. If we had done this in November, the "which suburbs actually convert" question would have been answerable in month two.
What to do next
If you sell a service in more than one geography, the programmatic × internal-links × weekly-audit combination is the highest-leverage six months of SEO work you can run. The exact playbook:
- List every suburb, city or region where you have actually delivered work. That is your suburb axis – not a list scraped from Wikipedia.
- List every distinct service you sell. Multiply. That is your page count.
- Template one page by hand. If you cannot write 400 words of unique content, the axis is wrong.
- Ship all pages, then wire the internal-linking overhaul in the same fortnight.
- Run a weekly audit from week one. The SEMOptimiser audit catches most of what will otherwise wait for the next Search Console alert.
- Report on impressions weekly, sessions monthly, enquiries quarterly. Different metrics move on different cadences – do not confuse yourself by tracking them all at the same frequency.
If you want to see this play in action on your own site, start a free audit – the first crawl surfaces the same shape of problem in about ten minutes.
