Skip to content
ListingrailGet an API key

How to get Apartments.com listings in Python

By the end of this guide you have a CSV of Austin apartment buildings with their lowest rent, size and leasing office, from one script you can run again tomorrow. Everything below was run on 16 October 2026; the output table is what it returned.

Why scraping Apartments.com yourself breaks

  • Apartments.com has no public developer API. Its official integration is a feed that property management software sends listings into, set up through feeds@apartments.com; nothing comes back out.
  • The website refuses most requests from cloud servers. Akamai sits in front of it and checks the TLS handshake, so a plain requests call gets HTTP 403.
  • A building's floor plans, units and fees are not in the search results; each building needs a second request for its details.

None of that is impossible, but each point is code you then maintain. The rest of this guide uses Listingrail, which does the reading and hands back clean JSON. The Apartments.com API page has the official options with sources.

1. Get a key

One request with your email returns a key with 100 free listings. It is shown once, so keep it in an environment variable.

curl -X POST https://listingrail.com/v1/signup \
  -H "content-type: application/json" \
  -d '{"email": "you@example.com"}'

export LISTINGRAIL_KEY=re_...

2. Search and save to CSV

Install requests (pip install requests), then run:

import csv
import os

import requests

r = requests.post(
    "https://listingrail.com/v1/listings/search",
    headers={"Authorization": f"Bearer {os.environ['LISTINGRAIL_KEY']}"},
    json={"source":"apartments-com","location":"Austin, TX","type":"rent","limit":50},
    timeout=300,
)
r.raise_for_status()
listings = r.json()["listings"]

fields = ["price", "currency", "beds", "baths", "area", "address", "url"]
with open("apartments-com-austin-tx.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(fields)
    for home in listings:
        writer.writerow([home[k] for k in fields])

print(len(listings), "listings saved to apartments-com-austin-tx.csv")

The request body, on its own:

{
  "source": "apartments-com",
  "location": "Austin, TX",
  "type": "rent",
  "limit": 50
}

Each listing returned costs one credit. A search that finds nothing, or fails at Apartments.com, costs nothing. A request can take up to a minute or two, because Apartments.com is read at that moment.

3. What comes back

The first three rows of the run on 16 October 2026:

AddressPriceSizeAgent
4711 E Riverside Dr, Austin, TX 787411,295 USD / month0 bd · 1 ba · 509 sq ftThe Connor Group
7655 N Ranch Road 620, Austin, TX 78726951 USD / month1 bd · 1 ba · 644 sq ftMaslow’s Campus Communities
5210 N Lamar Blvd, Austin, TX 787511,500 USD / month0 bd · 1 ba · 546 sq ftEmbrey

Every listing has the same keys on all 17 portals, so the code for Apartments.com reads the others too. Fields Apartments.com does not show come back as null, and the portal's own record sits in raw. With pandas:

import pandas as pd

df = pd.DataFrame(listings)
df["agent"] = df["agent"].str.get("company")
print(df[["price", "beds", "baths", "area", "address", "agent"]].head())

4. Get new listings and price cuts every day

Running the script on a schedule works, but then you compare the files yourself. A monitor stores the search, checks it once a day, and sends only what changed: new listings and price cuts. The free key holds one monitor.

requests.post(
    "https://listingrail.com/v1/monitors",
    headers={"Authorization": f"Bearer {os.environ['LISTINGRAIL_KEY']}"},
    json={
        "source": "apartments-com",
        "location": "Austin, TX",
        "filters": {"type":"rent"},
        "webhook_url": "https://example.com/new-listings",
    },
    timeout=30,
)

Changes can also be read with GET /v1/monitors/{id}/changes or downloaded as CSV. Plans and limits are on the monitors page.

Apartments.com quirks worth knowing

  • Pass a city and state, e.g. "Austin, TX". Results reach into nearby towns: this run returned buildings in Manchaca and Manor.
  • Each row is a building, not a unit. price, beds and area are each the lowest the building shows, so they can come from different floor plans; the full list is in raw.floorPlans and raw.units.
  • beds 0 is a studio. area is in square feet. price_period is "month".
  • agent.company is the management company and agent.phone the leasing line. listed_at is empty; raw.lastUpdated says when Apartments.com last updated the building.

Questions

Is there an Apartments.com API?

Not for reading listings. The official feed only takes listings in from property management software such as AppFolio or Yardi.

Why is the rent a single number when the building has many units?

price is the lowest rent in the building (raw.rentMin). raw.rentMax and raw.floorPlans hold the rest.

Can I filter by price or bedrooms?

Yes: pass minPrice, maxPrice and minBeds in the request body.

Get 100 free listingsAPI docsOther guides