Relocation guides, lead capture, and local intelligence.

A Python platform that turns regional information, first-party behavior and reviewed client signals into usable lead workflows.

The system connected content, data, and follow-up.

The build was not only a website. It connected static relocation guides, lead forms, scoring rules, analytics events, public records, and extracted signals into a portal that could support actual follow-up.

Two Southwest Virginia regions were in scope, and they were modelled as genuinely separate places rather than one market with a filter on top. The New River Valley covers Montgomery, Radford City, Pulaski and Floyd. The Roanoke Metro Area covers Roanoke County, Roanoke City and Salem. Each got its own towns, its own accent colour and its own standalone site, because a buyer relocating to Blacksburg is not reading the same page as a buyer relocating to Salem.

Domain
Regional real estate and relocation intelligence
Core
Guide sites, lead capture, review queues, and follow-up workflows
Stack
Python, Flask, Postgres, Typer, static HTML/CSS/JS

A small intelligence system around a real sales motion.

01

Two static relocation guide sites.

Place-first homepages, a library of topic guides, a cost-of-living calculator, a region-wide business directory, and an interactive map drawn from real boundary geometries with census-tract overlays. Per-area, subarea and local-resource pages go down to the town level, and every page carries a note about where its data came from and how fresh it is.

02

No framework on the front end.

The sites are hand-written HTML, CSS and JavaScript emitted by a Python build command. That keeps them fast to serve and cheap to host, which matters when the whole point of the guides is to be found in search and shared in a text message.

03

Lead capture and scoring.

The guide forms post to a self-hosted webhook rather than a third-party form service. Leads are scored as they arrive, normalised into a consistent record, and exported to a CSV shaped for the CRM the team already used.

04

First-party analytics.

A cookie-based visitor tracker and a funnel dashboard, built in rather than bolted on. No third-party trackers were required, so the behavioural signal feeding the lead score stayed in the same database as the leads themselves.

05

An operator portal with review built in.

A Flask application with login and role scoping over the lead dashboards, guide readiness, guide content, and previews that do not publish. Signals surface here for a person to accept or discard; nothing reaches an agent unreviewed.

How a public record becomes a reviewed lead.

The interesting engineering is not the scraping. It is everything between a raw public record and something an agent should act on.

Public hiring and press sources are ingested, then structured by a language model rather than by brittle per-site parsers, because the sources change layout constantly and a parser that breaks silently is worse than one that never existed. Extracted records are cross-referenced against county parcel ownership, enriched from public professional directories, and then passed through identity resolution, which is the step that decides whether two similarly named records are one person or two.

Only after that does anything become a ranked signal, and even then a person reviews it before it is treated as a lead. That ordering is deliberate. A signal that is wrong about who someone is does not waste a minute of an agent's time; it costs them a relationship.

The test suite records real HTTP interactions as fixtures and replays them, so the pipeline can be tested end to end without touching a live source. It also means a source changing its markup shows up as a failing test rather than as a quiet gap in the data.

The difficulty was provenance, not volume.

01

Two regions, one codebase.

Regions had to stay genuinely separate in the output while sharing every importer, model and build path. The lead pipeline's market attribute is a different concept from the site region, and conflating the two would have quietly mixed one region's leads into the other's reporting.

02

Identity is the expensive problem.

Names collide, records are stale, and parcel data uses its own conventions. Getting entity resolution wrong does not produce an obviously broken system — it produces a confident one that is wrong, which is far more costly.

03

Freshness has to be visible.

Public data ages at different rates. Rather than hide that, every guide page states its sources and how current they are, so a reader can judge the number instead of trusting it blindly.

The useful lesson: local knowledge becomes more valuable when it becomes operable.

This is the data-work shape Seam likes: take scattered public and first-party signals, build a usable workflow around them, and keep the final system plain enough that a small team can work it every week. The parts of this build that survived into Seam's own products were not the clever ones. They were the review queue, the provenance notes, and the decision to make a person confirm a signal before it became a lead.