longread · retrospective

allcars.cy: the first 8 weeks

from zero to the first page of Google. an honest breakdown: metrics, what I built, which decisions and why, the product, the users, and where I mess up.

0
organic growth (9 → 179 clicks/wk)
0
living users
0
PRs merged
0
user events

If you have no time to read

What I realised along the way: I learned to build and pull traffic at insane speed, but noticing that something I built had silently died - not yet (three silent failures in 8 weeks). From here the focus is one thing: finishing, not just building.

00 — intro

8 weeks, in short

01 — organic

SEO: the curve goes up

Organic clicks by week

9 → 179 in 8 weeks (W7 partial, 5 days).

Average position (lower = better)

44 → 12. Best days already on page 1.

vs the last report, 28 days

Clicks 231 → 580 (+151%) · impressions 10,955 → 21,179 (+93%) · CTR 2.11% → 2.74% · position 19.7 → 14.1. Cyprus = 82% of clicks. Mobile converts 3.4x better than desktop.

The pain: national high-volume queries slipped

QueryWasNow
cars for sale nicosia30.616.0 ↑
used cars cyprus41.144.8 ↓
second hand cars cyprus36.840.1 ↓

Which queries I actually rank for (and how much demand they have)

Since I'm bragging about the first page above - here's the specifics, who's on it. Two caveats so you read it right. "Impressions" is my demand proxy: how many times Google showed me at all over 28 days (not the same as the full market volume). "Market estimate" is the monthly Cyprus volume from deep research (this is NOT a precise Keyword Planner number: order of magnitude, medium confidence; I'll pull the exact figures once I have a KP export). And here's what comes out: I'm on the first page for a pile of specifics, but mostly narrow, small-volume queries. And on the fat ones - deep. Exactly that unfinished door.

Where I'm on page 1 (top-10)

QueryPositionImpressions 28dMarket estimate, /mo
μεταχειρισμένα αυτοκίνητα (used cars, Greek)10.233~8,000
φθηνά αυτοκίνητα μεταχ. κύπρος3.47~900
kia sportage for sale cyprus4.39~1,200
mitsubishi l200 cyprus price7.726~700
peugeot 208 cyprus8.114~500
toyota highlander cyprus8.08~250
used electric cars cyprus5.814~120
honda vezel 20279.131niche
allcars (brand)6.114growing

Plus dozens of ultra-long-tail queries (specific model+year+price: "jeep renegade 2019 price used", "mazda demio 2013 price", "kia stonic price") sitting at positions 1-5, but with 1-4 impressions each. I'm at the top for them, the demand is just tiny. The table's main insight: my fattest query - the Greek "μεταχειρισμένα αυτοκίνητα" (~8,000/mo!) - hangs at position 10.2, right on the first-page threshold. This is literally the single high-volume query where I almost made it. One push into the top-5 and that's completely different traffic.

High demand - and I'm on page 3-5

QueryPositionImpressions 28dMarket estimate, /mo
cars for sale cyprus (biggest English)deep~5,000
used cars cyprus4581~4,000
toyota cyprus → /cars/toyota22.0686~3,000
bmw cyprusnot in top~2,500
cars for sale nicosia15.743~2,000
second hand cars cyprus40.128~1,500
used cars limassol29.862~1,200
cars for sale larnaca16.240~900
volkswagen used cars26.751~350

Look at the top of the table: the two fattest queries - cars for sale cyprus (~5,000/mo, biggest English) and bmw cyprus (~2,500/mo) - and I'm nowhere in results for either. BMW especially hurts: on the site itself it's query #1 (1,292 searches on clean data), yet on "bmw cyprus" in Google I'm invisible. Meaning from the outside I'm invisible exactly where demand is highest. That's the hole in the top, most valuable layer, and it needs a hub (see plan at the end).

02 — engineering

What I built, week by week

PRs merged by week

~494+ in 8 weeks. W2 and W5 - peaks; W3 - a deliberate pause.

Launch6-11 May
the allcars.cy site, 3 languages EN/EL/RU, security (CSP/HSTS, tokens), duplicate-image detection, blog
26PR · v1.13.1-7

In short: public launch of allcars.cy - shipped the site itself, trilingual, security and the whole SEO/image layer at once.

Shipped
  • Public site + inline listing modal in the app (#830/#832)
  • i18n EN/EL/RU with language auto-detect (#849/#855/#887)
  • Security: enforce API origin, CSP/HSTS preload (#823/#824); short-lived signed tokens + HMAC/nonce (#825/#826)
  • Blog, scoring guide, releases page (#839/#840)
  • Self-hosted listing images + duplicate-image detection (#888/#889)
  • Donate page (crypto QR + donations) (#877/#864)
Fixed / perf
  • Hid listings without photos across all surfaces (#820/#822/#885)
  • Landing PageSpeed: preconnect, HLS video hero, autoplay (#892/#893/#879)
  • Ingest: x1000 fix for impossibly low mileage (typos) (#829/#831)
  • Alerts pipeline: alert-manager + metrics sidecar, Copy-phone CTA (#899)
Deploys: v1.13.1 - v1.13.7 · 26 PRs merged
W112-18 May
programmatic SEO (make+model+city), /el/ /ru/ categories, GA4 SSR, trim-badge taxonomy · P0: the data pipeline was dead 8 days
19PR · v1.13.7-31

In short: rolled out programmatic SEO (long-tail make+model+city), localized categories and GA4; plus taxonomy cleanup and reviving a dead data pipeline.

Shipped
  • Programmatic long-tail make+model+city pages (#941/#939)
  • Localized /el/cars and /ru/cars categories (#938/#937)
  • Category depth: intro, FAQPage JSON-LD, 410-Gone, browse chips, /sitemap-categories.xml (#930/#923)
  • GA4 on SSR pages + key events (#936/#934)
  • Listings from the bot into Picks + estimate for no-price listings (#911/#912)
Fixed / perf
  • Trim taxonomy: M-Sport / S-Line / ST-Line / AMG-Line (#935/#922/#924-927)
  • P0: a missing build label silently killed data ingestion for 8 days (#906)
  • app ModuleNotFoundError + OperationalError 1020 (#905)
  • Pickups: CY commercial road-tax rate (#902)
  • SEO slug: drop .html, dedup year (#903)
Deploys: v1.13.7 - v1.13.31 · 19 PRs (per-commit bump era)
W219-25 May
TCO calculator + PDF, EV grants, /electric-cars-cyprus hub, /methodology, depreciation v0, GEO/llms.txt · shrank the DB 23GB→6.6GB
96PR · peak

In short: the biggest throughput week - TCO/EV-grants combine + EV hub, depreciation foundation, GEO/LLM citation, and a risky compression of the biggest data table.

Shipped
  • TCO calculator: the math, ICE-vs-EV, 9 columns (#1064/#1113), EV picker (#1072/#1111), A4 PDF (#1068/#1117), confidence/sensitivity/break-even (#1066/#1116)
  • EV-grants engine + 5-step tree + PDF (#1070/#1071)
  • /electric-cars-cyprus hub (#1013/#1049); /methodology (#1069)
  • Depreciation v0: schema + fit job → depreciation curves (#1099/#1062); ML features + training set (#1136/#1156)
  • GEO/LLM: /llms.txt, schema, guide; citable Dataset JSON-LD on /report (#1036/#1048)
  • SEO sidebar category interlinking (#954)
Fixed / perf
  • Compressed the biggest data table 23GB→6.6GB + dropped duplicate indexes (#961/#970)
  • Reshaped a migration for the perf gate (correlated-UPDATE → GROUP BY) (#992/#993)
  • HEAD method on all SSR routes - unblocked Googlebot (#1107); 404 on ?fuel= (#1084)
  • P0: disk monitoring didn't see the data volume - a silent 5-day outage (#952)
Deploys: v1.13.34 - v1.13.43 · 96 PRs (week peak, sample cap - this is a floor)
W326 May-1 Jun
quiet week, ~0 commits. a deliberate pause after the peak
~1PR

In short: a deliberate pause after the 96-PR peak - let the risks settle, don't chase bugs out of nowhere. Confirmed zero commits (checked by author and committer dates).

  • App year filter: down to 2000, value entry, missing Jeep generations (#1161/#1160)
  • Prep for blueprint/silhouettes and sitemap pagination parity (#1186)
Deploys: no release
W42-8 Jun
2 microservices: specs-svc (specifications) + silhouette-svc (silhouettes), mass backfill, listing-image fixes
100+PR

In short: microservices week - stood up specs-svc and silhouette-svc, ran a mass specs backfill, cleaned up listing-image quality.

Shipped
  • specs-svc microservice: /v1 lookup/blueprint/browse, Redis DB-2 cache (#1309/#1320/#1296)
  • vehicle-specs foundation Phases A-C (#1265/#1296)
  • Backfill of generation dimensions in batches (#1268, #1280-#1339)
  • silhouette-svc: SVG car wireframes, generative AI model, front+side, body-aware (#1167/#1173/#1198/#1211)
  • plate-blueprint accuracy (#1308/#1314/#1253)
Fixed / perf
  • Image gate: reorder photos, drop showroom shots at ingestion (#1239/#1254)
  • Never an empty image (#1220/#1222); HD image archive (#1202/#1205)
  • Pricing batch limited to the scoreable set - stops a deploy-blocking scan (#1263/#1264)
  • Incremental refresh of catalog state (#1238/#1262); migrations table (#1035/#1190)
Deploys: bump-at-merge (release accumulated in Unreleased) · 100+ PRs (cap is a floor)
W59-15 Jun
2 flagships: Car-Data API (api.allcars.cy) + new PWA Signal Dark (17 screens), Deal Radar, web-push backend, ops-console, a second data source added
100+PR · 170 commits

In short: two flagships in one week - the Car-Data /v1 API and a full rewrite of the PWA Signal Dark; plus Deal Radar, the alerts/web-push backend, ops-console, and a second data source for the catalog. The busiest week of all.

Shipped
  • PWA Signal Dark: 17 screens, /tma flip, Latest Deals (#1626/#1631); Deal Radar backend P1 (#1527)
  • Alerts engine + Web Push backend P2 (#1539/#1553/#1552)
  • Calc: depreciation + value-estimate P3a (#1562); track-my-value + monthly re-valuation → value-change alerts P3b (#1565)
  • Car-Data API: api.allcars.cy + landing/docs, /v1/deal /v1/depreciation /v1/value /v1/plate, live scorer playground, SVG widgets (#1478/#1483/#1514/#1522)
  • ops-console: owner admin PWA on ops.allcars.cy (#1466/#1476)
  • A second data source + ingestion/enrichment (#1596/#1605)
  • SEO: /cars/all index (#1409), /cars/browse A-Z (#1378), enriched /cars/ hub + AggregateOffer (#1622)
Fixed / perf
  • Redesigned comparable selection in the pricing engine (#1559)
  • Pinned fastapi<0.137 - otherwise 500 on all panel routes (#1617)
Deploys: v1.13.44 - v1.13.47 · 100+ PRs · 170 commits
W616-22 Jun
ops-hub (sync/deploy queue/messaging/mailbox) + own CI/CD, auth on /api/*, Deal Radar v2, listing history · user spike 486 WAU
80PR

In short: infra week - ops-hub and own CI/CD with no user features, auth on /api/*, Deal Radar v2, cross-platform history. Coincided with the 486 WAU peak (all-time max).

Shipped
  • ops-hub: context sync + core (#1819/#1828), serialized deploy queue (#1824/#1832), two-way messenger relay + agent mailbox (#1823/#1825)
  • Self-hosted CI/CD runner phase 1 + board automation on ci-bot (#1782/#1792); verify.sh gate (#1789)
  • Deal Radar v2: composite multi-axis scoring (#1652/#1680)
  • Cross-platform listing history + "Listed on" panel in SD (#1753/#1762); multi-select sources (#1716/#1726)
  • Local CV hero-photo selection, computer vision (#1739/#1717); ops-health panel (#1771/#1772)
Fixed / perf
  • Hybrid auth on /api/* (allowlist) + shadow telemetry mode (#1810/#1802)
  • Redis rate-limiter for the app + /plate (#1804/#1806)
  • Stopped a cold 30-60s radar/pricing compute for non-scoreable listings (#1774/#1778)
Deploys: v1.13.48 - v1.13.49 · 80 PRs
W723-28 Jun
VIN via external provider (end-to-end identity), Web Push client, /v1 contract, ops-hub to prod, price segmentation · fixed saved-search alerts
72PR · 77 tickets

In short: identity-and-finish week - plate→VIN via an external provider end-to-end, the Web Push client, the /v1 contract finalized, ops-hub moved to prod, and finally fixing the long-silently-broken saved-search alerts.

Shipped
  • Full plate spec sheet: drivetrain/gearbox/chassis (#1914) + specs-svc projection
  • External plate→VIN provider into identity (#1924/#1925); VIN on blueprint + SD detail (#1939/#1941); stage-2 VIN→specs + read-back (#1926)
  • VIN hard-veto when both known and different + retro re-check in dedup (#1927/#1931); the provider is never shown to users (#1945)
  • Web Push client: SW push handler + subscribe UI (#1962/#1965)
  • Car-Data API: closed /v1 error-code taxonomy + X-Request-Id (#1544/#1915)
  • ops: deploy approvals in the owner's bot + inline buttons (#1889/#1893); hub moved to prod
Fixed / perf
  • Pricing: refined comparable segmentation (#1599/#1744)
  • Restored listing-image loading: 3h cron + freshness alert (#1964)
  • Saved-search alerts: "new match" finally fires - removed a dead image gate (#1961)
  • Gate fallback scans T7/T8/T9 - fix cold /api/radar (#1854/#1857)
Deploys: v1.13.50 - v1.13.57 · 72 PRs · 77 tickets closed (max for the period)
03 — decisions

Which decisions and why

Launch

Hide photo-less listings + 3 languages from day one

Why: a listing with no photo = garbage, it kills trust in results. 3 languages - Cyprus is trilingual, and Greek queries showed up in GSC immediately. The data confirmed the bet.

W1

Go all-in on programmatic SEO make+model+city

Why: early GSC showed I was already top-8 on narrow long-tail queries, but page 4-5 on the fat ones. Build what already ranks. The data itself showed where to dig.

W2

Invest in TCO + EV, shrink the DB

Why: the TCO calculator kept people ~29 minutes on the page - insane dwell time. If they stick, give them more tools. The DB shrink wasn't by choice - the disk was filling up.

W3

Do nothing (that's a decision too)

Why: after 96 PRs, pushing further = catching bugs out of nowhere. We deliberately slowed down.

W4

Split specs/silhouette into microservices

Why: silhouette generates images on its own budget (AI generation costs money), so isolating it behind a budget gate is safer. Specs are needed by both surfaces - logically a shared service.

W5

Flip /tma to the PWA + ship the API at once

Why: the PWA is the main mobile retention surface (users are mobile, per the data), the API is future distribution. We risked two big things in one week.

W6

Spend a whole week on infra, no features

Why: deploy became the bottleneck, and then the cloud CI deploy died (~20 June). We invested - and hit the 486 WAU peak right then. Ship speed is a feature.

W7

VIN via external provider + urgently fix alerts

Why: VIN is the golden identity key, an external provider is the only legal channel. And digging through the data I found the saved-search alerts engine had NEVER sent a "new match". Instant decision - fix it, it's a direct hole in retention.

04 — experiment

Indexing for AI: what, how and why

experiment result · AI Assistant channel (GA4)
0 → 145

sessions from AI assistants (a new class of source), 62% engagement, from zero and with no spend. Already the 3rd-biggest channel and growing on its own.

05 — product and people

Who uses us

"people are genuinely obsessed with BMW - it's not a joke, it's literally demand straight off the screen"

Top makes in search

BMW dominates - ~2x more than #2 (clean data, no dev tests).

Engagement depth (events per user)

median in the 6-20 band; ~15% power users (20+).

Users by week (WAU)

W6 peak (485) - 18 June spike (~6x), settled in 2 days. MAU growing: Apr 218 -> May 479 -> June 789.

Key actions (total for the period)

search - the product core (67% of users searched).

4 user cohorts

buyers (hunters + criteria) = 41% of the base; sellers/own-car - a future paid audience.

06 — the sore spot

Retention: honestly

reached a real listing
5.5%

of 947 searchers. Only 54 people ever clicked through to a seller. This is the main funnel blocker, not weekly return.

saved-search alerts engine
0

"new match" notifications ever (only delisted). A ghost feature, silent for months. Fixed in W7 - need to verify it actually sends.

"people look deeply - and almost nobody reaches the seller. the hole isn't return, it's the handoff"
07 — lessons

What I learned

merged != deployed != working

Alerts looked done, PR merged, all green - yet notifications silently didn't send for weeks. Verify a feature ACTUALLY fires in prod.

silent failures everywhere

The data pipeline was down 8 days, disk monitoring slept 5 days, alerts didn't send. Need quality thresholds and freshness alerts, not "seems to work".

SEO at the start is indexing

I hit a wall not on positions but on indexing. Until Google indexed the inventory - an impressions ceiling. Once indexing moved - everything unlocked.

users are obsessed with BMW

1,292 searches (clean data), ~2x more than #2. Demand straight off the screen - go hard on BMW and top-model content.

infra investment pays off

An ops-hub/CI week looked "featureless", but it sped up shipping - and the user peak hit right then. Speed is a feature.

big weeks cut both ways

W2 and W5 (96-100+ PRs) - great throughput, but that's exactly when risks pile up (a risky migration, the fastapi pin). Push - but double the caution.

08 — what's next

The plan, by priority