Apartments APIapartments.com ↗
Search apartment listings, get unit-level pricing and amenities, and look up properties by management company via the Apartments.com API.
What is the Apartments API?
The Apartments.com API exposes 3 endpoints covering listing search, property details, and management company lookup across thousands of rental properties. Use search_properties to query by city name or lat/lng coordinates with bedroom and rent filters, get_property_details to retrieve unit-level floorplans, amenities, photos, and resident reviews for a specific listing, and get_management_companies to find all properties operated by a named company in a given market.
curl -X GET 'https://api.parse.bot/scraper/623cb755-df0e-49e0-ae8b-63558585a1c2/search_properties?query=New+York%2C+NY' \ -H 'X-API-Key: $PARSE_API_KEY'
Typed, relational, agent-ready
A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.
- Fully typed · autocompletes
- Objects link to objects
- Typed errors & pagination
Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:
uv add parse-sdk uv run parse init uv run parse add --marketplace apartments-com-api
uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""
Apartments.com Parse API Client
Search apartment listings, get property details, and find properties by management company.
Get your API key from: https://parse.bot/settings
"""
import os
import requests
from typing import Any, Dict, List, Optional
class ParseClient:
"""Client for interacting with the Apartments.com Parse API."""
def __init__(self, api_key: Optional[str] = None):
"""
Initialize the Parse API client.
Args:
api_key: API key for authentication. If not provided, reads from PARSE_API_KEY env var.
Raises:
ValueError: If API key is not provided or found in environment.
"""
self.base_url = "https://api.parse.bot"
self.scraper_id = "623cb755-df0e-49e0-ae8b-63558585a1c2"
self.api_key = api_key or os.getenv("PARSE_API_KEY")
if not self.api_key:
raise ValueError("API key must be provided or set in PARSE_API_KEY environment variable")
def _call(self, endpoint: str, method: str = "POST", **params) -> Dict[str, Any]:
"""
Make an API call to the Parse Bot endpoint.
Args:
endpoint: The endpoint name (e.g., 'search_properties')
method: HTTP method ('GET' or 'POST')
**params: Query/body parameters for the request
Returns:
Response JSON as dictionary
Raises:
requests.RequestException: If the API call fails
"""
url = f"{self.base_url}/scraper/{self.scraper_id}/{endpoint}"
headers = {
"X-API-Key": self.api_key,
"Content-Type": "application/json"
}
if method.upper() == "GET":
response = requests.get(url, headers=headers, params=params)
elif method.upper() == "POST":
response = requests.post(url, headers=headers, json=params)
else:
raise ValueError(f"Unsupported HTTP method: {method}")
response.raise_for_status()
return response.json()
def search_properties(
self,
query: Optional[str] = None,
lat: Optional[float] = None,
lng: Optional[float] = None,
radius: float = 5,
min_rent: Optional[int] = None,
max_rent: Optional[int] = None,
min_beds: Optional[int] = None,
max_beds: Optional[int] = None,
min_sqft: Optional[int] = None,
page: int = 1
) -> Dict[str, Any]:
"""
Search for apartment listings by city, region, or coordinates.
Either query or lat+lng must be provided.
Args:
query: Search query (e.g., 'Sunnyvale, CA', 'Boston, MA')
lat: Latitude for coordinate-based search
lng: Longitude for coordinate-based search
radius: Search radius in miles (default: 5)
min_rent: Minimum rent amount
max_rent: Maximum rent amount
min_beds: Minimum number of bedrooms
max_beds: Maximum number of bedrooms
min_sqft: Minimum square footage
page: Page number for pagination (default: 1)
Returns:
Dictionary with 'total_count', 'page', 'next_page_url', and 'properties' list
"""
params = {
k: v for k, v in {
"query": query,
"lat": lat,
"lng": lng,
"radius": radius,
"min_rent": min_rent,
"max_rent": max_rent,
"min_beds": min_beds,
"max_beds": max_beds,
"min_sqft": min_sqft,
"page": page
}.items() if v is not None
}
return self._call("search_properties", method="GET", **params)
def get_property_details(self, url: str) -> Dict[str, Any]:
"""
Get detailed information for a specific property listing.
Args:
url: Full URL or relative path of the property page
Returns:
Dictionary with property details including amenities, units, photos, reviews, etc.
"""
return self._call("get_property_details", method="GET", url=url)
def get_management_companies(
self,
company_name: str,
market: Optional[str] = None
) -> Dict[str, Any]:
"""
Search for properties managed by a specific company in a market.
Args:
company_name: Name of the management company (e.g., 'Bozzuto', 'Greystar')
market: Market name (e.g., 'Boston'). If omitted, searches US-wide.
Returns:
Dictionary with 'total_count' and 'properties' list
"""
params = {"company_name": company_name}
if market:
params["market"] = market
return self._call("get_management_companies", method="GET", **params)
def extract_price(price_str: str) -> int:
"""Extract numeric value from price string."""
try:
return int(price_str.replace("$", "").replace(",", "").split("-")[0].strip())
except (ValueError, IndexError):
return 0
def main():
"""
Practical workflow: Search for apartments, get details for promising results,
and identify management companies to contact.
"""
# Initialize client
client = ParseClient()
print("=" * 90)
print("APARTMENT HUNTING WORKFLOW - PARSE API DEMO")
print("=" * 90)
# Step 1: Search for apartments with specific criteria
print("\n[STEP 1] Searching for apartments...")
print(" Location: San Francisco, CA")
print(" Criteria: $3000-$5500/month, 1-2 bedrooms, min 600 sqft\n")
search_results = client.search_properties(
query="San Francisco, CA",
min_rent=3000,
max_rent=5500,
min_beds=1,
max_beds=2,
min_sqft=600,
page=1
)
total_found = search_results.get("total_count", 0)
properties = search_results.get("properties", [])
current_page = search_results.get("page", 1)
next_page_url = search_results.get("next_page_url")
print(f"✓ Found {total_found} properties matching criteria")
print(f" Displaying page {current_page} with {len(properties)} results")
if next_page_url:
print(f" More results available on next page")
if not properties:
print("No properties found. Exiting.")
return
# Display search results summary
print("\n" + "-" * 90)
print("TOP SEARCH RESULTS")
print("-" * 90)
for idx, prop in enumerate(properties[:5], 1):
listing_id = prop.get("listing_id", "N/A")
name = prop.get("name", "Unknown")
address = prop.get("address", "N/A")
price_range = prop.get("price_range", "N/A")
beds = prop.get("beds", "N/A")
phone = prop.get("phone", "Contact for info")
print(f"\n{idx}. {name}")
print(f" ID: {listing_id}")
print(f" Address: {address}")
print(f" Bedrooms: {beds} | Price: {price_range}")
print(f" Contact: {phone}")
# Step 2: Get detailed information for top properties
print("\n" + "=" * 90)
print("DETAILED PROPERTY ANALYSIS")
print("=" * 90)
management_companies_found = {}
detailed_properties = []
for idx, prop in enumerate(properties[:3], 1):
prop_url = prop.get("url")
prop_name = prop.get("name", "Unknown Property")
if not prop_url:
print(f"\n[Property {idx}] ⚠ No URL available, skipping...")
continue
print(f"\n[Property {idx}] Fetching details for {prop_name}...")
try:
details = client.get_property_details(prop_url)
detailed_properties.append(details)
address = details.get("address", "N/A")
management = details.get("management_company", "Not specified")
neighborhood = details.get("neighborhood", "N/A")
pricing_summary = details.get("pricing_summary", "N/A")
has_ev = details.get("has_ev_charging", False)
total_units = details.get("total_units_count", 0)
print(f" ✓ Details retrieved successfully")
print(f" Neighborhood: {neighborhood}")
print(f" Pricing: {pricing_summary}")
print(f" Management: {management}")
print(f" Total Units Available: {total_units}")
print(f" EV Charging: {'Yes ⚡' if has_ev else 'No'}")
# Track management companies
if management and management != "Not specified":
if management not in management_companies_found:
management_companies_found[management] = []
management_companies_found[management].append(prop_name)
# Display amenities
amenities = details.get("amenities", [])
if amenities:
display_amenities = amenities[:5]
print(f" Amenities: {', '.join(display_amenities)}", end="")
if len(amenities) > 5:
print(f" + {len(amenities) - 5} more")
else:
print()
# Display pet policy
pet_policy = details.get("pet_policy", [])
if pet_policy:
print(f" Pet Policy: {', '.join(pet_policy)}")
else:
print(f" Pet Policy: No information available")
# Display unit details
units = details.get("units", [])
if units:
sorted_units = sorted(
units,
key=lambda u: extract_price(u.get("price", "$0"))
)
print(f" Sample Units (showing {min(3, len(sorted_units))} of {len(units)}):")
for unit in sorted_units[:3]:
beds = unit.get("beds", "?")
baths = unit.get("baths", "?")
price = unit.get("price", "Call for price")
sqft = unit.get("sqft", "?")
availability = unit.get("availability", "Contact")
print(f" • {beds}BR/{baths}BA - {price} ({sqft} sqft) - {availability}")
# Display reviews
reviews = details.get("reviews", [])
if reviews and reviews[0].get("rating"):
latest = reviews[0]
rating = latest.get("rating", "N/A")
title = latest.get("title", "No title")
print(f" Latest Review: ⭐ {rating}/5 - {title}")
except Exception as e:
print(f" ✗ Error fetching details: {str(e)}")
# Step 3: Search for properties by management company
print("\n" + "=" * 90)
print("MANAGEMENT COMPANY PORTFOLIO")
print("=" * 90)
if management_companies_found:
print(f"\nFound {len(management_companies_found)} management company/companies:\n")
for company_name in list(management_companies_found.keys())[:2]:
properties_in_results = management_companies_found[company_name]
print(f"[Company] {company_name}")
print(f" Properties in search results: {', '.join(properties_in_results)}")
print(f" Searching all {company_name} properties in San Francisco...")
try:
company_results = client.get_management_companies(
company_name=company_name,
market="San Francisco, CA"
)
total_company_props = company_results.get("total_count", 0)
company_props = company_results.get("properties", [])
print(f" ✓ Found {total_company_props} total properties managed by {company_name}")
if company_props:
print(f" Showing {min(3, len(company_props))} additional properties:")
for prop in company_props[:3]:
prop_name = prop.get("name", "N/A")
prop_phone = prop.get("phone", "N/A")
print(f" • {prop_name} - {prop_phone}")
print()
except Exception as e:
print(f" ✗ Error searching by company: {str(e)}\n")
else:
print("\nNo specific management companies found in detailed property results.")
print("Try searching for individual companies using get_management_companies().")
# Summary and recommendations
print("=" * 90)
print("SEARCH SUMMARY & RECOMMENDATIONS")
print("=" * 90)
print(f"\n📊 Statistics:")
print(f" • Total properties found: {total_found}")
print(f" • Properties analyzed in detail: {len(detailed_properties)}")
print(f" • Unique management companies: {len(management_companies_found)}")
if detailed_properties:
print(f"\n💡 Next Steps:")
print(f" 1. Review the {len(detailed_properties)} detailed properties above")
print(f" 2. Contact management companies directly for tours and current availability")
print(f" 3. Use search_properties() with page=2 to see more listings")
print(f" 4. Filter by management company to see their full portfolio")
print(f"\n✓ Workflow complete!\n")
if __name__ == "__main__":
main()Search for apartment listings by city, region, or coordinates with advanced filters. Returns up to 40 properties per page. Either query or lat+lng must be provided.
| Param | Type | Description |
|---|---|---|
| lat | number | Latitude for coordinate-based search. Must be provided together with lng. |
| lng | number | Longitude for coordinate-based search. Must be provided together with lat. |
| page | integer | Page number for pagination. |
| query | string | Location search query (e.g., 'New York, NY', 'Sunnyvale, CA'). Either query or lat+lng required. |
| radius | number | Search radius in miles when using lat/lng search. |
| max_beds | integer | Maximum number of bedrooms. |
| max_rent | integer | Maximum monthly rent filter. |
| min_beds | integer | Minimum number of bedrooms. |
| min_rent | integer | Minimum monthly rent filter. |
| min_sqft | integer | Minimum square footage. |
{
"type": "object",
"fields": {
"page": "integer current page number",
"properties": "array of property objects with listing_id, property_id, url, name, address, price_range, beds, phone",
"total_count": "integer total number of matching listings",
"next_page_url": "string URL of the next page or null if no more pages"
},
"sample": {
"data": {
"page": 1,
"properties": [
{
"url": "https://www.apartments.com/10-halletts-point-astoria-ny/1j2c5h6/",
"name": "10 Halletts Point, Astoria, NY",
"phone": "+1 (555) 012-3456",
"address": "10, 20, 30 Halletts Pt, Astoria, NY 11102",
"listing_id": "1j2c5h6",
"property_id": null
}
],
"total_count": 700,
"next_page_url": "https://www.apartments.com/new-york-ny/2/"
},
"status": "success"
}
}About the Apartments API
Search and Filter Listings
The search_properties endpoint accepts either a text query (e.g., "Austin, TX") or a lat/lng pair with an optional radius in miles. Results return up to 40 properties per page, each object carrying listing_id, property_id, name, address, price_range, beds, phone, and a direct url. Pagination is handled via the page parameter; the response also includes total_count and a next_page_url field so you can walk through large result sets programmatically. Narrow results further with min_beds, max_beds, and max_rent filters.
Property Detail Data
get_property_details takes a full property URL and returns a structured object with an array of units, each containing floorplan, unit_number, price, max_rent, sqft, availability, beds, and baths. Beyond unit data, the response includes amenities (array of strings), pet_policy, a photos array of image URLs, neighborhood, and a reviews array where each entry has title, rating, text, and date. This makes it possible to surface resident sentiment alongside pricing in a single call.
Management Company Lookup
get_management_companies accepts a company_name (e.g., "Greystar" or "Bozzuto") and an optional market string. When market is omitted, the search covers the entire US. The response mirrors the structure of the search endpoint — a properties array with the same listing fields — plus total_count and the echoed company_name and market values. This is useful for portfolio analysis, competitive research, or tracking which markets a specific operator is active in.
The Apartments API is a managed, monitored endpoint for apartments.com — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when apartments.com changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official apartments.com API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Aggregate current rent ranges by neighborhood using
search_propertieswith coordinate-based queries across a metro area. - Build a unit availability tracker that polls
get_property_detailsand alerts when a specific floorplan opens or changes price. - Map management company footprints by calling
get_management_companiesfor major operators across multiple markets. - Compare
reviewsratings across competing properties in the same ZIP code to surface resident satisfaction signals. - Populate a relocation tool with
amenities,pet_policy, andphotospulled fromget_property_details. - Estimate market-rate rents for a given bedroom count by filtering
search_propertiesresults withmin_bedsandmax_beds. - Monitor portfolio concentration by tracking how many listings a company holds per
marketviaget_management_companies.
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 100 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
One credit = one API call regardless of which marketplace API you call. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does Apartments.com have an official developer API?+
How does pagination work in `search_properties`?+
total_count integer and a next_page_url string. Pass the page parameter to retrieve subsequent pages. When next_page_url is null, you have reached the last page of results.What unit-level fields does `get_property_details` return?+
units array includes floorplan, unit_number, price, max_rent, sqft, availability, beds, and baths. The endpoint also returns amenities, pet_policy, photos, neighborhood, and a reviews array with per-review rating, title, text, and date.Can I filter search results by amenities or property type (e.g., only condos or pet-friendly buildings)?+
search_properties endpoint supports filters for min_beds, max_beds, max_rent, and location. Amenity-level or property-type filters are not available as parameters. You can fork this API on Parse and revise it to add those filter parameters.