All projects

Trade-Contractor SEO & Lead Platform.

Free tools, documents and a directory that turn contractor searches into booked demos.

A US company selling an AI receptionist to trade contractors needed a site that ranks and turns search traffic into demos. I built all of it solo (site, CMS, tools, data pipeline and admin), and it's live in production.

Role
Full-stack developer · Softaims
Team
Solo
Time
~6 months
Stack
Next.js · Strapi · OpenAI · PostgreSQL
Access
Private: commercial product
  • 43
    trade calculators
  • ~80
    document builders
  • 2,800+
    contractor listings
  • 500+
    commits, all but one mine
Search in, tracked lead outsimplified · example values
SearchGoogleFree toolscalcs & docsContentblog,licensingDirectorylistingsTracked leadwith its sourceload calcTX licencenear me
  1. Search: Contractors arrive from search. Every kind of page is built to rank for something they actually type.
  2. Tools: 43 free calculators and about 80 document builders answer the search on the spot. The free owner app pre-fills every document from the shop's Google Maps listing.
  3. Content: Blog posts and state licensing pages, written in the CMS, build authority for the searches a tool can't answer.
  4. Directory: A public directory of 2,800+ contractor listings targets local searches.
  5. Lead: Every path ends in a tracked lead (a demo request, a template download or an owner-app sign-up) with its source attached, sent to Discord and the admin dashboard.

Search. Contractors arrive from search. Every kind of page is built to rank for something they actually type.

Key decisions.

  1. Scraped data never touches the live tables directly

    I made each run write a checksummed artifact instead of writing straight to the database. It has to pass an AI rewrite, and a publish, normally started by hand, is refused unless it targets the right database and passes hard checks. A bad scrape can't damage SEO pages, and a bad publish can be undone.

    From scrape to live pagesimplified · example valuescheckpointed
    ScrapercheckpointedArtifactchecksummedAI rewritebatch jobPublishhard checksLive tablesdirectorypagesManifestfor rollbackone city
    1. Scrape: Runs go city by city and checkpoint every business, so a crash resumes exactly where it stopped.
    2. Hold: Raw results never touch the database. They're held as a checksummed artifact.
    3. Rewrite: An AI batch job rewrites the listings, photo alt text included.
    4. Publish: A publish, normally started by hand, is refused unless it targets the right database and passes hard checks. It saves a rollback manifest of every row and image it changed.
    5. Merge: A merge rule stops a weaker re-scrape from overwriting richer content that's already live.
    6. Rollback: If a publish turns out wrong, its manifest undoes it.

    Scrape. Runs go city by city and checkpoint every business, so a crash resumes exactly where it stopped.

  2. Calculators as pure functions, checked against real numbers

    I wrote all 43 calculators as pure functions, each tested and most also checked against hand-computed industry benchmarks, instead of tangling the logic into the UI, so a pricing formula can't quietly drift.

  3. The CMS writes, the database serves

    I had content written in Strapi and pushed into Postgres by webhook, refreshing only the pages that changed, instead of the site querying the CMS on every request, so pages stay static-fast and still update without a rebuild.

    The CMS writes, the database servessimplified · example values
    Editorwrites inStrapiStrapi CMScontentPostgressite databaseWebsiteall pagescachedVisitorsfrom searchpublish post B
    1. Write: An editor writes and publishes a post in Strapi.
    2. Webhook: Strapi pushes the change into Postgres by webhook.
    3. Refresh: Only the pages that changed are regenerated, with no rebuild. Everything else stays cached.
    4. Serve: Visitors get static-fast pages. The CMS is never in the request path.

    Write. An editor writes and publishes a post in Strapi.

Background

A US company selling an AI receptionist to HVAC, plumbing and electrical shops needed a website that would rank, earn trust and turn search traffic into booked demos. I built it on my own, and it runs in production.

It works as a growth engine: free calculators and document builders bring contractors in from search, a CMS-driven blog and state licensing pages build authority, a public directory of contractor listings targets local searches, and a free owner app turns a visitor into a known shop with their details already filled in. Every path ends in a tracked lead.

How it was built

The scraping pipeline

The directory is built by a resumable, rate-limited scraping pipeline: browser automation, exponential backoff, and a circuit breaker that stops a run automatically when failures spike. Every business is checkpointed, so a crash resumes exactly where it stopped. Raw results never touch the database. They're held as checksummed artifacts and rewritten by an AI batch job (photo alt text included). Publishing is normally started by hand and is refused unless it targets the right database and passes hard checks; it saves a manifest of every row and image it changed, so a bad publish can be undone. A merge rule stops a weaker re-scrape from overwriting richer content that's already live.

Tools and documents

The free tools are 43 calculators (job pricing, labour rates, electrical and HVAC sizing and more), each written as pure functions and tested, most also against hand-computed industry benchmarks, plus about 80 document builders that export branded PDF, Excel and Word files. The free owner app looks a shop up on Google Maps and opens every builder pre-filled with its details.

Content and SEO

Blog posts and state licensing pages live in Strapi (the licensing pages drafted with the OpenAI Batch API) and sync into Postgres by webhook, which refreshes only the pages that changed, and a renamed or deleted post leaves a permanent redirect. When about a quarter of businesses turned up under more than one trade, I collapsed each to one canonical URL with permanent redirects, so duplicate pages stopped competing with each other in search.

Running it

Leads are tracked from the first visit (UTM and click IDs) through to submission, with Discord alerts and an admin area for leads, analytics and directory operations. It runs on Railway as two services (the web app in Docker, and the CMS), while the scraper runs from a local-only admin panel that's switched off in production. When the hosting bill looked too high, I traced it to Node sizing its memory from the container limit rather than real use.

My role

I built it solo (all but one of the project's 500+ commits), from the architecture and product documentation to the code, the data pipeline and the deployments. Client and product names are left out here.

Highlights
  • 43 trade calculators written as pure functions and tested, most also against hand-computed industry benchmarks
  • About 80 document builders exporting branded PDF, Excel and Word files
  • A resumable, rate-limited scraping pipeline: backoff, a circuit breaker that stops a run when failures spike, per-business checkpoints and stale-lock recovery, so a crashed run picks up where it stopped
  • Checksummed artifacts and a gated publish that can be undone
  • 176 test scripts, including an offline suite for the directory pipeline's publish, merge and rollback
  • Owners can add their business from the directory; an approved request publishes as a listing
  • CMS → Postgres webhook sync with incremental static regeneration and on-demand refreshes
  • Free owner app: emailed-link sign-in, Google Maps business lookup and pre-filled documents
  • Lead attribution (UTM and click IDs), Discord lead alerts, and admin leads and analytics dashboards
Full stack

Frontend

Next.js 16 · React 19 · Tailwind CSS · Framer Motion · Tiptap

Backend & data

Prisma · PostgreSQL · Strapi 5 · Zod · Better Auth · Turborepo

AI & data pipeline

OpenAI Batch API · Playwright · S3-compatible storage (Railway)

Documents

pdf-lib · exceljs · docx

Infra & integrations

Docker · Railway · Resend · PostHog · Discord webhooks

Want to talk about this build?

I'm open to remote full-time roles and freelance work, and happy to walk through the decisions behind it.