Pure-Python core library + FastAPI service for UK property data (PPD, EPC, Rightmove, Planning).
Project description
Property Shared
FastAPI service + pure-Python core library for UK property data. Integrates Land Registry (PPD), EPC, Rightmove, Planning portal lookup (98 councils), Stamp Duty calculator, Block Analyzer, and Companies House. Use as a library, HTTP API, CLI, or MCP server.
How to run
Dev (uv)
- Create
.envfrom.env.example(setEPC_API_EMAIL/EPC_API_KEYif you want EPC enabled) - Install deps:
uv sync --extra dev - Run API:
uv run property-api(oruv run uvicorn app.main:app --reload) - CLI (core mode):
uv run --extra cli property-cli meta(add--api-url http://localhost:8000to hit the running API instead of local core) - Demo UI: visit
http://localhost:8000/demo(served by the same FastAPI app) - Quick checks:
- Health:
curl http://localhost:8000/v1/health - Integration status:
curl http://localhost:8000/v1/meta/integrations - Rightmove:
curl 'http://localhost:8000/v1/rightmove/search-url?postcode=SW1A%201AA&radius=0.25'thencurl 'http://localhost:8000/v1/rightmove/listings?search_url=<pasted_url>&max_pages=1' - PPD address search:
curl 'http://localhost:8000/v1/ppd/address-search?postcode_prefix=B1&street=Broad%20Street&limit=5' - PPD comps:
curl 'http://localhost:8000/v1/ppd/comps?postcode=NG1%201GF&months=24&limit=5' - Yield analysis:
curl 'http://localhost:8000/v1/analysis/yield?postcode=NG1%201AA' - Rental analysis:
curl 'http://localhost:8000/v1/analysis/rental?postcode=NG1%201AA'
- Health:
Live integration tests
Live tests make real network calls and are gated:
- Run:
RUN_LIVE_TESTS=1 uv run --extra dev pytest -q -s
Fly.io (high-level)
- Set secrets:
fly secrets set EPC_API_EMAIL=... EPC_API_KEY=... - Deploy:
fly deploy
Python SDK (OpenAPI)
Generate a typed client from the running service OpenAPI:
- Run the API:
uv run uvicorn app.main:app --reload - Generate client:
uv run --extra dev openapi-python-client generate --url http://localhost:8000/openapi.json --output-path clients/python
Structure
property_core/– pure-Python core library (no FastAPI, no DB/Redis assumptions)models/– domain Pydantic models (PPDTransaction, EPCData, PropertyReport, YieldAnalysis, etc.)ppd_client.py– transport: Land Registry SPARQL + Linked Data APIepc_client.py– transport: EPC registry (async)rightmove_scraper.py– transport: listings scraper (sync)rightmove_location.py– transport: search URL builderpostcode_client.py– transport: postcodes.iocompanies_house_client.py– transport: Companies House APIppd_service.py– domain service: PPD comps, search, statsplanning_service.py– domain service: council matching + URL buildingreport_service.py– orchestrator: multi-source aggregation (async)rental_service.py– rental analysis with optional yield calculation (async)yield_service.py– yield analysis: PPD sales + Rightmove rentalsinterpret.py– opt-in interpretation helpers (yield labels, data quality, insights)enrichment.py– EPC enrichment pipeline for PPD compsaddress_matching.py– fuzzy address matching for EPC enrichmentstamp_duty.py– SDLT calculator (April 2025 bands)block_service.py– groups transactions by buildingplanning_scraper.py– vision-guided planning portal scraper (Playwright + OpenAI)planning_councils.json– verified council database (98 councils, 6 system types)
app/– FastAPI service wrapping property_coreapi/v1/– versioned routers (import directly from property_core)schemas/– API envelope models (convenience re-exports from core)core/config.py– settings via pydantic-settings
property_cli/– Typer CLI with dual mode (core direct vs API)mcp_server/– MCP server for AI hosts (12 tools wrapping property_core)docs/– examples and reference documentationUSER_GUIDE.md– quickstart and endpoint/CLI usage
Using as a library
pip install property-shared
from property_core import (
PPDService, PropertyReportService, PlanningService,
EPCClient, CompaniesHouseClient,
calculate_yield, analyze_rentals, analyze_blocks, calculate_stamp_duty,
fetch_listings, fetch_listing,
)
# Opt-in interpretation helpers (core returns raw numbers; consumers classify)
from property_core import (
classify_yield, classify_data_quality, classify_price_position,
estimate_value_range, generate_insights,
)
# Models available at top level
from property_core import (
PPDTransaction, PPDCompsResponse, EPCData,
RightmoveListing, PropertyReport, YieldAnalysis,
BlockAnalysisResponse, CompanyRecord, StampDutyResult,
)
Local setup
- Copy
.env.exampleto.envand fill values (EPC keys, OPENAI_API_KEY for planning scraper) - Install deps:
uv sync --extra dev - Run:
uv run property-api(oruv run uvicorn app.main:app --reload)
Notes
- Transport-parsed models (PPDTransaction, EPCData, RightmoveListing, CompanyRecord, PostcodeResult) carry a
rawfield with original source data. Report, block, and aggregate models do not. - Rightmove scraper has built-in rate limiting via
rate_limit_secondsparameter. - Rightmove search URLs default to a small radius (0.25 miles); override
radiusto widen/narrow. - Station distances in listing details are rounded to 1 decimal place (e.g., "1.9 miles").
- Rental analysis (
analyze_rentals) uses IQR-based outlier filtering by default (filter_outliers=True). - Core services return raw numbers only. Use
property_core.interprethelpers (classify_yield,classify_data_quality,generate_insights, etc.) for human-readable labels and assessments. - See
docs/examples.mdfor copy-paste usage examples with real output.
API endpoints (summary)
GET /v1/health→{ "status": "ok" }GET /v1/meta/integrations→{ environment, integrations: { ppd|rightmove|epc: { available, configured } } }GET /v1/ppd/download-url?kind=complete|monthly|year&year?&part?&fmt=csv|txt→{ url }GET /v1/ppd/transactions?postcode|postcode_prefix&limit&filters...&include_raw=bool→{ count, limit, offset, results, warnings, raw? }GET /v1/ppd/address-search?paon?&saon?&street?&town?&...&limit&include_raw=bool(requires >=2 fields, limit<=50)GET /v1/ppd/comps?postcode&property_type?&months?&limit?&search_level&auto_escalate&enrich_epc→{ query, count, median, mean, min, max, thin_market, transactions }GET /v1/ppd/blocks?postcode&months?&limit?&min_transactions?&search_level?→{ postcode, blocks_found, blocks }GET /v1/ppd/transaction/{id}?view=all|basic&include_raw=bool→{ record, raw? }GET /v1/epc/search?postcode&address?&include_raw=bool→{ record, raw? }GET /v1/epc/certificate/{hash}?include_raw=bool→{ record, raw? }GET /v1/rightmove/search-url?postcode&property_type=sale|rent&radius?&filters...→{ url }GET /v1/rightmove/listings?search_url&max_pages?→{ count, results }GET /v1/rightmove/listing/{property_id}→{ result }GET /v1/analysis/yield?postcode&months?&search_level?&radius?→YieldAnalysisGET /v1/analysis/rental?postcode&radius?&purchase_price?→RentalAnalysisGET /v1/calculators/stamp-duty?price&additional_property&first_time_buyer&non_resident→StampDutyResultGET /v1/companies/search?q&limit?→ company resultsPOST /v1/property/reportbody:{ address, include_rentals?, include_sales_market?, ppd_months?, search_radius? }→PropertyReport- Planning routes exist in code but are disabled — scraping requires a UK residential IP.
CLI commands (core mode; add --api-url to hit the API)
property-cli meta– integration statusproperty-cli ppd comps "SW1A 1AA" --months 24– comparable salesproperty-cli ppd comps "B1 1BB" --enrich-epc– comps with EPC enrichmentproperty-cli ppd search --postcode-prefix SW1A --limit 10– transaction searchproperty-cli ppd address-search --postcode "SW1A 2AA" --street "Downing Street"– address searchproperty-cli ppd transaction <ID>– single transaction recordproperty-cli ppd blocks "B1 1AA"– flat blocks analysisproperty-cli epc search "SW1A 1AA"– EPC lookupproperty-cli rightmove search-url "SW1A 1AA"– build search URLproperty-cli rightmove listings --search-url "<url>"– fetch listingsproperty-cli rightmove listing 161151632– listing detailproperty-cli analysis yield "NG1 1AA"– rental yield analysisproperty-cli analysis rental "NG1 1AA"– rental market analysisproperty-cli calc stamp-duty 300000– stamp duty calculationproperty-cli companies search "Tesco"– Companies House lookupproperty-cli report generate "10 Downing Street, SW1A 2AA"– full property report
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters