Internal tool · 2026

Trawl

Finding the businesses that need a website, with Claude.

Role
Design and engineering
Timeline
Built in a concentrated burst
Status
Private
Year
2026
Trawl's leads table: each row a Singapore business with its website class, an unclaimed-listing flag, rating, and pain points drawn from its reviews.
Trawl's leads table: each row a Singapore business with its website class, an unclaimed-listing flag, rating, and pain points drawn from its reviews.2026

01 / The problem

The problem

The businesses most likely to need a website are the hardest ones to find, because by definition they do not have one to find. They exist as a maps listing, a phone number and a stack of reviews.

The reviews are the interesting part. A restaurant with forty comments complaining that you cannot see the menu online has told you exactly what to build, in its own customers' words. That is a far better opening than a cold template.

02 / What it does

What it does

Trawl scrapes listings for Singapore businesses with no real web presence, pulls their reviews, and runs those through the Claude API to extract recurring, concrete complaints. Not a sentiment score: the specific operational problem a website would solve.

What comes out is a dashboard of qualified prospects with the evidence attached, so any outreach can reference something real.

localhost · internal build
The internal build's landing page. Sample rows only; scraped businesses are never publishedNot deployed

03 / The constraints that shaped it

The constraints that shaped it

A 60-second ceiling. The scrape endpoint runs on a hosting tier that terminates a function at 60 seconds, which is well short of a full crawl. So the endpoint starts the job, returns a run ID immediately, and a separate poll-and-import path collects the result later. Nothing blocks on the crawl.

Cost per lead. Enrichment reads review text from the database and never re-hits the scraping API, so re-running analysis on a prospect costs nothing extra. Getting that boundary wrong is how a lead tool turns into a monthly bill nobody budgeted for.

Small, trusted user base. Sign-in is username and password, because an internal tool with a handful of operators has no business doing inbox round-trips to log in. New accounts sit pending until an admin approves them, and a policy check prevents the last remaining admin from deleting themselves out of the system.

04 / The scope

The scope

This is a working internal tool, not a product. It was built in a concentrated burst on top of patterns I already had, and it carries 39 tests over the paths that would cost real money if they broke.

It is here because it is the clearest example of using a language model as a component rather than as an assistant: structured extraction inside a pipeline, with a defined output shape, a cost ceiling and tests around it. That discipline is the one clients are asking about now.

Stack

  • 01Next.js 15 (App Router), TypeScript
  • 02Supabase: Postgres, Auth, row-level security
  • 03Apify for the Google Maps listing crawl
  • 04Anthropic Claude API for structured extraction
  • 05Vitest and Playwright

Need something in this shape?

Tell me what you are trying to build and roughly when you need it. I reply to every enquiry within one business day, including the ones I am not the right fit for.

Start a project

Replies within one business day