RRochak Shrestha

Build log · · Rochak Shrestha

How Kata Jaam got to first on Google for "events in Kathmandu"

Search first, not social first. Per-event pages with JSON-LD, canonicals for recurring series, tombstones for deleted events, and a weekly loop that reads Search Console.

Kata Jaam is an event app for Kathmandu, and its acquisition channel is Google. The playbook is programmatic SEO on Next.js: one indexable page for every event, venue, scene and festival, each with JSON-LD and a static map, strict canonical rules for recurring series, a tombstone for every deleted event, and a weekly loop that reads Search Console and decides what to build next. As of Oct 2026 the site is first on Google for "events in Kathmandu" and gets about 196 search clicks a day, with no paid acquisition behind it.

Why search came before social

Plans in Kathmandu lived in Instagram stories that vanish after a day. When I posted there, I got reach for a week at a time and then had to post again. A page that ranks gives you reach every day without another post. So the website became the front door and the Flutter app became the place people come back to.

Most signups say they found Kata Jaam on Google, which is the whole argument for doing it this way.

The stack is Next.js on Vercel with Firebase behind it. The first store build shipped two weeks after the first commit, in Feb 2026. The public pages are rendered on the server and cached, with incremental static regeneration, so a new or edited event goes live without a full rebuild and Google always gets complete HTML.

Programmatic SEO in Next.js: four page types

Programmatic SEO means generating pages from data instead of writing them one at a time. The data here is the catalogue: 1,321 approved events. Each page type exists because it answers a different kind of search.

  • Events. One page per event, with the date, the venue, a registration form and a map. These catch the long tail: people searching the name of a night, a show or an organizer.
  • Venues. A place outlives every event held there. The venue page keeps collecting signal after each event ends. People search a venue's name to see what is on there, and that search lands on the same page all year.
  • Scenes. Nightlife, running, concerts, screenings. These match how people actually search when they have no event in mind yet.
  • Festivals. Seasonal searches that come back every year to the same URL.

An event page has a short life. Venue, scene and festival pages stay and gather rankings over time, and new event pages keep them current. Each event page links to its venue, its scene and any festival it belongs to, and those pages list their upcoming events, so the links run both ways and every new event feeds the pages that last.

Structured data on every page

Every page carries JSON-LD: a block of schema.org data that tells a search engine what the page is about without it having to guess from the layout. For an event that means the Event type, with a name, a start date and a location, which is what Google's documentation for event structured data asks for. Venue pages describe the venue with the Place type. Scene and festival pages are list pages, and their JSON-LD is a list of the events they hold, each one an Event.

Every page also has a static map: an image of the location rather than an interactive map widget. It loads as one small image, which matters on a phone on mobile data, and it keeps a heavy map script off a page that only needs to say where. A tap opens the location in Google Maps for directions.

Canonical and tombstone rules for event pages

Recurring series are the first trap. A weekly night at the same venue produces pages that are nearly identical apart from the date, and they compete with each other for the same query. The canonical rule tells Google which URL is the real one. Every occurrence points its canonical at one series page, and the series page shows the next date. When the series ends, the series page stays up with its past dates, so the URL keeps what it earned.

Deleted events are the second trap. Events get removed: duplicates, cancellations, an organizer who asks for a takedown. If a URL Google has already indexed starts returning 404, it eventually drops out of the index and whatever it had earned goes with it. So every deleted event leaves one tombstone, and no indexed URL is allowed to 404. A tombstone keeps the event's name, date and venue, says plainly that the event was removed, and points to the venue and what is on there next. It is a normal page, so the links it earned still lead somewhere useful.

The weekly SEO loop

Every Tuesday a Claude Code agent with a written brief runs the SEO loop. It pulls Search Console, reads the coverage report and the foreign canonicals, meaning pages where Google chose a different canonical than the one the page declares, then drafts what to thicken or build. It pings IndexNow and updates a tracker. The tracker is a plain table with one row per page: the URL, the query it is meant to win, what the loop proposed, whether I approved it, and how its position moved after.

Like every loop in the operation, it files drafts on an approvals desk and publishes nothing on its own. I wrote about that setup on the agents page.

IndexNow is used by Bing and a few other engines. Google does not take part, so for Google the work is the sitemap, the internal links and the coverage report.

What the numbers did

As of 17 Oct 2026:

  • 2,451 registered users in eight months, with no paid acquisition.
  • First position on Google for "events in Kathmandu".
  • About 196 Google clicks a day (Oct 2026).
  • Rankings for hundreds of long-tail event queries.
  • 1,321 approved events in the catalogue.

What did not work, or is still unsolved

Ranking first did nothing for retention. Week-four retention sits under 10 percent. Search brings people with a plan for one night, and most come back the next time they need a plan, which is rarely a week later. Targeted pushes and scene follows exist to change that, and a separate Thursday loop reads the cohorts every week.

The SEO loop exists partly because some generated pages are thin, and thin pages from a template are exactly what Google's guidance warns against. A template that only swaps the name and the date makes a page with nothing worth ranking. The loop's job is to add what a template cannot: the organizer's own description, the venue's other nights, the scene the event belongs to.

If you want this for your product

I build products with search as the acquisition channel from the first commit: build my app or product.