# AppFolio Rental Listings Scraper: Rent, Beds, Map & Apply Link (`themineworks/appfolio-rental-listings-scraper`) Actor

Scrape current rental vacancies from any AppFolio property manager portal: rent, beds, baths, square feet, address with map coordinates, availability, pet policy, photo and apply link. Add the manager's website to get their office phone. Filters included. No login. Pay per vacancy.

- **URL**: https://apify.com/themineworks/appfolio-rental-listings-scraper.md
- **Developed by:** [The Mine Works](https://apify.com/themineworks) (community)
- **Categories:** Real estate, Lead generation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 vacancies

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Rental Listings Scraper: Rent, Beds, Map & Apply Link

### What does AppFolio Rental Listings Scraper do?

Give it a property manager's AppFolio portal: the subdomain (`acme`), the host (`acme.appfolio.com`) or any link on it. Get back every vacancy that manager advertises there right now, one row each: rent, bedrooms, bathrooms, square feet, the address split into street, city, state and ZIP, map coordinates, availability, pet policy, utilities, amenities, appliances, the main photo, the listing link and the link to the rental application.

AppFolio's listings page does not name the property manager or show a phone number. If you give the manager's own website page instead (the page where their AppFolio listings appear), the actor finds the portal on that page and adds the office name, phone and email the manager publishes there.

✅ Subdomain, portal link or the manager's website | ✅ Every current vacancy in one request per portal | ✅ Map coordinates on every row | ✅ Bedroom, rent and city filters | ✅ Reads robots.txt first | ✅ Pay only for vacancies delivered | ✅ MCP-ready for AI agents

### Who is it for?

This is a narrow tool. It is useful when you already know which property managers you care about. It does not search all AppFolio listings in a city, because AppFolio publishes no directory of its portals (see the FAQ for how to find them).

Typical users: rental market analysts tracking the asking rents of specific managers, investors watching supply from the managers in their market, vendors who sell to property managers and want to know who has vacancies right now, and aggregators that want listings which never reach the big rental sites.

### How much does it cost to scrape AppFolio listings?

You pay per vacancy delivered to your dataset, plus a flat $0.005 per run whatever memory you choose.

| Apify plan | Per vacancy |
| --- | --- |
| Free | $0.002 |
| Starter (Bronze) | $0.0017 |
| Scale (Silver) | $0.0014 |
| Business (Gold) and above | $0.001 |

So 1,000 vacancies cost 1,000 times the per vacancy price of your plan, plus $0.005 for the run. Filters, the manager website lookup, the map coordinates and the proxies are included.

**Never charged:** portals that refused the connection, portals with no vacancies, start URLs that are not a live AppFolio portal, vacancies your filters left out, vacancies above your per portal limit, the same vacancy found twice in one run, and the information row. The Pricing tab always shows the rate for your own plan; if this table and the Pricing tab ever disagree, the Pricing tab is right.

### How does it get the listings without logging in?

Every AppFolio portal has a public listings page, for example `https://apg.appfolio.com/listings`. It holds all of the manager's current vacancies on one page, plus the map data with each vacancy's coordinates. The actor reads that page, one request per portal, and turns each vacancy into a row. There is no paging: a portal with 168 vacancies (the largest one in our tests) is still one request.

Before any request the actor reads the host's robots.txt and follows it. AppFolio's robots.txt allows crawlers on the listings page and asks them to stay out of everything else, including the listing detail pages. So the actor never opens a detail page. That is why the description is the first few hundred characters the listings page shows, why there is one photo per vacancy, and why there is no deposit or office phone from AppFolio itself.

Given a manager's website, the actor reads that page (robots.txt checked there too), and if it does not link to an AppFolio portal, up to two pages on the same site that look like a rentals page. The portal found is then read as above.

Connections go through Apify datacenter proxies. In our tests on 28 Sep 2026 every portal answered on the first attempt. If a portal refuses three times, the actor tries once through Apify's unblocking proxy; refused portals are listed in the run summary and never charged.

### What input does it take?

```json
{
  "startUrls": [
    "rivertonrentals",
    "https://apg.appfolio.com/listings",
    "https://www.sdppm.com/available.html"
  ],
  "maxListings": 100,
  "minBedrooms": 2,
  "maxRent": 3000,
  "cities": ["Sacramento", "El Cajon, CA"]
}
```

| Input | Default | What it does |
| --- | --- | --- |
| `startUrls` | none | One per line: a subdomain (`acme`), a host (`acme.appfolio.com`), any link on the portal (the listings page, one listing, the tenant login page), or the manager's own website page. A link to one listing returns only that listing. Up to 1,000 per run. `startUrl`, `urls` and `portals` are accepted too |
| `maxListings` | 100 | Most vacancies to return from each portal (1 to 5,000) |
| `minBedrooms` | empty | Only vacancies with at least this many bedrooms. A studio counts as 0 |
| `minBathrooms` | empty | Only vacancies with at least this many bathrooms |
| `minRent`, `maxRent` | empty | Monthly rent range in US dollars. Vacancies with no rent shown are left out when a rent filter is set |
| `cities` | empty | Only vacancies in these cities. `Oakland, CA` pins the state, `Oakland` matches any state |
| `allowUnblockerFallback` | true | Retry a refused portal once through Apify's unblocking proxy |

The filters are the same ones the portal page offers. The actor applies them to each portal's full list after reading it, because the portal returns everything in one page anyway. They save you money (filtered vacancies are not charged), not time.

### What data do you get back?

One row per vacancy. This row is from a local test run on 28 Sep 2026, unchanged:

```json
{
  "listing_id": "fb82f7f9-2f59-4206-8641-aa76ab8dc1f9",
  "listing_url": "https://rivertonrentals.appfolio.com/listings/detail/fb82f7f9-2f59-4206-8641-aa76ab8dc1f9",
  "portal": "appfolio",
  "portal_url": "https://rivertonrentals.appfolio.com/listings",
  "title": "2 Bedroom 1 Bath Upstairs Apartment with Shared Laundry and Storage",
  "address": "701 Spencer - 701-5, Apt. 5, Riverton, WY 82501",
  "street": "701 Spencer - 701-5, Apt. 5",
  "city": "Riverton",
  "state": "WY",
  "postal_code": "82501",
  "country": "US",
  "latitude": 43.039218,
  "longitude": -108.382535,
  "rent": 725,
  "rent_text": "$725",
  "currency": "USD",
  "bedrooms": 2,
  "bathrooms": 1,
  "square_feet": 800,
  "available_date": "2026-11-13",
  "available_now": false,
  "pets_policy": "Cats not allowed, Dogs not allowed",
  "pets_allowed": false,
  "amenities": ["Shared Coin-operated Laundry Room", "Very Clean", "Ample Storage", "Quiet", "Well Maintained", "Upstairs Unit"],
  "appliances": ["Refrigerator", "Electric Range/Oven"],
  "utilities_included": "City of Riverton (Sewer Water Trash)",
  "description": "www.RivertonRentals.com\nRiverton Rentals Property Management LLC\n307 856 7178\n\nYoutube video walkthrough of us walking through the unit (if link is not shown please go to Riverton Rentals dot com website and find listing there):\n\nhttps://youtu.be/ig_3-DCLy6U\n\n(please watch this before filling out rental application, rental application required prior to seeing unit in person.)\n\n2 Bed...",
  "description_truncated": true,
  "photo_urls": ["https://images.cdn.appfolio.com/rivertonrentals/images/22d5e883-d549-45b0-82cf-7693bec16109/large.jpg"],
  "apply_url": "https://rivertonrentals.appfolio.com/listings/rental_applications/new?listable_uid=fb82f7f9-2f59-4206-8641-aa76ab8dc1f9&source=Website",
  "source_input": "rivertonrentals",
  "scraped_at": "2026-09-28T11:53:16.346Z"
}
```

Started from the manager's website (`https://www.sdppm.com/available.html` in our test), rows also carry `manager_name` (San Diego Professional Property Managers), `manager_phone` ((858) 627-9320), `manager_email` when the site publishes one, and `manager_website`.

| Field | Description |
| --- | --- |
| `listing_id`, `listing_url` | AppFolio listing id and the public listing link |
| `portal`, `portal_url` | Always `appfolio`, and the portal listings page |
| `manager_name`, `manager_phone`, `manager_email`, `manager_website` | Only when you started from the manager's website |
| `title`, `address`, `street`, `city`, `state`, `postal_code`, `country` | Headline and address |
| `latitude`, `longitude` | From the portal map |
| `rent`, `rent_max`, `rent_text`, `currency` | Monthly rent as a number (the low end of a range), the high end, the text shown, and USD |
| `bedrooms`, `bathrooms`, `square_feet` | Studio is 0 bedrooms; half baths count (1.5) |
| `available_date`, `available_now` | Move in date, or `available_now: true` when the portal says NOW |
| `pets_policy`, `pets_allowed` | The policy text, and true or false when it is clear |
| `amenities`, `appliances`, `utilities_included` | As the portal lists them |
| `property_type`, `lease_type`, `rent_per_sqft` | Set on commercial listings (offices, retail) |
| `description`, `description_truncated` | The description as the listings page shows it |
| `photo_urls` | The main photo |
| `apply_url` | The rental application for this vacancy |
| `source_input`, `scraped_at` | The start URL the row came from, and when it was collected |

Empty fields are left out rather than sent as `null`. Each run also writes a summary to the key-value store record `OUTPUT`: a status for every start URL (`ok`, `no_vacancies`, `no_matches`, `not_a_portal`, `blocked`, `robots_disallowed`, `duplicate`), vacancies found and delivered, attempts per connection type, and the robots.txt rule that allowed each request. A final `_type: "info"` row appears only when nothing was delivered, and is never charged.

### What are the limitations?

**You bring the portals.** There is no search across all of AppFolio. See the FAQ for ways to find portals.

**No manager contact from AppFolio.** The listings page names no manager and shows no phone. Start from the manager's website to get them.

**Listings page only.** AppFolio's robots.txt closes the detail pages to crawlers, so the actor does not read them: descriptions are cut at a few hundred characters, there is one photo per vacancy and no deposit. Everything else on the listings page is returned.

**Fill rates** in our tests (187 vacancies from 4 portals): rent, address, coordinates, photo and apply link on every row; bedrooms 99%; availability 90% (a date or NOW); pet policy 75%; square feet 55%; title 88%.

**Limits.** Up to 1,000 start URLs per run and 5,000 vacancies per portal. AppFolio asks crawlers to wait 10 seconds between requests to the same portal; the actor makes one request per portal, and waits when it needs a second.

### What can you use it for?

**Rent tracking.** Run it weekly on the managers in your market and compare asking rents by bedroom count and ZIP code.

**Supply monitoring.** See how many units each manager has open, and how long they stay open between runs.

**Lead generation for property services.** Start from managers' websites to get each office's name and phone next to its live vacancy count.

**Listings feeds.** Pull structured vacancies with coordinates and apply links into your own map or search.

### How do I get started?

1. Paste portals into **AppFolio portals or manager websites**, one per line.
2. Set **Vacancies per portal**, and any filters you want.
3. Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API or MCP.

### Can I run it on a schedule?

Yes. Run it once with the input you want, click **Save as a task**, then in Apify Console go to **Schedules**, create a schedule, pick how often (for example every Monday) and add the task. Each scheduled run is billed the same way as a manual one.

### FAQ

#### How do I find AppFolio portals?

AppFolio does not publish a list, and this actor does not discover portals on its own. What works:

- **The manager's website.** Most managers who use AppFolio show their vacancies from it. The "Available rentals", "Apply" or "Pay rent" links point to `something.appfolio.com`. You can paste the website page itself and the actor finds the portal.
- **Web search.** Searching for `appfolio.com/listings` together with a city or state name returns portals that search engines have indexed. It is partial, but it is free and quick.
- **Rental ads.** Ads on the big rental sites often link to the manager's AppFolio application page; the subdomain in that link is the portal.
- **Your own lists.** If you already have a list of property managers, their websites are the fastest route.

A subdomain that is not a live portal is reported as `not_a_portal` and costs nothing.

#### How much does it cost to scrape 1,000 AppFolio vacancies?

1,000 times the per vacancy price of your plan (see the table above), plus $0.005 for the run. Portals with no vacancies and vacancies your filters leave out are not charged.

#### Why is there no phone number or manager name?

AppFolio's public listings page does not show them, and the detail pages that do are closed to crawlers by AppFolio's robots.txt. Start from the manager's own website page and the actor adds the office name, phone and email that the manager publishes there.

#### Why is the description cut off?

The listings page shows the first few hundred characters of each description; the rest is on the detail page, which the actor does not open. `description_truncated` tells you when the text was cut.

#### Does it respect robots.txt?

Yes. It reads robots.txt on every host before requesting anything from it, applies it the way Google does (wildcards, end anchors, the most specific rule wins), and never requests a page it disallows. It also keeps AppFolio's 10 second crawl delay between requests to the same portal. The run summary records the rule that allowed each request.

#### Am I charged for portals with no vacancies or links that are not portals?

No. Only vacancies delivered to your dataset are charged.

#### What about Buildium portals?

Buildium runs on different software. Use [Buildium Rental Listings Scraper](https://apify.com/themineworks/buildium-rental-listings-scraper) for `managebuilding.com` links; this actor tells you when a link belongs there.

#### Is it legal to scrape AppFolio listings?

The actor collects only public rental advertisements, which describe properties, not people. It never logs in, reads no tenant or applicant data, and follows each site's robots.txt. The manager contact it adds is the business contact the manager publishes on its own website. How you store and use the data is up to you and your local laws. This is general information, not legal advice.

### Use in Claude, ChatGPT and any MCP agent

Add it to Claude Code in one line:

```bash
claude mcp add --transport http apify "https://mcp.apify.com/?tools=themineworks/appfolio-rental-listings-scraper"
```

Or point any MCP client at:

```
https://mcp.apify.com/?tools=themineworks/appfolio-rental-listings-scraper
```

Things an agent can ask once connected:

- "List the 2 bedroom vacancies under $2,500 on rivertonrentals.appfolio.com."
- "How many vacancies does each of these five property managers have right now?"
- "Get the office phone and current vacancies for the manager at this website."

Or call it from code with the Apify client:

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

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('themineworks/appfolio-rental-listings-scraper').call({
  startUrls: ['rivertonrentals', 'https://apg.appfolio.com/listings'],
  minBedrooms: 2,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((v) => `${v.address}: ${v.rent_text}`));
```

### More from The Mine Works

Browse every tool at [themineworks.com](https://themineworks.com/). Related actors for rental data:

- **[Buildium Rental Listings Scraper](https://apify.com/themineworks/buildium-rental-listings-scraper)**: the same for property managers on Buildium, with the office phone and email from the feed.
- **[Zillow Rentals Scraper](https://apify.com/themineworks/zillow-rental-listings)**: for-rent listings from Zillow by city or ZIP code.

Found a bug or want a field added? Open an issue on the actor's Apify Console page.

***

**Disclaimer:** This actor is an independent tool and is not affiliated with, endorsed by, or sponsored by AppFolio, Inc. AppFolio is a trademark of AppFolio, Inc. Use the data in line with the law where you are.

# Actor input Schema

## `startUrls` (type: `array`):

Required. One per line. A property manager's AppFolio subdomain (acme), its host (acme.appfolio.com), any link on it (the listings page or one listing), or the manager's own website page that shows their AppFolio listings (the actor finds the portal on it and adds the office name, phone and email published there). Up to 1,000 per run.

## `maxListings` (type: `integer`):

Most vacancies to return from each portal. A portal lists all its vacancies on one page, so this only trims the result; it does not make the run faster.

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

Only vacancies with at least this many bedrooms. A studio counts as 0. Vacancies that do not state bedrooms are left out when this is set.

## `minBathrooms` (type: `integer`):

Only vacancies with at least this many bathrooms. Half baths count, so 1.5 passes a minimum of 1.

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

Only vacancies asking at least this much a month. Vacancies with no rent shown are left out when a rent filter is set.

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

Only vacancies asking at most this much a month.

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

Only vacancies in these cities, one per line. Add the state to be exact: Oakland, CA. Leave empty for every city the portal covers.

## `allowUnblockerFallback` (type: `boolean`):

Portals are read over a fast connection first. If a portal refuses it three times, try once more through Apify's unblocking proxy. Included in the price. Turn off to report such portals as blocked instead.

## Actor input object example

```json
{
  "startUrls": [
    "https://apg.appfolio.com/listings",
    "https://www.sdppm.com/available.html"
  ],
  "maxListings": 20,
  "allowUnblockerFallback": true
}
```

# Actor output Schema

## `results` (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 = {
    "startUrls": [
        "https://apg.appfolio.com/listings",
        "https://www.sdppm.com/available.html"
    ],
    "maxListings": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("themineworks/appfolio-rental-listings-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 = {
    "startUrls": [
        "https://apg.appfolio.com/listings",
        "https://www.sdppm.com/available.html",
    ],
    "maxListings": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("themineworks/appfolio-rental-listings-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 '{
  "startUrls": [
    "https://apg.appfolio.com/listings",
    "https://www.sdppm.com/available.html"
  ],
  "maxListings": 20
}' |
apify call themineworks/appfolio-rental-listings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,themineworks/appfolio-rental-listings-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/DGU7KsFAL7u1R9m6D/builds/IHwRvFFgmfL3D8Fck/openapi.json
