# Rent Comps API - Apartment Rents, Concessions, Comps (`nabeelbaghoor/rent-comps-data-api`) Actor

Get US multifamily rent comps with asking and effective rents net of concessions, unit level availability and days on market, comparable buildings, predicted market rent per unit, and operating expense benchmarks for any apartment building.

- **URL**: https://apify.com/nabeelbaghoor/rent-comps-data-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Real estate, Business, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.00 / 1,000 building record returneds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Rent Comps API - Apartment Rents, Concessions, Comps

Get US multifamily rent comps where the asking rent and the rent net of concessions are both on the row, unit by unit, plus comparable buildings, predicted market rent and a full operating expense estimate.

### What this actor does

- **Asking rent and effective rent, on every unit.** Effective rent is the asking rent net of the concessions the building is currently running. Two months free on a twelve month lease is about a sixteen percent difference, so a comp set built on asking rents alone is wrong in exactly the markets where it matters. Both figures, the concession value, the concession percentage and both per square foot rates are on every unit row.
- **Unit level detail.** One row per apartment with bedrooms, bathrooms, square footage, floor, days on market, availability dates, lease term, deposit and amenities. Advertised floorplans are reported as their own row type rather than counted as apartments.
- **Comparable buildings.** Distance, similarity score and full attributes, filtered by unit count, year built, storey count, distance, proximity to a university or hospital, and the affordable, student, senior, build to rent, condominium and lease up flags. Force specific buildings in or out.
- **Predicted market rent per unit.** What the provider thinks each unit should be asking, on both the asking and effective bases, with the signed gap to what it is actually asking and the number of comparable units behind the prediction.
- **Operating expense benchmarks.** A predicted operating statement: gross potential rent, vacancy loss, effective gross income, other income, total operating expenses and NOI, with taxes, insurance, utilities, payroll, repairs, marketing, management fees and reserves broken out.
- **Building quality scores.** An overall score derived from the building's photographs, plus per room scores for the kitchen, bathroom, bedroom, common areas, fitness centre and pool.
- **Search a drawn polygon.** Every building inside an area you define, filtered on rent, square footage, quality, review score and amenities.
- **Pay per result.** You are charged for rows that carry a real record. Property IDs the provider does not hold are free.

### Bring your own key

This actor calls the provider with **your own API key**, from the API section of your account. Paste it into the API key field, or set it as the `DATA_API_KEY` secret. Nothing runs on anyone else's credentials, and a run without a key ends cleanly with an explanation rather than failing.

The provider sells endpoint access by plan. A key that works everywhere else and is refused on one endpoint returns a 403, and this actor reports that as a plan problem rather than a bad key, so you are not left changing a key that was never wrong.

### Start here, and note what is free

Property search costs nothing on the provider's plans. Every other endpoint costs one request. So resolve your whole address list to property IDs first, in one free run, then read only the buildings you actually want. A hundred addresses resolved is free; a hundred reports is a hundred requests.

### Example output

```json
{
  "recordType": "unit",
  "requested": "7f1c2b5a-3d4e-5f60-8a9b-0c1d2e3f4a5b",
  "found": true,
  "propertyId": "7f1c2b5a-3d4e-5f60-8a9b-0c1d2e3f4a5b",
  "buildingName": "The Wacker",
  "streetAddress": "111 W Wacker Dr",
  "city": "Chicago",
  "state": "IL",
  "unitName": "2104",
  "floorplanName": "B2 Corner",
  "isFloorplan": false,
  "bedrooms": 2,
  "bathrooms": 2,
  "sqft": 1104,
  "floor": 21,
  "price": 4295,
  "effectivePrice": 3937,
  "concessionValue": 358,
  "concessionPercent": 8.34,
  "pricePerSqft": 3.89,
  "effectivePricePerSqft": 3.57,
  "daysOnMarket": 18,
  "availability": "available",
  "leaseTerm": "12"
}
```

### FAQ

#### What is the difference between asking rent and effective rent?

Asking rent is the advertised number. Effective rent is that number net of the concessions the building is currently offering, spread across the lease term. A building advertising 2,000 dollars with two months free on a twelve month lease has an effective rent of about 1,667. Both are on every unit row, along with the difference in dollars and as a percentage, because which one you should use depends on the question: asking rent for a marketing comparison, effective rent for underwriting.

#### Why are there separate unit and floorplan rows?

Because the provider's availability data contains both, and they are different things. A floorplan is the advertised unit type - "B2 Corner, 2 bed, 1104 sq ft" - and a unit is a specific apartment such as 2104. Counting both as apartments double counts the building. Floorplan rows are off by default and the `isFloorplan` flag says which kind of row you are looking at when you turn them on.

#### How do I tell whether a unit is underpriced?

Use market rents mode. It returns the provider's predicted rent for each unit next to what the unit is actually asking, and a signed gap column: a positive gap means the provider thinks the unit should be asking more. Check the supporting unit count too, because a prediction built on three comparable units is a guess and one built on ninety is not.

#### Why does this actor read the property report before finding comparables?

Because the comparables, market rents and expense benchmark endpoints all take the whole subject property in the request body rather than its ID, and the provider's own documentation recommends passing the full report rather than a thin object: it then uses the quality scores, the unit detail and the market statistics in it to pick comparables. That costs one extra request per subject and produces a materially better answer, so the actor does it for you.

#### What are the allow-unknown switches for?

Every attribute filter has to decide what to do with a building whose value the provider does not know. Allowing unknowns through a year-built filter keeps older stock that has no year on file; disallowing them drops it. Neither default is right for every question, so the actor only sends these when you set them, and otherwise leaves the choice to the provider.

#### Why does affordable or student housing need its own comp set?

Because those buildings are not priced against the market. A rent restricted building prices against an income limit, and student housing is quoted per bed rather than per unit. Mixing either into a conventional comparable set drags the whole set in a direction that has nothing to do with the market. The flags let you include or exclude them deliberately.

#### How do I write a polygon?

One `latitude,longitude` pair per line, at least three of them, in order around the boundary. The actor closes the ring for you and handles the axis order the provider expects, which is longitude first - getting that backwards is the classic mistake and it silently searches the wrong part of the world. If a latitude is outside -90 to 90 the actor tells you the pair is probably the wrong way round rather than sending it.

#### Can I get a market's shape rather than drawing one?

Yes. Shape search mode takes a market name such as "Austin, TX" and returns its boundary polygon along with its centre and area. Feed that polygon straight into market search mode.

#### Do property IDs change?

Occasionally. The provider's IDs are UUIDs, and it merges records when it works out that two entries describe the same building, which retires one of the IDs. An ID that has stopped working is reported as not found with that explanation, and re-resolving the address through the free search mode gives you the current one.

#### What does it cost?

Pricing is pay per result: 0.02 USD per full property report, 0.02 USD per expense benchmark estimate, 0.015 USD per rent prediction, 0.008 USD per building record, 0.006 USD per market shape and 0.005 USD per unit, with Apify platform usage included. Rows the provider found nothing for are not charged. You also need your own account with the data provider, which bills per request on its own plans.

#### Does the actor fail if my key is wrong?

No. A missing key, a rejected key, an endpoint outside your plan, an exhausted allowance or an empty input all end the run cleanly with a message saying what to change. Only a real outage or a bug fails the run.

### Keyword map

rent comps API, apartment rent data API, multifamily data API, effective rent concessions, rent concession data, apartment market rent prediction, unit level rent data, multifamily underwriting data, operating expense benchmarks multifamily, NOI estimate apartment, apartment comparables API, rental market analysis API, days on market apartments, revenue management pricing detection, build to rent data, student housing rent data, affordable housing rent data, apartment building quality score

# Actor input Schema

## `mode` (type: `string`):

Property search resolves addresses to the provider's IDs and is free on its plans, so start there. Property report reads one building in full. Units returns one row per apartment with its asking and effective rent. Comparables finds similar buildings. Market rents says what each unit should be asking. Expense benchmarks predicts the operating statement. Market search reads every building inside a polygon.

## `searchTerm` (type: `string`):

Property search mode. A partial address or building name, for example 111 w wacker. This endpoint costs nothing on the provider's plans, so resolving a long list of addresses here is free.

## `state` (type: `string`):

Property search mode. A two letter state code, which narrows a name that repeats across states.

## `zipCode` (type: `string`):

Property search mode. A five digit ZIP code.

## `latitude` (type: `string`):

Property search mode. Give a longitude with it to search around a point.

## `longitude` (type: `string`):

Property search mode. Give a latitude with it to search around a point.

## `propertyIds` (type: `array`):

One property ID per line, as returned by property search. Used by the report, units, comparables, market rents and expense benchmark modes. IDs are UUIDs and can change when the provider merges two records for the same building, so re-resolve one that stops working.

## `includeFloorplans` (type: `boolean`):

Units mode. Off by default. A floorplan is the advertised unit type rather than a specific apartment, so counting both double counts the building.

## `historyFrom` (type: `string`):

As YYYY-MM-DD. How far back the unit rent history goes in the report. A longer window is a bigger response rather than a more expensive request.

## `historyTo` (type: `string`):

As YYYY-MM-DD.

## `occupancyOverTime` (type: `boolean`):

Adds the building's occupancy series to the report, which is how to see whether a rent cut was followed by a fill.

## `useTrailingMarketLeasePrice` (type: `boolean`):

Reads the market from recently signed leases rather than from what is being asked today. The steadier of the two bases in a fast moving market.

## `comparablesCount` (type: `integer`):

Comparables mode. How many similar buildings to return, best match first.

## `comparablesTier` (type: `string`):

Comparables mode. Current compares against the building as it stands. Value add compares against what it would be after renovation, which is the right basis for an acquisition underwriting.

## `renovationLevel` (type: `integer`):

Comparables mode, on the value add tier. How extensive a renovation to assume.

## `selectedIds` (type: `array`):

Comparables mode. One property ID per line that must be included whatever the rules say.

## `excludedIds` (type: `array`):

Comparables mode. One property ID per line to exclude, which is how to drop your own properties from a competitive set.

## `minDistanceMiles` (type: `integer`):

Comparables mode. Excludes buildings closer than this, which is occasionally useful for excluding a sister property next door.

## `maxDistanceMiles` (type: `integer`):

Comparables mode and property search. How far out to look.

## `minNumberUnits` (type: `integer`):

Comparables and market search modes. A building's total apartment count.

## `maxNumberUnits` (type: `integer`):

Comparables and market search modes.

## `allowNullNumberUnits` (type: `boolean`):

Whether a building whose unit count the provider does not know survives the unit count filter. Left unset the provider decides; setting it either way is a deliberate choice between letting unknowns through and dropping them.

## `minYearBuilt` (type: `integer`):

Comparables and market search modes. A four digit year.

## `maxYearBuilt` (type: `integer`):

Comparables and market search modes. A four digit year.

## `allowNullYearBuilt` (type: `boolean`):

Whether a building with no year on file survives the year filter. Older stock is the most likely to be missing one.

## `minNumberStories` (type: `integer`):

Comparables and market search modes. Storey count separates a garden style property from a mid rise.

## `maxNumberStories` (type: `integer`):

Comparables and market search modes.

## `allowNullNumberStories` (type: `boolean`):

Whether a building with no storey count on file survives the storey filter.

## `maxMilesToUniversity` (type: `integer`):

Comparables mode. Keeps only buildings within this distance of a university, which is how to build a student housing comparable set.

## `maxMilesToHospital` (type: `integer`):

Comparables mode. Keeps only buildings within this distance of a hospital.

## `useEmbedding` (type: `boolean`):

Comparables mode. Uses the provider's learned building similarity rather than the attribute rules alone, which finds buildings that feel comparable without matching on paper.

## `isApartment` (type: `boolean`):

Comparables and market search modes. Leave this alone to ignore it; set it either way to filter.

## `isCondo` (type: `boolean`):

Comparables and market search modes. Leave this alone to ignore it.

## `isSingleFamily` (type: `boolean`):

Comparables and market search modes. Leave this alone to ignore it.

## `isBuildToRent` (type: `boolean`):

Comparables and market search modes. Purpose built rental houses rather than converted ones.

## `isStudent` (type: `boolean`):

Comparables and market search modes. Student rents are quoted per bed rather than per unit, so mixing them into a conventional comp set skews it.

## `isSenior` (type: `boolean`):

Comparables and market search modes.

## `isAffordable` (type: `boolean`):

Comparables and market search modes. Rent restricted buildings price against an income limit rather than against the market, so they belong in their own comp set.

## `isLeaseUp` (type: `boolean`):

Comparables mode. A building still filling for the first time discounts heavily, which distorts a stabilised comparison.

## `polygon` (type: `array`):

Market search mode. One "latitude,longitude" pair per line, at least three of them, in order around the boundary. The ring is closed for you and the axis order the provider needs is handled for you. Shape search mode returns a named market's own polygon.

## `minUnitRent` (type: `integer`):

Market search mode. Keeps only buildings with units at or above this rent.

## `maxUnitRent` (type: `integer`):

Market search mode. Keeps only buildings with units at or below this rent.

## `rentFilterType` (type: `string`):

Market search mode. The asking price, or the price net of concessions. In a market giving away free months these select materially different buildings.

## `minUnitSqft` (type: `integer`):

Market search mode.

## `maxUnitSqft` (type: `integer`):

Market search mode.

## `minBuildingQuality` (type: `integer`):

Market search mode. The provider's own quality score, derived from the building's photographs.

## `maxBuildingQuality` (type: `integer`):

Market search mode. The provider's own quality score.

## `minReviewScore` (type: `integer`):

Market search mode. The building's resident review score.

## `maxReviewScore` (type: `integer`):

Market search mode.

## `includedAmenities` (type: `array`):

Market search mode. One amenity per line, in the provider's own spelling, for example Pool or In Unit Laundry. The amenity lists on a property report are where the exact spellings come from.

## `excludedAmenities` (type: `array`):

Market search mode. One amenity per line to exclude.

## `listingStatus` (type: `string`):

Market search mode. Active is what is advertised now; inactive is what has been taken down, which is how to study recent leasing history.

## `marketNames` (type: `array`):

Shape search mode. One market name per line, for example Austin, TX. Each returns the market's polygon, which you can feed straight into market search mode.

## `maxResults` (type: `integer`):

The most rows this run will save and charge for. This is the spending control on both sides.

## `requestsPerMinute` (type: `integer`):

How fast to call the provider. Its hard ceiling is 500 per rolling minute, and the actor follows the reset time the provider publishes when it does rate limit.

## `baseUrl` (type: `string`):

Only set this if the provider has given you a different host. Leave empty to use the documented one.

## `apiKey` (type: `string`):

Your own API key for the rental market data provider, from the API section of your account. This actor is bring your own key: it never uses anyone else's credentials, and the key is stored as a secret.

## Actor input object example

```json
{
  "mode": "propertySearch",
  "includeFloorplans": false,
  "comparablesCount": 10,
  "comparablesTier": "current",
  "maxResults": 1000,
  "requestsPerMinute": 240
}
```

# Actor output Schema

## `records` (type: `string`):

One row per record, alongside the search, property ID or market that produced it.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/rent-comps-data-api").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/rent-comps-data-api").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 '{}' |
apify call nabeelbaghoor/rent-comps-data-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nabeelbaghoor/rent-comps-data-api"
        }
    }
}
```

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/n5QqRhHvNgPwfhXCP/builds/SQd8LHkcmqEMLTU64/openapi.json
