# AppFolio & Buildium Rentals Scraper (`steadyscrape/appfolio-rentals-scraper`) Actor

Scrape rental listings of any property manager on AppFolio or Buildium: rent, beds, baths, square feet, address, map coordinates, availability, pets, photos and the leasing office phone and email. Paste a subdomain, a link or the manager's website. Export to JSON, CSV or Excel, or via API.

- **URL**: https://apify.com/steadyscrape/appfolio-rentals-scraper.md
- **Developed by:** [Steady Scrape](https://apify.com/steadyscrape) (community)
- **Categories:** Real estate, Lead generation, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 rental listings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## AppFolio & Buildium Rentals Scraper

Scrape the **rental listings of any property manager on AppFolio or Buildium**. This AppFolio scraper and Buildium scraper
reads a manager's public listings (the pages at `<manager>.appfolio.com/listings` and `<manager>.managebuilding.com`) and
gives every vacancy in **one normalized schema** for both platforms: **rent**, **bedrooms**, **bathrooms**, **square feet**,
**full address** split into street, city, state and zip, **map coordinates** (AppFolio), **available date**, **pet policy**,
**photo**, **listing link** and **apply link**.

For Buildium managers it adds what each listing's own page shows: **every photo** with its caption, the **features and
amenities**, the **security deposit**, **lease terms**, the **date listed**, the **property type**, and the **leasing office's
name, phone, email and address**.

Paste a **subdomain**, an **AppFolio or Buildium link**, a **single listing link**, or simply the **property manager's own
website**: the Actor finds its AppFolio or Buildium link for you. Filter by **rent**, **bedrooms**, **bathrooms**, **state**,
**city** and **pets**, and schedule it to get **only new listings and rent changes**. No login, no browser. Pay only for
the listings you get.

### What you can do with this AppFolio and Buildium scraper

- **Rental market research**: asking rents, unit sizes and availability across the managers of a city or a whole
  portfolio, in one table.
- **Real-estate investors and single-family rental funds**: track what competing property managers list, at what rent, and
  how fast rents change.
- **Lead generation for property-management services**: Buildium managers with their leasing office phone and email
  (business contact data published on their listings).
- **Rental listing aggregation and alerts**: schedule a daily run with **Only new listings and rent changes** and receive
  only the vacancies posted, and the rents changed, since the last run.
- **Relocation and housing teams**: pet-friendly homes with three bedrooms under a budget, across the managers you work
  with.

### How it works

- **AppFolio**: the manager's listings page (`https://<manager>.appfolio.com/listings`, then its numbered pages) holds every
  vacancy as a card with rent, beds and baths, square feet, availability, address, pet policy, utilities and appliances,
  and the page's map gives each listing's coordinates. AppFolio's `robots.txt` allows only these list pages and asks
  10 seconds between requests, so the Actor reads nothing else and keeps that pace; a large manager (800+ listings, 3
  pages) takes about 30 seconds.
- **Buildium**: one request reads the manager's public listing feed (`/Resident/PublicPages/XMLRentals.ashx`, the feed
  Buildium publishes for websites to embed listings), with the same listings as its rentals page and everything its
  listing pages show.
- **A manager's own website**: the Actor opens the page you give, finds its link to `<manager>.appfolio.com` or
  `<manager>.managebuilding.com` (a login, application or listings link), and reads that manager. If the home page names
  neither platform it opens the site's rentals page and looks there.
- Every site's `robots.txt` is read first and obeyed.

### Input example

```json
{
  "managers": [
    "https://evernest.appfolio.com/listings",
    "zoulproperties",
    "https://www.sdppm.com/"
  ],
  "maxRent": 2000,
  "minBedrooms": 3,
  "states": ["TX"],
  "maxListingsPerManager": 0
}
```

| Field | What it does |
|---|---|
| **Property managers** | One per line: an AppFolio listings link, a Buildium rentals link, a single listing link of either, the subdomain alone (`evernest`: looked up on AppFolio, then on Buildium), or the manager's own website. |
| **Maximum listings per manager** | Stop each manager after this many listings; 0 reads all of them. |
| **Include Buildium details** | On by default: Buildium listings come with photos, features, deposit, lease terms, date listed and the leasing office contact. Off: as their list shows them, at the lower price. |
| **Minimum / maximum rent** | US$ a month. A listing without a rent is left out when a rent filter is set. |
| **Minimum bedrooms / bathrooms** | 0 bedrooms includes studios; half baths count as 0.5. |
| **States / Cities** | Two-letter state codes (`TX`) and city names (`San Antonio`). |
| **Only pet-friendly** | Keep listings that say some pet is allowed. |
| **Only new listings and rent changes** | For schedules: deliver only listings an earlier run did not, and listings whose rent changed, with the earlier rent. |

### Output: AppFolio and Buildium rental listings

Each listing is one row in the same shape whatever its platform. A manager or link that gives no listing gets one **free
row** with the reason (`status` other than `ok`). An AppFolio listing, a Buildium listing with details, and a free row:

```json
[
  {
    "rowType": "listing",
    "status": "ok",
    "input": "https://evernest.appfolio.com/listings/detail/75b4676e-695d-4c68-b9b7-52731ed8e848",
    "platform": "appfolio",
    "managerId": "evernest",
    "managerName": null,
    "managerUrl": "https://evernest.appfolio.com/listings",
    "listingId": "75b4676e-695d-4c68-b9b7-52731ed8e848",
    "listingUrl": "https://evernest.appfolio.com/listings/detail/75b4676e-695d-4c68-b9b7-52731ed8e848",
    "applyUrl": "https://evernest.appfolio.com/listings/rental_applications/new?listable_uid=75b4676e-695d-4c68-b9b7-52731ed8e848&source=Website",
    "position": 2,
    "title": null,
    "address": "4423 Bonnell Drive Apt D, Hunstville, AL 35816",
    "street": "4423 Bonnell Drive Apt D",
    "city": "Hunstville",
    "state": "AL",
    "zip": "35816",
    "latitude": 34.7442724,
    "longitude": -86.6379711,
    "rent": 675,
    "rentText": "$675",
    "bedrooms": 1,
    "bathrooms": 1,
    "squareFeet": 703,
    "availableDate": "2026-11-08",
    "availableNow": false,
    "petsAllowed": true,
    "petPolicy": "Cats allowed, Small dogs allowed",
    "propertyType": null,
    "photoUrl": null,
    "description": null,
    "utilitiesIncluded": "$35 water/sewer bill to be charged in addition to rent. Owner pays for trash., Tenant must establish the rest of the ...",
    "features": [
      "Electric range",
      "refrigerator",
      "dishwasher"
    ],
    "change": null,
    "previousRent": null,
    "details": null,
    "scrapedAt": "2026-10-28T14:00:00.000Z"
  },
  {
    "rowType": "listing",
    "status": "ok",
    "input": "https://zoulproperties.managebuilding.com/Resident/public/rentals/151286",
    "platform": "buildium",
    "managerId": "zoulproperties",
    "managerName": "Zoul Properties",
    "managerUrl": "https://zoulproperties.managebuilding.com/Resident/public/rentals",
    "listingId": "151286",
    "listingUrl": "https://zoulproperties.managebuilding.com/Resident/public/rentals/151286",
    "applyUrl": "https://zoulproperties.managebuilding.com/Resident/rental-application/account/register?listingId=151286&goToApplication=true",
    "position": 16,
    "title": "Woodland Park Ivanhoe - 1",
    "address": "106 County Road 5, Ivanhoe, MN 56142",
    "street": "106 County Road 5",
    "city": "Ivanhoe",
    "state": "MN",
    "zip": "56142",
    "latitude": null,
    "longitude": null,
    "rent": 650,
    "rentText": "$650",
    "bedrooms": 1,
    "bathrooms": 1,
    "squareFeet": 600,
    "availableDate": "2026-05-18",
    "availableNow": true,
    "petsAllowed": true,
    "petPolicy": "Cats allowed, Dogs allowed",
    "propertyType": "Apartment",
    "photoUrl": "https://zoulproperties.managebuilding.com/Resident/api/public/files/download/listingImages?fileName=585ce4528e2b49ab931144d2048decc6.jpg",
    "description": "1 BR Apartment For Rent. 1 BR/1 BA apartment for rent with water/sewer/trash/heat included. 2 BR available for $750/month. Subsidized rents available. Short term rentals available. Furnished units available for $950/month. Coin-op washer/dryer on site, community room. Some pets allowed. Managed by Zoul Properties.",
    "utilitiesIncluded": null,
    "features": [
      "Pet friendly",
      "Cable",
      "Air conditioner",
      "Refrigerator",
      "Laundry room / hookups",
      "Stove and oven",
      "Heat - electric"
    ],
    "change": null,
    "previousRent": null,
    "details": {
      "photos": [
        {
          "url": "https://zoulproperties.managebuilding.com/Resident/api/public/files/download/listingImages?fileName=585ce4528e2b49ab931144d2048decc6.jpg",
          "caption": null
        },
        {
          "url": "https://zoulproperties.managebuilding.com/Resident/api/public/files/download/listingImages?fileName=ee206b19c5cb4b3199191b79348b8786.jpg",
          "caption": null
        }
      ],
      "deposit": 650,
      "leaseTerms": null,
      "listedDate": "2026-05-18",
      "yearBuilt": null,
      "unitCount": 1,
      "contactName": "Zoul Properties - Ivanhoe",
      "contactPhone": "507-680-1047",
      "contactEmail": "info@zoulproperties.com",
      "officeAddress": "321 West 2nd Street, Grand Island, NE 68801"
    },
    "scrapedAt": "2026-10-28T14:00:00.000Z"
  },
  {
    "rowType": "status",
    "status": "not_found",
    "input": "zzqxjvqnotapm",
    "platform": null,
    "managerId": "zzqxjvqnotapm",
    "url": "zzqxjvqnotapm",
    "reason": "No AppFolio or Buildium property manager is called \"zzqxjvqnotapm\": zzqxjvqnotapm.appfolio.com and zzqxjvqnotapm.managebuilding.com both answer \"not found\". Paste the manager's link or website instead.",
    "scrapedAt": "2026-10-28T14:00:00.000Z"
  }
]
```

### Rental listing fields

| Field | Description |
|---|---|
| `rowType`, `status`, `input`, `scrapedAt` | `listing` rows (`ok`) are charged; `status` rows (`no_results`, `not_found`, `invalid`, `unsupported`, `disallowed`, `duplicate`, `blocked`, `site_error`) are free and carry `url` and `reason`. `input` is the line of your input the row came from; `scrapedAt` is when it was read (UTC, ISO 8601). |
| `platform`, `managerId`, `managerName`, `managerUrl` | `appfolio` or `buildium`, the manager's subdomain, its company name (Buildium; AppFolio's list page does not name the company), and its listings page. |
| `listingId`, `listingUrl`, `applyUrl` | The platform's listing id, the listing page and the rental application link. |
| `position` | The listing's place in the manager's list: AppFolio's page order (most recent first), Buildium's feed order. |
| `title` | AppFolio's headline when the manager wrote one; Buildium's property name. |
| `address`, `street`, `city`, `state`, `zip` | The address as written and split into parts. |
| `latitude`, `longitude` | AppFolio's map pin; Buildium publishes no coordinates. |
| `rent`, `rentText` | Monthly rent in US$ as a number (the lower end of a range) and as shown. |
| `bedrooms`, `bathrooms`, `squareFeet` | 0 bedrooms is a studio. |
| `availableDate`, `availableNow` | The date the unit is available (`YYYY-MM-DD`) and whether it is available today ("NOW" or a past date). |
| `petsAllowed`, `petPolicy` | Whether some pet is allowed, and the policy as `Cats allowed, Small dogs allowed`. |
| `propertyType` | Buildium's type: House for Rent, Apartment, Duplex, Townhouse… |
| `photoUrl` | The main photo. |
| `description` | Buildium's full description; AppFolio's card text, which AppFolio itself cuts at about 400 characters. |
| `utilitiesIncluded`, `features` | AppFolio's utilities line; appliances and amenities (Buildium: with details). |
| `change`, `previousRent` | With **Only new listings and rent changes**: `new` or `rent_changed`, and the earlier rent. |
| `details.photos` | Buildium: every photo, in order, with its caption. |
| `details.deposit`, `details.leaseTerms`, `details.listedDate` | Buildium: security deposit, the manager's lease terms and qualifications, and the date listed. |
| `details.yearBuilt`, `details.unitCount` | Buildium: when given. |
| `details.contactName`, `details.contactPhone`, `details.contactEmail`, `details.officeAddress` | Buildium: the leasing office the listing names, and the management company's office address. |

### Pricing

Pay per event, no start fee:

- **US$0.001 per listing** (`listing`): an AppFolio listing, or a Buildium listing without details.
- **US$0.002 per Buildium listing with details** (`listing-with-details`).
- Free: rows for a manager with no listings, an unknown manager, a website without AppFolio or Buildium, a Propertyware
  link, a duplicate, or a site that stopped answering. Listings left out by your filters, or already delivered in monitor
  mode, are not charged.

### Limits

- **AppFolio listing pages are not read**: AppFolio's `robots.txt` disallows them, so AppFolio rows carry what the list
  shows (no full description, no photo gallery, no office phone). AppFolio's 10-second crawl delay is kept per manager.
- **Propertyware is not supported**: its rental pages load their listings from `webreq.propertyware.com/pw/`, which its
  `robots.txt` closes to every crawler. Such links get a free `disallowed` row.
- There is no search across all managers: you bring the managers. A website that loads its listings by script from a
  platform this Actor does not read gets a free `unsupported` row.
- Only the managers' public listings are read: nothing behind a resident or owner login.

### Is it legal to scrape AppFolio and Buildium rental listings?

This Actor reads only the rental listings property managers publish for prospective tenants, with no login, and obeys
each site's `robots.txt`. It collects business contact data only (a leasing office's phone and email) and never tenant or
owner data. You are responsible for how you use the data, including privacy and fair-housing rules where you work. This
Actor is unofficial and is not affiliated with, endorsed by or sponsored by AppFolio, Inc., Buildium or RealPage, or
Propertyware.

### Feedback

Found a manager whose listings come out wrong, or need another field? Open an issue on the Actor's Issues tab.

# Actor input Schema

## `managers` (type: `array`):

One property manager per line, in any of these forms: an AppFolio listings link (<code>https://evernest.appfolio.com/listings</code>), a Buildium rentals link (<code>https://zoulproperties.managebuilding.com/Resident/public/rentals</code>), a single listing link of either platform, just the subdomain (<code>evernest</code>), or the manager's own website (<code>https://www.sdppm.com/</code>), from which the Actor follows its AppFolio or Buildium link. Propertyware links give a free row: its listing feed is closed to crawlers by robots.txt.

## `maxListingsPerManager` (type: `integer`):

Stop each manager after this many listings. 0 means every listing it publishes.

## `includeDetails` (type: `boolean`):

For Buildium managers, add what each listing's own page shows: every photo with its caption, the features, the security deposit, lease terms, the date listed, and the leasing office's name, phone, email and address (priced as <code>listing-with-details</code>). AppFolio listings always come as their list shows them, because AppFolio's robots.txt closes its listing pages.

## `minRent` (type: `integer`):

Leave empty for any rent. Listings without a rent are left out when a rent filter is set.

## `maxRent` (type: `integer`):

Leave empty for any rent.

## `minBedrooms` (type: `integer`):

0 includes studios. Leave empty for any.

## `minBathrooms` (type: `number`):

Half baths count as 0.5, e.g. <code>1.5</code>. Leave empty for any.

## `states` (type: `array`):

Two-letter state codes, e.g. <code>TX</code>, <code>FL</code>. Managers such as Evernest list homes in 20+ states.

## `cities` (type: `array`):

City names as the listings write them, e.g. <code>San Antonio</code>, <code>Austin</code> (any letter case).

## `onlyPetFriendly` (type: `boolean`):

Keep only listings that say some pet is allowed (cats, dogs or small dogs).

## `onlyNewOrChanged` (type: `boolean`):

For scheduled runs: deliver only listings no earlier run of the same managers and filters delivered, and listings whose rent changed (with the earlier rent). The memory lives in your account's key-value store <code>steadyscrape-appfolio-rentals-state</code>.

## Actor input object example

```json
{
  "managers": [
    "https://evernest.appfolio.com/listings",
    "https://zoulproperties.managebuilding.com/Resident/public/rentals"
  ],
  "maxListingsPerManager": 5,
  "includeDetails": true,
  "states": [],
  "cities": [],
  "onlyPetFriendly": false,
  "onlyNewOrChanged": false
}
```

# Actor output Schema

## `listings` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "managers": [
        "https://evernest.appfolio.com/listings",
        "https://zoulproperties.managebuilding.com/Resident/public/rentals"
    ],
    "maxListingsPerManager": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadyscrape/appfolio-rentals-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "managers": [
        "https://evernest.appfolio.com/listings",
        "https://zoulproperties.managebuilding.com/Resident/public/rentals",
    ],
    "maxListingsPerManager": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("steadyscrape/appfolio-rentals-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "managers": [
    "https://evernest.appfolio.com/listings",
    "https://zoulproperties.managebuilding.com/Resident/public/rentals"
  ],
  "maxListingsPerManager": 5
}' |
apify call steadyscrape/appfolio-rentals-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,steadyscrape/appfolio-rentals-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/rtKwM3ZCrXCYVhg89/builds/yyzlVlkGVDRSfeHGh/openapi.json
