DEV Community

Berkcan Kapusuzoglu
Berkcan Kapusuzoglu

Posted on

How I Built a Rental Property Deal Analyzer with FastAPI and AI

I spent months evaluating rental properties with spreadsheets and paid calculator subscriptions before deciding to build my own tool. The result is a free, open-source web app that calculates 20+ investment metrics, scores deals on a 14-point system, projects 5-year returns, and runs optional AI analysis -- all from two files with zero build step.

Live demo: https://rental-property-deal-analyzer.onrender.com
GitHub: https://github.com/berkcankapusuzoglu/Rental-Property-Deal-Analyzer

The Problem

If you are evaluating rental property investments, you need answers to basic questions: What is the cash-on-cash return? Does it pass the 1% rule? What is the DSCR? What is the total return over 5 years when you factor in appreciation, debt paydown, and tax benefits?

Paid tools like DealCheck charge $10-20/month. Spreadsheets are fragile and tedious. I wanted something that was fast, thorough, and free.

The Solution

A 6-step wizard that walks you through property info, financing, income, expenses, review, and results. You can paste a Zillow URL to auto-fill data or enter everything manually. In about 2 minutes you get a full dashboard with:

  • 20+ metrics: CoC, Cap Rate, DSCR, NOI, GRM, OER, Break-Even Occupancy, monthly cash flow, and more
  • 14-point deal scorecard: 7 factors scored individually with plain-English reasoning
  • 5-year total return: Broken into Cash Flow + Appreciation + Debt Paydown + Tax Benefits
  • Strategy fit: Cash Flow / Wealth Building / Low Risk / BRRRR analysis
  • AI narrative analysis: Optional, using free local LLMs or Claude API

Technical Deep-Dive

Single-File Architecture

The entire frontend is one HTML file: ~2,800 lines of inline CSS, HTML, and JavaScript. The backend is one Python file: ~1,000 lines of FastAPI.

Why? Because rental property analysis is fundamentally a calculation problem, not a UI framework problem. The state is simple (form inputs go in, metrics come out), there is no routing, and the interactivity is a linear wizard. React or Vue would add a build step, a node_modules folder, and hundreds of dependencies for zero benefit.

The JS is organized as an IIFE (Immediately Invoked Function Expression) that exposes only what needs to be global (window.runAI, window.scrapedData). Inside the closure: DOM helpers, state management, wizard navigation, the calculation engine, an amortization builder, a chart renderer, and event listeners.

# The entire backend is 3 routes + 1 streaming endpoint
app = FastAPI()

@app.get("/")                          # Serve index.html
@app.post("/api/scrape")               # Zillow data extraction
@app.post("/api/analyze-ai")           # AI analysis (non-streaming)
@app.post("/api/analyze-ai-stream")    # AI analysis (SSE streaming)
@app.get("/api/models")                # List available AI models
Enter fullscreen mode Exit fullscreen mode

Zillow Scraping: 4 Extraction Strategies

Zillow embeds property data in a __NEXT_DATA__ JSON blob in their HTML, but the structure varies. After reverse-engineering multiple listing pages, I identified four distinct schemas:

Strategy A -- gdpClientCache: The most common. Data lives under props.pageProps.componentProps.gdpClientCache, keyed by a GraphQL-style cache key.

Strategy B -- apiCache: Same concept but under apiCache. Different key structure, different nesting.

Strategy C -- Direct pageProps: Sometimes the property data sits directly in props.pageProps.property without any cache layer.

Strategy D -- componentProps: A fallback where data is nested in component-specific props.

Each strategy extracts and normalizes the same fields: price, beds, baths, sqft, lot size, year built, home type, tax history, zestimate, and rent zestimate.

The scraper tries httpx first with realistic browser headers. When Zillow blocks the request, it falls back to Playwright headless Chromium. Even Playwright can get CAPTCHAd, so manual entry is always the final fallback.

AI Streaming with SSE

The /api/analyze-ai-stream endpoint provides real-time streaming from three different AI providers through a unified interface.

Each provider has its own SSE format:

  • LM Studio uses OpenAI-compatible data: {"choices": [{"delta": {"content": "..."}}]}
  • Ollama uses {"message": {"content": "..."}}
  • Anthropic uses event: content_block_delta with {"delta": {"text": "..."}}

The backend normalizes all three into a consistent data: {"token": "..."} stream that the frontend consumes.

The hardest part was handling "thinking" models. Qwen3 and DeepSeek-R1 emit <think>...</think> blocks before their actual response. The _process_stream_token() function tracks state across tokens to strip these artifacts in real-time, without buffering the full response.

Deal Scoring Algorithm

The 14-point scorecard evaluates 7 factors. Each gets 0, 1, or 2 points:

Factor 2 points 1 point 0 points
CoC Return >= 8% >= 4% < 4%
Cap Rate >= 6% >= 4% < 4%
DSCR >= 1.25 >= 1.0 < 1.0
Cash Flow/Unit >= $200/mo >= $100/mo < $100/mo
Break-Even Occ. <= 75% <= 85% > 85%
1% Rule Pass (2) -- Fail (0)
50% Rule Pass (2) -- Fail (0)

Verdict: >= 75% of max points = Great Deal, >= 45% = Borderline, < 45% = Pass.

Lessons Learned

Single-file frontends scale further than you would think. At 2,800 lines, the index.html is still navigable because the JS is organized into clear sections inside one IIFE. The mental overhead of "where does this live?" is zero.

Thinking models need runtime filtering. You cannot just strip <think> tags after the response is complete if you are streaming -- the user would see the raw thinking output appear and then disappear. Token-by-token filtering with state tracking solves this cleanly.

The 4-pillar return model changes how people think about deals. Most calculators show cash flow and cap rate. When you show someone that a property with mediocre cash flow still generates a strong total return through appreciation and debt paydown, it reframes the analysis.

Try It

The live demo requires no installation: https://rental-property-deal-analyzer.onrender.com

To run locally:

pip install -r requirements.txt
python app.py
# Opens at http://localhost:8000
Enter fullscreen mode Exit fullscreen mode

GitHub: https://github.com/berkcankapusuzoglu/Rental-Property-Deal-Analyzer

MIT licensed. Contributions welcome.

If the tool helped you evaluate a deal, consider supporting its development: https://buymeacoffee.com/bkapu

Top comments (0)