Replacing getAddress.io? Free drop-in replacement →
← Back to Blog
Tutorials Jan 6, 2026 · 11 min read

How to Build a Rental Yield Calculator with the Homedata API

A practical tutorial: combine sold prices, rental comparables, EPC scores, and area trends to calculate gross and net rental yield for any UK property. Full Python and JavaScript code.

What is rental yield?

Rental yield is the annual rental income from a property expressed as a percentage of its value. It's the most important number in buy-to-let investing — it tells you whether a property will generate positive cash flow before you buy it.

Gross yield is the simple calculation: annual rent ÷ property value × 100. A property worth £200,000 generating £12,000/year in rent has a 6% gross yield. That's the number most investors screen by.

Net yield subtracts costs — mortgage interest, management fees, maintenance, insurance, void periods, council tax during voids. The same property might have a net yield of 3.5–4.5% after costs. Net yield is what actually hits your bank account.

The data you need

To calculate yield programmatically, you need three things:

  1. Property value — the last sold price, or comparable recent sales in the area
  2. Rental income — what similar properties are currently listed for, or recent let-agreed prices
  3. Property characteristics — bedrooms, type, EPC score, council tax band (for cost modelling)

The Homedata API gives you all three. Here's how to wire them together.

Step 1 — Get the property and its last sold price

Start with an address. Resolve it to a UPRN, then fetch the enriched property record:

curl "https://api.homedata.co.uk/api/address/retrieve/100023336956/?level=property" \
  -H "Authorization: Api-Key YOUR_API_KEY"

This returns the property with bedrooms, type, EPC score, floor area, and — crucially — last_sold_price from Land Registry. For a more complete sale history, use the property sales endpoint:

curl "https://api.homedata.co.uk/api/property_sales/?uprn=100023336956" \
  -H "Authorization: Api-Key YOUR_API_KEY"

Response includes every Land Registry transaction — price, date, tenure, new build flag. Use the most recent sale as your baseline valuation, or average the last 2–3 if you want to smooth out timing effects.

Step 2 — Estimate rental income from comparables

The comparables endpoint finds similar properties that have recently been listed or let nearby:

curl "https://api.homedata.co.uk/api/comparables/100023336956/?event_type=listed&count=20" \
  -H "Authorization: Api-Key YOUR_API_KEY"

Use event_type=listed to get properties with recent listings. Each comparable's latest_listing includes a transaction_type field — filter for "Rental" to isolate rental comparables. The latest_listing.latest_price is the asking rent. Average the asking rents of the 10–20 closest matches (weighted by distance and bedroom count) to estimate monthly rental income.

Step 3 — Calculate gross yield

With sold price and estimated rent, the calculation is simple:

import requests

API_KEY = "YOUR_API_KEY"
BASE = "https://api.homedata.co.uk"
HEADERS = {"Authorization": f"Api-Key {API_KEY}"}
UPRN = 100023336956

# 1. Get property + last sold price
prop = requests.get(f"{BASE}/api/address/retrieve/{UPRN}/?level=property",
    headers=HEADERS).json()

# 2. Get comparable properties nearby (sale + rental listings)
comps = requests.get(f"{BASE}/api/comparables/{UPRN}/",
    params={"event_type": "listed", "count": 20},
    headers=HEADERS).json()

# 3. Filter for rental listings and average asking rents (monthly)
rents = [c["latest_listing"]["latest_price"]
         for c in comps["comparables"]
         if c.get("latest_listing", {}).get("latest_price")
         and c["latest_listing"].get("transaction_type") == "Rental"]

if rents:
    avg_monthly_rent = sum(rents) / len(rents)
    annual_rent = avg_monthly_rent * 12

    # Last sold price as property value
    property_value = prop.get("last_sold_price", 0)

    if property_value > 0:
        gross_yield = (annual_rent / property_value) * 100
        print(f"Property: {prop['address']}")
        print(f"Bedrooms: {prop.get('bedrooms')}")
        print(f"Last sold: £{property_value:,.0f}")
        print(f"Est. monthly rent: £{avg_monthly_rent:,.0f}")
        print(f"Gross yield: {gross_yield:.1f}%")

Step 4 — Add cost modelling for net yield

Gross yield is the headline, but net yield is what matters. Here's a realistic cost model:

# Cost assumptions (adjust per property/strategy)
MANAGEMENT_FEE_PCT = 0.10    # 10% of rent (letting agent)
MAINTENANCE_PCT = 0.05        # 5% of rent (repairs/upkeep)
INSURANCE_ANNUAL = 300        # Buildings + landlord liability
VOID_WEEKS = 3                # Average void per year
MORTGAGE_RATE = 0.055         # 5.5% BTL rate
LTV = 0.75                   # 75% LTV

# Annual costs
mortgage_annual = property_value * LTV * MORTGAGE_RATE
management = annual_rent * MANAGEMENT_FEE_PCT
maintenance = annual_rent * MAINTENANCE_PCT
void_cost = (annual_rent / 52) * VOID_WEEKS
total_costs = mortgage_annual + management + maintenance + INSURANCE_ANNUAL + void_cost

# Net yield
net_income = annual_rent - total_costs
net_yield = (net_income / (property_value * (1 - LTV))) * 100  # On equity invested

print(f"\n--- Net yield breakdown ---")
print(f"Annual rent:       £{annual_rent:,.0f}")
print(f"Mortgage:         -£{mortgage_annual:,.0f}")
print(f"Management (10%): -£{management:,.0f}")
print(f"Maintenance (5%): -£{maintenance:,.0f}")
print(f"Insurance:        -£{INSURANCE_ANNUAL:,.0f}")
print(f"Void ({VOID_WEEKS} weeks):   -£{void_cost:,.0f}")
print(f"Net income:        £{net_income:,.0f}")
print(f"Net yield on equity: {net_yield:.1f}%")

Step 5 — Enrich with EPC and council tax

Two more data points that materially affect yield:

EPC score — Properties below EPC E can't legally be let under MEES regulations. An EPC score of 38 or below means you'll need to spend on insulation or heating upgrades before you can rent. Factor the upgrade cost into your purchase price model.

# EPC check — MEES compliance
epc = requests.get(f"{BASE}/api/epc-checker/{UPRN}/",
    headers=HEADERS).json()

epc_score = epc.get("current_energy_efficiency", 0)
if epc_score < 39:  # Band F or G
    print(f"⚠️  EPC {epc_score} — below MEES minimum (E). "
          f"Budget £5,000–15,000 for upgrades before letting.")
elif epc_score < 55:  # Band E
    print(f"EPC {epc_score} — Band E. Legal to let, but Band C "
          f"may become the minimum. Consider pre-emptive upgrades.")
else:
    print(f"EPC {epc_score} — compliant. No upgrade costs.")

Council tax band — During void periods, the landlord pays council tax. Higher bands in expensive areas can eat 2–3% of annual rent during a 4-week void. The council tax band API returns the band (A–H) so you can model void costs accurately.

Practical patterns

Pattern 1: Screen an entire postcode for yield hotspots

Use price trends to identify outcodes with low average prices but healthy rental demand:

# Compare outcode-level price trends
for outcode in ["LS6", "BS5", "M14", "SE15", "E17"]:
    trends = requests.get(f"{BASE}/api/price_trends/{outcode}/",
        headers=HEADERS).json()

    # monthly_average_prices is a dict of "YYYY-MM" → median price
    prices = trends.get("monthly_average_prices", {})
    latest_month = max(prices.keys()) if prices else None
    latest_price = prices[latest_month] if latest_month else 0
    volatility = trends.get("volatility_score", 0)

    print(f"{outcode}: latest median £{latest_price:,.0f} ({latest_month}), "
          f"volatility {volatility:.1f}")

Low average price + low volatility + proximity to universities or hospitals = classic yield-first buy-to-let territory. LS6 (Headingley, Leeds) and BS5 (Eastville, Bristol) are textbook examples.

Pattern 2: Portfolio-level yield monitoring

If you already own properties, run the yield calculation across your portfolio monthly:

portfolio_uprns = [100023336956, 200004178233, 10093048274]

for uprn in portfolio_uprns:
    prop = requests.get(f"{BASE}/api/address/retrieve/{uprn}/?level=property",
        headers=HEADERS).json()
    comps = requests.get(f"{BASE}/api/comparables/{uprn}/",
        params={"event_type": "listed", "count": 10},
        headers=HEADERS).json()

    rents = [c["latest_listing"]["latest_price"]
             for c in comps["comparables"]
             if c.get("latest_listing", {}).get("latest_price")
             and c["latest_listing"].get("transaction_type") == "Rental"]

    if rents and prop.get("last_sold_price"):
        avg_rent = sum(rents) / len(rents)
        gross = (avg_rent * 12 / prop["last_sold_price"]) * 100
        print(f"{prop['address'][:40]:40s} | "
              f"£{avg_rent:,.0f}/mo | {gross:.1f}% gross")

JavaScript version

The same calculation in Node.js for web application backends:

const API_KEY = 'YOUR_API_KEY';
const BASE = 'https://api.homedata.co.uk';
const headers = { 'Authorization': `Api-Key ${API_KEY}` };

async function calculateYield(uprn) {
  const [propRes, compsRes] = await Promise.all([
    fetch(`${BASE}/api/address/retrieve/${uprn}/?level=property`,
      { headers }),
    fetch(`${BASE}/api/comparables/${uprn}/?event_type=listed&count=20`,
      { headers }),
  ]);

  const prop = await propRes.json();
  const comps = await compsRes.json();

  const rents = comps.comparables
    .filter(c => c.latest_listing?.latest_price
      && c.latest_listing.transaction_type === 'Rental')
    .map(c => c.latest_listing.latest_price);

  if (!rents.length || !prop.last_sold_price) return null;

  const avgMonthlyRent = rents.reduce((a, b) => a + b, 0) / rents.length;
  const annualRent = avgMonthlyRent * 12;
  const grossYield = (annualRent / prop.last_sold_price) * 100;

  return {
    address: prop.address,
    bedrooms: prop.bedrooms,
    lastSoldPrice: prop.last_sold_price,
    avgMonthlyRent: Math.round(avgMonthlyRent),
    grossYield: grossYield.toFixed(1),
    epcScore: prop.current_energy_efficiency,
    comparablesUsed: rents.length,
  };
}

// Usage
calculateYield(100023336956).then(console.log);

API endpoints used

Endpoint What it provides Auth
/api/address/retrieve/{uprn}/ Property record — bedrooms, type, EPC, last sold price Api-Key
/api/property_sales/ Full Land Registry transaction history Api-Key
/api/comparables/{uprn}/ Similar properties — filter latest_listing.transaction_type for "Rental" or "Sale" Api-Key
/api/epc-checker/{uprn}/ EPC score — MEES compliance check Api-Key
/api/price_trends/{outcode}/ 12-month area price trends + volatility Api-Key

Get started: Create a free API key (100 calls/month, no credit card) and try the yield calculation on a property you know. The Getting Started guide will have you making API calls in under 5 minutes.