AMY scraper
Referral book

Find the agents
who send loans.

Purchase volume comes through real estate agents. A loan officer with twelve productive referral partners has a business; one with none has a marketing budget. This finds those partners — by area, by houses sold, by the price band the lender can actually serve — checks each licence against the state, and hands over a list a person can work through in an afternoon.

What it will not do.

The refusals are in the code, not in a config file, because a flag that turns off a terms-of-use check is a flag that eventually gets turned on by someone in a hurry.

No portals

Zillow, Realtor.com, Redfin, Trulia, Homes.com and the rest prohibit automated collection in their terms of use. Refused before a request is made — the URL never reaches the fetcher.

No MLS or IDX data

An IDX feed is licensed to the broker who receives it, under rules that do not permit re-publication. A lender who builds a book out of one has a contract problem long before it has a data problem.

No ignoring robots.txt

RFC 9309, including the parts that are usually wrong: the most specific user-agent group wins, the longest matching rule wins, and a 5xx on robots.txt closes the host rather than being read as permission. There is no override.

No consent it does not have

Nothing published on a web page is consent to be texted. Every record leaves with texting off until a consent record exists, and carries what CAN-SPAM and the state rules require before the other channels are used.

permit listrobots.txtcrawl-delaysitemap rosterfilter the cardprofilestate registry resolvescorereview

Every number says where it came from.

Nothing is written to a record directly. Values are claimed, with a source, and the record keeps every claim ever made. "How do you know she sold 41 houses?" has an answer, and the honest answers are worth different amounts.

SourceWeightWhat it is
registry100A state licence lookup. An authority, not a marketer.
public_record90A recorded deed or a county index.
jsonld70The site asserting a value in machine-readable form.
microdata65The same, older.
page50Read out of the page's own markup.
text35Inferred from prose.
derived30Computed by us from other fields.

The registry overrules the roster

A brokerage page is a marketing asset and lags reality by months. In the run below the state said one agent's licence had expired while her profile was still live and still advertising 24 sales. Her score is zero, and the record says why.

Corroboration breaks the tie

Two pages of equal authority disagreeing about a sold count is ordinary. The figure more independent sites agree on wins — not the one that happened to be crawled last. The loser stays on the record, flagged, rather than being deleted.

Then someone goes through it.

A scrape is half the job. The other half is one person making three hundred decisions without losing their place — so the console is keyboard-first, every figure expands to its sources, and what the filter dropped is one checkbox away.

  • JK  move  ·  A keep  ·  S skip  ·  D duplicate  ·  U undo
  • Possible duplicates are questions, not decisions. Two records the evidence could not settle are shown side by side with the reasons. Fusing two people is worse than listing one twice.
  • Conflicts sit at the top of the pane. Where two sources disagree, both are shown with their sources rather than one being silently chosen.
  • Decisions persist per run. Closing the tab is not a decision.
  • Then export. A CSV, or POST /v1/leads into the AMY hub, where the lender's existing mapping puts each partner in Salesforce and the LOS.

One run, honestly counted.

The numbers below are from the run in the console — two brokerages, one agent's own site and a state registry, swept with --sold-min 8 --has email,phone --verified. It is the fixture world, not a live territory: a real HTTP server built to contain the awkward parts rather than a tidy demo.

121HTTP requests, 4 hosts
12profiles skipped from the card
50people from 52 records
42licences verified with the state
33kept by the filter
17dropped, each with a reason
13band A — call this week
0textable without consent
What is not confirmed The crawl behaviour, the extraction, the resolution and the scoring are tested — 98 tests against a real HTTP fixture world. The state registry endpoints were written without access to a live commission API and need checking before this points at a lender's territory; they are rows in sources/registries.json, so a wrong one is an edit rather than a rewrite. Production figures published on a brokerage page are marketing copy, and every record says so. The full list travels with the code, and every dataset page repeats the part that affects its data.