The first directory took months. The second took a weekend, because building the first one properly meant everything niche-specific was already a setting: categories, locations, terminology, pricing, the lot.
That is the whole thesis. Nothing is hard-coded that would stop the same code running a locksmith directory, a roofing directory and a pool cleaning directory at once. It does run all three, from one codebase, with no code changes between them.
A directory only becomes a business when it earns, so there are four ways in and all four are built in rather than bolted on: a lead marketplace where local firms pay monthly, per city, for the quote requests that come in; featured placement on the homepage; featured placement inside a city or a category; and ad slots. The limited number of slots per city is doing quiet work there. Scarcity is most of what makes a local listing worth paying for.

The unglamorous half is filling it. Ten empty categories are not a directory, so the import side took the longest: pulling businesses out of Google Places, scraping their own sites for the detail that is missing, running Claude over the result to clean it up, and a CSV importer that survives thousands of rows and near-duplicate names.

Then it has to run without me. Outreach email goes out at a rate that keeps the sending domain alive, bounces are processed back rather than ignored, and a Telegram bot handles approvals and daily summaries from a phone. When a city has no subscribers yet, the system goes and finds businesses to contact instead of waiting to be asked.
Flask, deliberately. It is boring, it is small, and instances of it have been running unattended for years. It starts on SQLite so it can be unzipped and running the same afternoon, and moves to Postgres on one environment variable. The interesting engineering is in the configuration layer, not the framework.
Recent work
- The Google Places importer moved to the new Places API and runs from an admin page.
- One server can run many directories, each with its own database, domain and nightly backup, from one deploy script.
- A Brand screen. Upload a mark as an SVG and the site draws its own favicon, app icons, logo files and share image from it, and grades your colours for contrast as you pick them.
- Footer credits. An optional maker's signature and an optional "Built with" line, each with its own link and logo.
- API keys are entered in the admin and stored encrypted, and every payment lands in one ledger.

Local Lab is one of three kits sold at DirectoryLab.








