Docs navigation
Quickstart — one curl, no key
$ curl -X POST https://reizer.io/api/v1/analyze \ -H "Content-Type: application/json" \ -d '{"strategy":"ltr","inputs":{"price":285000,"marketRent":3100, "downPct":0.25,"rate":0.0675}}'
{
"score": 96, "verdict": "Strong",
"metrics": { "monthly_cash_flow": 636.85, "dscr": 1.4594,
"cap_rate": 0.0852, "cash_on_cash": 0.0989, … },
"stress": [ 7 scenarios, each with a verdict ],
"thresholds": { "break_even_rent": 2282.47, "max_rate": 0.0878, … },
"assumptions": { "estimated_fields": ["taxes", "insurance", …] },
"summary": "Strong (96/100). $637/mo cash flow, DSCR 1.46, …",
"report_url": "https://reizer.io/report.html?id=…"
}Endpoints
| Endpoint | What it does | Access |
|---|---|---|
POST /api/v1/analyze | Full underwrite of one strategy: score, metrics, stress tests, thresholds, shareable report. | FREE |
POST /api/v1/best-use | Runs every strategy the inputs qualify for and ranks them. | FREE |
POST /api/v1/mao | Maximum allowable offer for your return targets — solved on the real engine. | FREE |
GET /api/v1/strategies | Machine-readable input schema for every strategy. | FREE |
GET /api/v1/reports/:id | The analysis data behind a shareable report page (30-day TTL). | FREE |
POST /api/v1/comps | Real sale + rental comparables and ZIP market stats for an address. | API KEY |
POST /api/v1/str-comps | Nearby STR listings with ADR / occupancy / revenue for coordinates. | API KEY |
Input rules
- camelCase keys, dollars as plain numbers. Discover every strategy's fields at
GET /api/v1/strategies. - Rates and percents are fractions —
0.0675, never6.75. Percent-style values are rejected with an error naming the field, never silently converted. - Omitted inputs get documented defaults. REIzer fills a documented default and lists every estimated field under
assumptions.estimated_fields, so a caller (or its user) knows exactly what was assumed. Invalid values are rejected, never replaced. - Minimum inputs per strategy:
ltrprice + marketRent ·strprice + adr + occupancy ·brrrprice + rehabBudget + marketRent + arv ·multifamilyprice + units ·flipprice + rehabBudget + arv ·newclandPrice + livingSqft + buildPsf + btsArv.
Money-field units
Dollar inputs use these periods (same on every strategy, including STR). Percents stay fractions as above. Discover the full schema at GET /api/v1/strategies.
| Field | Unit | Notes |
|---|---|---|
price, arv, rehabBudget, … | $ (one-time) | Purchase / sale / rehab dollars |
marketRent, ltrRent | $/mo | Gross monthly rent |
hoa | $/mo | All strategies, including STR |
utilities | $/mo | Owner-paid utilities |
otherIncome | $/mo | |
taxes, insurance | $/yr | Annual totals |
reserves | $/yr | Flat annual reserve (not CapEx %) |
Changelog (2026-08-16): hoa is monthly on every strategy, including STR. Earlier engine builds treated STR HOA as annual; callers that inferred that from behaviour should send monthly dollars going forward. API_VERSION remains v1 — the old annual-STR behaviour was never documented.
Best-use ranking
Send one property's inputs as a superset; strategies missing required fields are skipped with the reason:
"best_use": "ltr" "ranked": 1. ltr 91 · 2. str 77 · 3. brrr 29 · 4. flip 5 "skipped": multifamily, newc (missing unit / land inputs)
Max allowable offer
Targets are optional — rentals default to cash-on-cash ≥ 8%, DSCR ≥ 1.2, cash flow ≥ $0; flips to after-tax margin ≥ 10% and annualized ROI ≥ 15%; BRRRR adds capital recovery ≥ 80%. The response names the binding constraint and each target's individual price ceiling:
"mao": 303657 "binding_constraint": "cash-on-cash return ≥ 8%" "metrics_at_mao": { "monthlyCF": 546.10, "dscr": 1.3697 } "ceilings_per_target": { "cocReturn": 303657, "dscr": 346599, "monthlyCF": 333691 }
Beyond the defaults, each strategy accepts optional targets that bind only when you name them: capRate on rentals and STR (plus dscr on STR), afterTaxProfit and rule70 on flips, and coc, cashLeft and rule70 on BRRRR. cashLeft is a ceiling, not a floor — the solve keeps cash left in the deal at or below your number. BRRRR metrics depend on which refinance you underwrite to, so "scenario": "refiLTV1" | "refiLTV2" | "refiLTV3" picks one; omit it and the engine's winning scenario is used.
Shareable reports
Every /analyze response carries a report_url — a public report page (score gauge, metrics, stress table) that needs no account to view and expires after 30 days. Built to be handed to the person you're advising; GET /api/v1/reports/:id serves the underlying data.
/analyze takes two optional blocks that only affect that page. property — name, address, city, county, state, zip, beds, baths, sqft, type — puts a header and a map on the report; anything else in it is dropped, and nothing in it reaches the analysis. mao — { "targets": { … }, "scenario": "…" } — adds a max-allowable-offer section, re-solved server-side from the same inputs. A MAO that can't be solved drops the section rather than failing the analysis.
Keyed endpoints — licensed data
Analysis runs on REIzer's own engine and stays free. What costs money — licensed comps and STR market data — needs an API key from a paid plan (Investor and up), metered against the same monthly allowances as the web app. Create keys under Settings → API keys; send them as X-API-Key or Authorization: Bearer.
$ curl -X POST https://reizer.io/api/v1/comps \ -H "Content-Type: application/json" \ -H "X-API-Key: rz_live_…" \ -d '{"action":"comps", "address":{"street":"4917 Maple Ave","city":"Dallas","state":"TX"}, "options":{"saleCount":10,"rentCount":10}}'
The comps body takes a nested address object (a flat street/city/state body returns an empty result, not an error). /api/v1/comps returns comparable sales with a value estimate and range, rental comps with a rent estimate, and ZIP-level market stats. /api/v1/str-comps takes latitude/longitude (+ optional bedrooms) and returns nearby STR listings with ADR, occupancy, and revenue quartiles. Full argument tables: tool catalog — the REST bodies match the MCP tool arguments.
Keys are shown once at creation, revocable instantly, and the plan is resolved live on every call — a downgrade or revocation takes effect immediately.
Rate limits & errors
| Status | Meaning |
|---|---|
400 | Invalid request — the error lists each offending field and why (missing, negative, percent-style, unknown strategy or target). |
401 | Missing, malformed, unknown, or revoked API key on a keyed endpoint. |
402 | Valid key, but the plan lacks API access or the monthly allowance is used up — the response links the fix. |
404 | Unknown route or expired report id. |
429 | Analysis rate limit, shared with the MCP server. Anonymous: 20/min, 500/day per IP. With an API key you're metered per account at your plan's ceiling (Investor 30/min · 1,000/day, Pro 60 · 5,000, Elite 120 · 20,000). The response carries retry_after, a Retry-After header, and metered_as. |
503 | Key verification temporarily unavailable — fail-closed, retry shortly. |
FAQ
Is it production-ready?
Yes — beta label, stable contract, and it's the same engine the REIzer product runs on, held to the same test suite.
Why fractions instead of percents?
No unit ambiguity for AI callers. 6.75 could mean 6.75% or 675% — so it's rejected loudly instead of guessed at.
Is any of the output AI-generated?
No. The engine is deterministic and the summaries are templated — same inputs, same answer, every time. Your AI adds the narration on its side.
What data do you store?
Anonymous analysis requests persist only the report payload behind report_url, which expires after 30 days. See the privacy policy.