Project · Design and build
Buy-to-Let Deal Finder
A private tool that answers the question a property listing never does: if I bought this to let, what would it actually return? It works out the rent, the stamp duty, whether a lender would lend, the tax and the cash left over each month, then scores the listing out of 100 and shows its working. Every figure is checked to the penny against sums worked out by hand.
Reading level
The plain-English version. Every section also has a technical note.
What it is
A property site shows the asking price. It does not show what the property would earn, and working that out used to take a spreadsheet session per listing — long enough that the good ones went before I got to them.
This does it in the time a page takes to load. Open a listing in the browser and a panel appears beside it: the likely rent and the lets it was worked out from, the stamp duty, the cash needed to buy, the monthly cash flow before and after tax, the return on that cash, whether a lender's stress test passes, and a score with the reasons behind it. On a page of search results every listing gets a small badge instead, and filters dim the ones that miss my thresholds.
Behind the browser there is a private website: every listing the tool has seen, ranked; saved deals; up to four side by side; a deal page where changing the rent, the refurbishment cost or the mortgage rate recalculates everything as you type; and a one-page print-out for a lender or a partner, with its assumptions, its sources and a disclaimer on it.
Two ways to buy, one set of sums
It models a limited company buyer, two ways. A single let: buy with a 75% interest-only mortgage and let it. And buy, refurbish, refinance, rent — buy something tired, improve it, borrow against the higher value and take most of the cash back out. For that one the figure that matters is the money left in after the refinance, and when it reaches nothing the tool says all the cash was recycled rather than dividing by zero and reporting an infinite return. When a listing's wording suggests no mortgage lender would touch it — cash buyers only, no kitchen, non-standard construction, an auction — the purchase switches to a bridging loan, with its monthly interest and its fees, and the page says so.
One flat, several sites
The same flat is often advertised on more than one property site, sometimes at different prices, and occasionally twice on one site by two agents. It is stored once, with a link to each. Two listings are matched on the opening of their own words, because agents send the same description everywhere: at least half of the three-word phrases in the first 120 words in common, and only between listings with the same bedrooms within sixty metres of each other. The same flat scores about 0.98; two different flats from one agent, between 0.02 and 0.27. The closing paragraphs are ignored, since they are an agent's standard notes and identical on everything it sells. A search result is never matched on its own: flats in one block share a map pin and often a price, so the listing is read first and its words decide.
How it is put together
One Cloudflare Worker serves the website and the interface the extension talks to, and runs the scheduled work: a queue checked every minute and a nightly job. One database holds the listings, the lets, the sold prices and the area data for the postcode districts being searched. The browser extension and the server read a listing with the same page readers, and saved copies of real pages are kept as test fixtures, so a site that changes its layout shows up as a failing test rather than as wrong numbers. A page it cannot read reliably says "Couldn't read this page" and shows no figures at all, rather than guessing.
Sums that match to the penny
The money is one small library, worked in whole pence, and before anything else was built it had to reproduce a set of cases worked out by hand — exactly.
It was the first thing built, because everything else depends on it and it depends on nothing. Stamp duty at the rates for an additional property. A lender's stress test, which asks whether the rent would still cover the mortgage interest at a higher, stressed rate with a margin to spare, and reports the lowest rent that would pass and the largest loan this rent supports. Corporation tax, with the interest deductible because the buyer is a company. And cash flow, before and after that tax.
One of the hand-worked cases: a £150,000 flat let at £1,100 a month, bought with a 75% interest-only mortgage at 6.4%. Stamp duty £8,000. Cash put in £49,000. Interest of £7,200 a year and running costs of £4,718, leaving a profit of £1,282 and corporation tax of £243.58. Cash flow after tax: £86.54 a month. The gross yield — the figure a listing would lead with — is 8.8%, which sounds excellent. The return on the £49,000 actually put in is 2.1%. That gap is the reason the tool exists.
Where the pennies go
Every figure is held as a whole number of pence and rounded half away from zero, at named points only. Stamp duty is rounded down to the pound, as HMRC does. Monthly cash flow after tax is the annual figure divided by twelve and rounded once. In the case above, working month by month — £106.83 before tax, less £20.30 of tax — gives £86.53; dividing the year's £1,038.42 once gives £86.54, the figure worked by hand. A penny out is exactly the kind of error that makes somebody stop trusting every other number on the page.
One library, two places
The same library runs on the server and in the browser. When a figure is changed on the deal page, it is recalculated by the very code that produced the first answer, not by a second copy of the arithmetic that could quietly disagree with it. It has no side effects and no dependencies of its own, which is what makes it cheap to test, and the money is the part that has to be right.
A rent estimate that says how wrong it is
The rent is the number everything else hangs on, so it is estimated from real lets nearby, tested every day on those same lets, and shown with the accuracy the test measured.
Asking rents are collected from lets on the property sites and pooled for everybody using the tool. Before one is used it is cleaned: rooms, shares, student lets, holiday lets and bills-included lets are never kept, and a rent wildly out of line with the area is dropped rather than averaged in. A property's rent is then worked out from lets with the same bedrooms and the same kind of home within two miles, weighted towards the nearest, the most similar in size and the most recent, with extra weight for the same building or the same postcode.
Every day, and after each fresh batch of lets, the method is tried on the lets themselves: each one is estimated from all the others as though it were the property being valued, and compared with what it actually asks. That test decides two things. An adjustment — for parking, a garden, a balcony, a lift, a sea view, refurbishment or furnishing — is kept only if it makes the estimates more accurate. And the low-to-high range is set wide enough that about eight real rents in ten fall inside it.
The same treatment for service charges
A leasehold flat is never costed at an unknown £0 a year. A charge the listing states is read from it — per month, per quarter, per half year, "peppercorn", "nil" — and one it does not state is estimated from flats nearby that do: the same building first, then the same postcode, then widening circles. The money uses the cautious end of that estimate, the 75th percentile, while the page shows the typical figure and the likely range beside it. Tested the same way as the rent on 28 September 2026, service charges came out typically 29% adrift with 78% inside the likely range, and ground rent typically £92 adrift with 82% inside.
Size, without a house number
Size matters to a rent, and listings often leave it out. When they do, it comes from the public energy certificate for the home — but listings rarely publish the house number, so the certificate cannot simply be looked up. It is matched instead as the one certificate in that postcode with the same kind of home, the same number of rooms and, where stated, the same energy rating. Failing that, the typical size of the matching homes in the postcode; failing that, the typical size on the street. The page says which of the four it used.
A score that shows its working
Each listing gets a score out of 100, always shown with the three reasons pushing it up and the three pulling it down, and red flags that no score is high enough to hide.
Sixty points are for returns: the return on the cash put in, the monthly cash flow after tax, the gross yield, and for a refinance deal the money left in. Fifteen are for the price against what similar homes nearby have actually sold for over the last two years, brought up to date with the house price index. Twenty-five are for the area.
The area is scored differently for different homes, because different people rent them. For a house with three or more bedrooms, schools carry 35% of the area score; for a flat, 5%, and transport carries 30% instead. Crime, deprivation, the share of homes rented privately and broadband make up the rest.
Red flags sit apart from the score and are shown whatever the score is: a failed stress test, a high flood risk, a lease under 80 years, an energy rating too low to let legally, a school judged a concern near a house, a purchase that probably needs a bridging loan, and a rent worked out from too few lets to trust.
Why a failed stress test caps the score
A deal whose rent would not satisfy a lender at the chosen settings is capped at 40 and labelled "Finance doesn't work as modelled". Without the cap, the most attractive-looking returns would come from exactly the loans nobody will give, and the score would be at its most confident where it is most wrong.
Signs a seller may take less
A separate flag reads the signs, from what is already stored, that a seller may accept a lower offer: price cuts measured over time, months on the market, a sale that fell through, wording such as "must be sold", the same home relisted with a second agent, or an auction lot still unsold after the sale. Each sign carries a weight, and one strong sign is enough on its own. It never visits a site to look for them.
Public data, filtered to one town
All of the area data is free and official: sold prices, the house price index, private rents, postcodes, energy certificates, schools and their inspections, crime, the census, deprivation, stations and bus stops, broadband and flood risk. Only the postcode districts being searched are kept, which is what keeps the database small. School inspections moved from one-word grades to report cards in November 2025, so the two will be mixed for years: each is shown as published, with its date, and grouped into three levels for scoring only.
Limits written down before they were needed
It runs on a five-dollar-a-month hosting plan with a written ceiling it may not approach without asking, and it reads property sites the way a patient person would rather than the way a crawler does.
The plan includes a fixed allowance of requests, processing time, and database reads and writes. The standing rule is that nothing — a big data load, a new scheduled job, a new table or index, anything that loops over listings — is done without first estimating what it will use against that allowance, and stopping to ask if it could go over. The loader enforces its own share: it refuses any load estimated at more than half a million row writes unless that one run has been explicitly allowed.
That rule has a history. An early full load of the energy certificate data used a whole day's free allowance in one go, because the database counts every index entry as a write. Since then every load compares what it is about to write with what is already there, and writes only the rows that are new, changed or gone. A normal month is a few hundred rows.
It reads the pages I open. Beyond those, it opens pages only for a search I start from the website, or one saved to re-run overnight — and then slowly: one page every twenty seconds across every site together, with a user agent that says what it is. A failed visit ends that run; two in a row wait an hour; three, or a page it cannot read, pause that site for a day. A listing already read at the same price is not opened again. There are no proxies and no disguised browsers, and a site that answers automatic visits with a challenge is simply not supported. The tool does not try to get past it.
What it keeps, and what it will not
Only listing facts: price, type, bedrooms, tenure, size, location and the listing's own words. No photos, no floorplans, no agent details. The energy certificate register holds personal data, so only the rating, floor area, type, date and property reference are kept, never a name or an address. Sizes kept per home carry the postcode but never the house number or the street, and typical sizes by street are stored only against a one-way hash of the street, for streets with three homes or more. Auction lots are published with full addresses; they are stored without the house or flat number.
Built from a plan, and changed in writing
I wrote the plan on a Friday. Claude built it over the next three days, milestone by milestone, and every time the plan changed, the change was written down with its date and the decision behind it.
The plan set out eight milestones, each to work on its own before the next began: the money first, because it depends on nothing, then the public data, the panel in the browser, the area measures, the score and the badges, and the website. Those six landed in that order. The seventh, reading the property sites' own alert emails, was set aside before it was started, and searches started from the website and re-run each night took its place. The eighth, the other sites, came in half: one was added, one refuses automatic visits and was dropped, and two auction houses arrived that the plan never mentioned.
None of that is a plan failing. It is a plan meeting the thing it was a plan for. What matters is that each change is recorded in the standing instructions the build works to — the date, what it replaced, and that I agreed it — so a rule can be overridden without being lost, and the next change starts from what is actually true rather than from what the plan once said.
Who did what
The plan, the rules and every decision are mine. The code and its tests were written by Claude, working to a short list of standing rules it has to stop and ask about rather than decide for itself: the cost ceiling, the visit limits, what may be stored, and the disclaimer on every deal. Nearly three hundred unit tests run on every change, the money library's hand-worked cases among them, and the website and the browser extension each have a test that drives them in a real browser, run by hand.
Where it has got to
It is running, it is private, and it covers one town. There is no demo: the listings it reads are not mine to publish.
It covers Bournemouth's eleven postcode districts, for a buyer that is a limited company, and it is built for me and a handful of people I trust, who sign in with a one-time code sent by email. The browser extension is desktop only; the website works on a phone.
Not built yet: the alert inbox, a quick-entry form for a phone, and buying in a personal name, which is taxed differently enough to need its own model. Deliberately not built at all: houses in multiple occupation, holiday lets, and anywhere outside England, where the property taxes are different. Every deal page and print-out says what the tool is not: estimates for illustration only, not financial, tax or mortgage advice.
What I expect to be wrong first
The rent, and in a direction its own test cannot see. The accuracy it reports is measured against asking rents, because asking rents are what is published, and asking rents can run above what a tenant ends up paying. The test is honest about how far the estimate is from the asking rent and silent about how far the asking rent is from the real one. The other blind spot is condition: the tool knows a property needs work only from words like "needs modernisation" or an energy rating of E or worse, and a listing written by a good agent says neither.