Zillow rental analysis
POSThttps://api.propertiesapi.com/v1/zillow/rental-analysis
GEThttps://api.propertiesapi.com/v1/zillow/rental-analysis
A rental property analysis for one home by Zillow link, zpid or address: cash flow, cap rate, cash-on-cash return, DSCR, GRM, the 1% rule, break-even rent and a five-year projection. Price, rent, taxes and HOA are filled from the property; every other input is a documented default you can override.
At a glance
- Billing: 1 credit per successful live lookup of the home, the same as GET /v1/zillow/property; a home looked up within 24 hours comes from cache and is free. The analysis adds no charge. Errors before or during the lookup aren't billed.
- Auth:
x-api-keyheader. - Latency: typically 6 seconds, up to about 20 for a live lookup; repeat lookups within 24 hours come from the cache, with no live lookup.
Parameters
Send exactly one of url or address.
| Name | In | Type | Description |
|---|---|---|---|
| zpid | query | string | A Zillow property ID, 1–12 digits. Give exactly one of url, zpid or address. |
| url | query | string | A zillow.com property link: a home (zillow.com/homedetails/..._zpid/, zillow.com/homes/..._zpid/ or a new-construction zillow.com/community/.../..._zpid/ link) or a building (zillow.com/b/... or zillow.com/apartments/...). Photo and other sub-pages after the property part are ignored. Other zillow.com pages, such as search results, return 400 bad_param, free. URL-encode it. Max 500 characters. Short links (zil.lw) and m.zillow.com links aren't accepted (400 bad_param, free): send the full www.zillow.com link. |
| address | query | string | A one-line US address with a house number, 5-200 characters. We find the matching Zillow property first. Include the unit number for condos and apartments. |
Inputs (all optional overrides)
| Name | In | Type | Description |
|---|---|---|---|
| status | query | string: for_sale | sold | for_rent | Optional hint for which listing to read, for a home listed for sale and for rent at once. It is a hint, not a filter: a home served from the 24-hour cache, or found first by our primary source, comes back with whatever listing it has, so check homeStatus. When the home returned doesn't have the status you asked for, meta.warning says so. If omitted, the status is detected automatically. |
| price | query | number | Purchase price, USD. Required. Auto-filled from the list or sold price, else Zillow's Zestimate. Max 1000000000. |
| down_payment_pct | query | number | Down payment, percent of the price. Default 20% (an assumption; edit it). Default 20. Max 100. |
| interest_rate_pct | query | number | Interest rate, percent a year. Default 7% a year: assumed; edit it. Not a quoted rate. Default 7. Max 30. |
| loan_term_years | query | integer | Loan term, years. Default 30 years (an assumption; edit it). Default 30. Max 50. |
| closing_costs_pct | query | number | Closing costs, percent of the price. Default 3% of the price (an assumption; edit it). Default 3. Max 20. |
| monthly_rent | query | number | Monthly rent, USD. Required. Auto-filled from Zillow's rent Zestimate (an estimate). Max 10000000. |
| vacancy_pct | query | number | Vacancy, percent. Default 5% of gross rent (an assumption; edit it). Default 5. Max 100. |
| management_pct | query | number | Property management, percent. Default 8% of rent collected (an assumption; edit it). Default 8. Max 100. |
| maintenance_pct | query | number | Maintenance, percent. Default 5% of gross rent (an assumption; edit it). Default 5. Max 100. |
| capex_pct | query | number | Capital expenditures, percent. Default 5% of gross rent, set aside for big repairs (an assumption; edit it). Default 5. Max 100. |
| annual_property_tax | query | number | Property tax a year, USD. Auto-filled from the tax history when there is one; otherwise an estimate of 1% of the price a year (edit it). Max 100000000. |
| annual_insurance | query | number | Insurance a year, USD. An estimate of 0.5% of the price a year, not a quote (edit it). Max 100000000. |
| monthly_hoa | query | number | HOA a month, USD. Auto-filled from the listing's HOA fee; default 0. Default 0. Max 10000000. |
| other_monthly_expenses | query | number | Other costs a month, USD. Utilities you pay, landscaping and so on; default 0. Default 0. Max 10000000. |
| rent_growth_pct | query | number | Rent growth, percent a year. Projection only. Default 3% a year (an assumption; edit it). Default 3. Max 50. |
| appreciation_pct | query | number | Appreciation, percent a year. Projection only. Default 3% a year (an assumption; edit it). Default 3. Max 50. |
| expense_growth_pct | query | number | Expense growth, percent a year. Projection only: taxes, insurance, HOA and other costs. Default 3% a year (an assumption; edit it). Default 3. Max 50. |
Request body
Exactly one of url, zpid or address, optional status, and any of the inputs above as JSON numbers (or numeric strings). GET takes the same names as query parameters. Inputs you leave out are filled from the property or from the documented defaults.
{
"url": "https://www.zillow.com/homedetails/2640-Steiner-St-San-Francisco-CA-94115/15073305_zpid/",
"interest_rate_pct": 6.5
}Response fields
The body is { data, meta }. Money is in USD, rounded to cents; percentages and ratios are rounded to two decimals.
| Field | Type | Formula or meaning |
|---|---|---|
data.property | object | The home the analysis used: zpid, address, detailUrl, homeStatus, homeType, beds, baths, livingArea, yearBuilt, price, zestimate, rentZestimate, monthlyHoaFee, taxAnnualAmount, taxAssessedValue and propertyTaxRate, as GET /v1/zillow/property returns them (null when Zillow doesn't show them). |
data.inputs | object | One entry per input (the parameters above): value; source (property: read from this home, with basis naming the field used; default: a documented assumption or estimate, with note; request: the value you passed); basis or note. |
data.metrics.down_payment | number | price x down_payment_pct. |
data.metrics.loan_amount | number | price - down_payment. |
data.metrics.closing_costs | number | price x closing_costs_pct. |
data.metrics.total_cash_needed | number | down_payment + closing_costs. |
data.metrics.monthly_principal_and_interest | number | loan_amount x i / (1 - (1 + i)^-n), with i = interest_rate_pct / 12 and n = loan_term_years x 12. At a 0% rate, loan_amount / n; with no loan, 0. |
data.metrics.annual_debt_service | number | monthly_principal_and_interest x 12. |
data.metrics.gross_monthly_rent | number | monthly_rent. |
data.metrics.gross_annual_income | number | monthly_rent x 12. |
data.metrics.vacancy_loss | number | gross_annual_income x vacancy_pct. |
data.metrics.effective_gross_income | number | gross_annual_income - vacancy_loss (EGI). |
data.metrics.operating_expenses | object | management (EGI x management_pct), maintenance (gross x maintenance_pct), capex (gross x capex_pct), property_tax, insurance, hoa (monthly_hoa x 12) and other (other_monthly_expenses x 12), a year. |
data.metrics.total_operating_expenses | number | The sum of operating_expenses. |
data.metrics.noi | number | Net operating income: effective_gross_income - total_operating_expenses (before the loan payment). |
data.metrics.annual_cash_flow | number | noi - annual_debt_service. |
data.metrics.monthly_cash_flow | number | annual_cash_flow / 12. |
data.metrics.cap_rate_pct | number | noi / price, percent. |
data.metrics.cash_on_cash_pct | number | annual_cash_flow / total_cash_needed, percent. null when nothing is invested (0% down and no closing costs). |
data.metrics.dscr | number | Debt service coverage ratio: noi / annual_debt_service. null with no loan. |
data.metrics.grm | number | Gross rent multiplier: price / gross_annual_income. null with no rent. |
data.metrics.one_percent_rule | object | passes: monthly_rent is at least 1% of price; rent_to_price_pct: monthly_rent / price, percent. |
data.metrics.break_even_rent | number | The monthly rent at which annual_cash_flow is 0: (fixed costs + annual_debt_service) / (12 x [(1 - vacancy)(1 - management) - maintenance - capex]), fixed costs = tax + insurance + HOA and other costs a year. null when no rent can break even (for example 100% vacancy). |
data.projection | object[] | Five years: year, monthly_rent (grows by rent_growth_pct from year 2), noi (percentage costs follow the rent; tax, insurance, HOA and other costs grow by expense_growth_pct), annual_cash_flow (noi minus the loan payments still due that year: none once a short loan is paid off), cumulative_cash_flow, property_value (price x (1 + appreciation_pct)^year), loan_balance (after 12 x year payments), equity (property_value - loan_balance, before selling costs), total_profit (cumulative_cash_flow + equity - total_cash_needed) and total_return_pct (total_profit / total_cash_needed; null when nothing is invested). |
data.warnings | string[] | Inputs to check: an assumed interest rate, estimated insurance or taxes, a Zestimate used as the price, a rent Zestimate used as the rent, a missing HOA fee on a condo, a negative cash flow. Can be empty. |
data.disclaimer | string | Not financial advice: the figures are estimates for illustration. |
meta | object | The lookup's meta: credits (1 for a live lookup, 0 from the cache), cached, cache_key, fetched_at, source, release, took_ms, and warning when the lookup has one. |
Example
curl "https://api.propertiesapi.com/v1/zillow/rental-analysis" -H "x-api-key: $PROPERTIESAPI_KEY" \
-H "content-type: application/json" \
-d '{"url":"https://www.zillow.com/homedetails/2640-Steiner-St-San-Francisco-CA-94115/15073305_zpid/","interest_rate_pct":6.5}'{
"data": {
"property": {
"zpid": "15073305",
"address": "2640 Steiner St, San Francisco, CA 94115",
"detailUrl": "https://www.zillow.com/homedetails/2640-Steiner-St-San-Francisco-CA-94115/15073305_zpid/",
"homeStatus": "FOR_SALE",
"homeType": "SINGLE_FAMILY",
"beds": 3,
"baths": 2,
"livingArea": 1840,
"yearBuilt": 1998,
"price": 525000,
"zestimate": 512000,
"rentZestimate": 2650,
"monthlyHoaFee": null,
"taxAnnualAmount": 9840,
"taxAssessedValue": 498000,
"propertyTaxRate": 1.98
},
"inputs": {
"price": {
"value": 525000,
"source": "property",
"basis": "list price"
},
"down_payment_pct": {
"value": 20,
"source": "default",
"note": "Default 20% (an assumption; edit it)."
},
"interest_rate_pct": {
"value": 6.5,
"source": "request"
},
"loan_term_years": {
"value": 30,
"source": "default",
"note": "Default 30 years (an assumption; edit it)."
},
"closing_costs_pct": {
"value": 3,
"source": "default",
"note": "Default 3% of the price (an assumption; edit it)."
},
"monthly_rent": {
"value": 2650,
"source": "property",
"basis": "rent Zestimate"
},
"vacancy_pct": {
"value": 5,
"source": "default",
"note": "Default 5% of gross rent (an assumption; edit it)."
},
"management_pct": {
"value": 8,
"source": "default",
"note": "Default 8% of rent collected (an assumption; edit it)."
},
"maintenance_pct": {
"value": 5,
"source": "default",
"note": "Default 5% of gross rent (an assumption; edit it)."
},
"capex_pct": {
"value": 5,
"source": "default",
"note": "Default 5% of gross rent, set aside for big repairs (an assumption; edit it)."
},
"annual_property_tax": {
"value": 9840,
"source": "property",
"basis": "tax history (2025)"
},
"annual_insurance": {
"value": 2625,
"source": "default",
"note": "An estimate of 0.5% of the price a year, not a quote (edit it)."
},
"monthly_hoa": {
"value": 0,
"source": "default",
"note": "Auto-filled from the listing's HOA fee; default 0."
},
"other_monthly_expenses": {
"value": 0,
"source": "default",
"note": "Utilities you pay, landscaping and so on; default 0."
},
"rent_growth_pct": {
"value": 3,
"source": "default",
"note": "Projection only. Default 3% a year (an assumption; edit it)."
},
"appreciation_pct": {
"value": 3,
"source": "default",
"note": "Projection only. Default 3% a year (an assumption; edit it)."
},
"expense_growth_pct": {
"value": 3,
"source": "default",
"note": "Projection only: taxes, insurance, HOA and other costs. Default 3% a year (an assumption; edit it)."
}
},
"metrics": {
"down_payment": 105000,
"loan_amount": 420000,
"closing_costs": 15750,
"total_cash_needed": 120750,
"monthly_principal_and_interest": 2654.69,
"annual_debt_service": 31856.23,
"gross_monthly_rent": 2650,
"gross_annual_income": 31800,
"vacancy_loss": 1590,
"effective_gross_income": 30210,
"operating_expenses": {
"management": 2416.8,
"maintenance": 1590,
"capex": 1590,
"property_tax": 9840,
"insurance": 2625,
"hoa": 0,
"other": 0
},
"total_operating_expenses": 18061.8,
"noi": 12148.2,
"annual_cash_flow": -19708.03,
"monthly_cash_flow": -1642.34,
"cap_rate_pct": 2.31,
"cash_on_cash_pct": -16.32,
"dscr": 0.38,
"grm": 16.51,
"one_percent_rule": {
"passes": false,
"rent_to_price_pct": 0.5
},
"break_even_rent": 4771.88
},
"projection": [
{
"year": 1,
"monthly_rent": 2650,
"noi": 12148.2,
"annual_cash_flow": -19708.03,
"cumulative_cash_flow": -19708.03,
"property_value": 540750,
"loan_balance": 415305.55,
"equity": 125444.45,
"total_profit": -15013.58,
"total_return_pct": -12.43
},
{
"year": 2,
"monthly_rent": 2729.5,
"noi": 12512.65,
"annual_cash_flow": -19343.58,
"cumulative_cash_flow": -39051.61,
"property_value": 556972.5,
"loan_balance": 410296.71,
"equity": 146675.79,
"total_profit": -13125.82,
"total_return_pct": -10.87
},
{
"year": 3,
"monthly_rent": 2811.39,
"noi": 12888.03,
"annual_cash_flow": -18968.2,
"cumulative_cash_flow": -58019.81,
"property_value": 573681.68,
"loan_balance": 404952.42,
"equity": 168729.26,
"total_profit": -10040.55,
"total_return_pct": -8.32
},
{
"year": 4,
"monthly_rent": 2895.73,
"noi": 13274.67,
"annual_cash_flow": -18581.56,
"cumulative_cash_flow": -76601.38,
"property_value": 590892.13,
"loan_balance": 399250.2,
"equity": 191641.92,
"total_profit": -5709.46,
"total_return_pct": -4.73
},
{
"year": 5,
"monthly_rent": 2982.6,
"noi": 13672.91,
"annual_cash_flow": -18183.32,
"cumulative_cash_flow": -94784.7,
"property_value": 608618.89,
"loan_balance": 393166.11,
"equity": 215452.78,
"total_profit": -81.91,
"total_return_pct": -0.07
}
],
"warnings": [
"monthly_rent is Zillow's rent Zestimate, an estimate. Pass monthly_rent if you know the market rent.",
"annual_insurance is an estimate (0.5% of the price a year), not a quote. Pass a real quote.",
"Cash flow is negative with these inputs: the rent doesn't cover the costs and the loan payment."
],
"disclaimer": "Not financial advice. These figures are estimates for illustration: plain arithmetic on the inputs shown, several of which are assumptions or estimates (see inputs). The Zestimate and rent Zestimate are Zillow's estimates, not appraisals or offers. Check every input with a lender, an insurer and a local expert before you rely on it."
},
"meta": {
"credits": 1,
"cached": false,
"cache_key": "zpid:15073305",
"fetched_at": "2026-10-19T14:02:11.000Z",
"source": {
"note": "Read from the public Zillow page. Field availability varies by listing. PropertiesAPI is not affiliated with Zillow."
},
"release": "v1",
"took_ms": 14236
}
}Example values are illustrative, not a real record. Fields Zillow doesn't show for a property come back as null. Data is provided as-is.
Not financial advice. The analysis is arithmetic on the inputs shown; several are assumptions or estimates (data.warnings lists them). Try it without a key in the free rental property calculator, or see the cap rate, cash-on-cash return and DSCR calculators.
Notes
- Send exactly one of url, zpid or address, as JSON (POST) or as query parameters (GET). Every other field is an optional override, used as you pass it (source request).
- The lookup is GET /v1/zillow/property's own: the same key, rate limits, credits and 24-hour cache. Your dashboard lists these calls as property lookups.
- price and monthly_rent are never invented. When the home has no list price, sold price or Zestimate, or no rent Zestimate (or listed rent), and you didn't pass one, the answer is 422 rental_input_missing: details.missing names the input, and nothing else about the home is returned. The record is refused before it is cached or billed, so the 422 costs nothing; ask again with the input and it is a normal lookup (1 credit live, none from the cache).
- interest_rate_pct defaults to an assumed rate, not a quoted or current mortgage rate. annual_insurance, and annual_property_tax when the home has no tax figure, are estimates as a share of the price. data.warnings lists every such input; pass your own numbers.
- Override values are checked before the lookup: a bad value returns 400 bad_param (details.fields names it), and nothing is looked up or billed.
- Money is in USD rounded to cents; percentages and ratios to 2 decimals. The formulas are listed under Response fields.
- The analysis is plain arithmetic on the inputs, for illustration. It isn't financial advice, an appraisal or a loan quote (data.disclaimer). The same engine runs the free rental property calculator.
Errors
Errors are { error: { code, message, details? }, meta: { took_ms } } with status 400, 401, 404, 422, 500, 502, 503 or 504. Errors aren't billed. See errors.
Zillow is a trademark of Zillow, Inc. PropertiesAPI is an independent service and is not affiliated with, endorsed by or sponsored by Zillow.
Zillow rental analysis questions
Which inputs come from the property?+
The purchase price (the list or sold price, else Zillow's Zestimate), the monthly rent (Zillow's rent Zestimate), the property tax (the latest tax-history year, else the annual tax amount, else the assessed value times the local rate) and the HOA fee. data.inputs says where every value came from.
Is the interest rate a live mortgage rate?+
No. Without interest_rate_pct the analysis uses an assumed rate and says so in data.warnings. Pass the rate you were quoted.
What if the home has no rent Zestimate?+
Pass monthly_rent. Without it the answer is 422 rental_input_missing, which names the missing input and costs nothing; asking again with the rent is a normal lookup.
Can I try it without an API key?+
Yes: the free rental property calculator runs the same formulas in your browser, and can fill the inputs from an address.
Is this Zillow's official API?+
No. PropertiesAPI is an independent service and is not affiliated with, endorsed by or sponsored by Zillow. The data is provided as-is.
Do failed lookups cost anything?+
No. Only successful live lookups are billed. Errors are free, and repeat lookups of the same home within 24 hours come from cache for free.
Related guides
- Asking Rent vs. Rent Zestimate: Build a Rental Comparison Spreadsheet
What the rent Zestimate means, how it differs from the asking rent, and a Python report that compares the two for a list of rental properties.
- Calculate Estimated Gross Rental Yield for a Property Shortlist
A Python gross rental yield calculator for a property shortlist that labels every input: your assumed price and rent, or the source's asking price and rent Zestimate, with expenses excluded.
- RentCast vs. PropertiesAPI: Rental Analytics or Zillow Property Data?
A dated, task-based comparison. RentCast offers records, its own rent and value estimates, listing search and market data; PropertiesAPI returns a known Zillow property page as JSON.