# Crexi Scraper & New Listings Monitor - CRE Price Drop Alerts (`neverempty/crexi-new-listings-monitor`) Actor

Crexi scraper for commercial real estate: properties for sale or lease by state or city, newest first, with price, cap rate, NOI, building and lot size, property type, address and brokerage. Watch mode returns only new listings and price drops. Export as CSV, Excel or JSON.

- **URL**: https://apify.com/neverempty/crexi-new-listings-monitor.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Real estate, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $12.00 / 1,000 listing returneds

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

## Crexi Scraper & New Listings Monitor

**A Crexi scraper that turns Crexi commercial real estate listings into clean rows, and a new listings alert for commercial property.** Pick a US state or city, choose for sale or for lease, and get every listing newest on Crexi first: asking price, cap rate, NOI, price per square foot, building and lot size, property type, address, coordinates and the brokerage. Turn on **watch mode** and schedule it: each run returns only the properties that appeared on Crexi since your last check and the ones whose asking price went down.

Built for CRE investors who want new deals before others call on them, brokers tracking their market, and analysts who need Crexi data in a spreadsheet without copying it by hand. No Crexi account, no API key and no cookies are needed.

Export as JSON, CSV or Excel, or push new listings to Slack, email or a Google Sheet with an Apify integration.

### What you get

One row per Crexi property:

| Field | Example | Notes |
|---|---|---|
| `listingId`, `listingType`, `url` | `sales-2742881`, `sale`, `https://www.crexi.com/properties/2742881/texas-walgreens` | Crexi's own ID and the listing page |
| `title`, `summary` | `Walgreen's`, `Retail \| 6.75% CAP \| 14,820 SqFt` | The listing name and the one-line summary Crexi shows in search |
| `propertyType`, `propertySubType` | `Retail`, `Pharmacy/Drug` | As Crexi classifies it (a property can have several) |
| `listingStatus` | `On-Market`, `Auction`, `Call For Offers`, `Active` (lease) | |
| `price`, `currency`, `pricePerSqft`, `pricePerAcre` | `6133733`, `USD`, `413.88` | Asking price. Unpriced listings get `null` (never 0) |
| `capRatePercent`, `netOperatingIncome` | `6.75`, `414027` | When the listing states them |
| `auctionStartingBid` | | Auction listings only |
| `leaseRatePerSqftYearMin`, `leaseRatePerSqftYearMax`, `rentableSqftMin`, `rentableSqftMax`, `spacesCount` | `18`, `24`, `1200`, `5400`, `3` | Lease listings: the range across the spaces offered |
| `buildingSqft`, `lotSizeAcres`, `lotSizeSqft`, `unitsCount`, `buildingsCount`, `storiesCount`, `buildingClass` | `14820`, `1.7` | |
| `yearBuilt`, `yearRenovated`, `tenancyType`, `investmentType` | `2017`, `Single`, `Net Lease` | |
| `address`, `streetAddress`, `city`, `state`, `zip`, `county`, `latitude`, `longitude` | `15633 Vintage Preserve Parkway, Houston, TX 77070` | `addressCount` is above 1 for portfolios |
| `brokerage` | `SVN \| Net Lease Texas` | The listing brokerage firm(s) |
| `listedAt`, `updatedAt`, `daysOnMarket` | `2026-10-10T19:53:32.059Z` | When the listing went live and was last updated on Crexi (UTC) |
| `photoCount`, `imageUrl`, `hasVirtualTour` | `7` | Main photo link |
| `changeType`, `previousPrice`, `priceChange` | `new`, `price-drop`, `6400000`, `-266267` | Watch mode only |
| `positionInSearch`, `listingsInSearch`, `searchLocation` | `1`, `446`, `Houston, TX` | Where the listing was in the search, and how many Crexi counts for it |

In a sample of 282 Texas listings for sale (2026-10-12): asking price was present in 220, building size in 194, lot size in 208 (acres) or 34 (square feet), year built in 156, a brokerage in 273, a main photo in 268, and a cap rate in 48 (most often on retail and net-lease listings). In 60 Texas lease listings, a lease rate per square foot was present in 33 and rentable square feet in all 60.

### How to use it

**Plain search**: put locations in `locations`, one per line, as a state (`TX` or `Texas`) or a city with its state (`Houston, TX`).

```json
{ "locations": ["Houston, TX", "Dallas, TX"], "listingType": "sale", "maxResultsPerSearch": 100 }
```

`maxResultsPerSearch` sets how many listings each location returns (1 to 1,000; 50 when empty).

Filters: `propertyTypes`, `minPrice`, `maxPrice` and `minCapRate` (for sale), and `listingType: "lease"` for spaces for lease. Crexi applies them, and every row is checked again against your values before it is returned.

```json
{ "locations": ["Florida"], "propertyTypes": ["Retail"], "minPrice": 1000000, "maxPrice": 5000000, "minCapRate": 6.5 }
```

```json
{ "locations": ["Austin, TX"], "listingType": "lease", "propertyTypes": ["Office"] }
```

**Watch mode** (`onlyNew: true`): run the same input on a schedule (for example every hour or every morning).

1. The first run saves the current listings as the starting point. It returns no listing rows and is not charged.
2. Every later run returns only:
   - `new`: listings that appeared on Crexi after the watch started and match your filters;
   - `price-drop`: properties for sale the watch has seen whose asking price is now lower, with `previousPrice` and `priceChange`.
3. If nothing changed, you get one row `no-new-listings` saying so.

Set `includePriceDrops` to `false` to get only new listings. Use `watchName` to keep two separate watches of the same search, and `resetMonitoringState` to start a watch again.

### Pricing

- **Per listing returned** (`listing-returned`): you pay only for property rows. No start fee.
- **Watch check with nothing new** (`search-checked`): in watch mode, one small charge for each location search that was compared and had nothing new to return.
- Free: the first run of a watch, listings your filters leave out, and every row that explains why something was not returned.

### Output rows that cost nothing

Every problem comes back as a row with `status` and a plain-English `note`, and is never charged:

- `no-results`: Crexi lists nothing for this location (for example a city name Crexi does not use).
- `none-matching-filters`: Crexi has listings here, but none matches your filters.
- `watch-started`: the first check of a watch saved its starting point.
- `no-new-listings`: watch mode found nothing new (this is the one case that carries the small watch-check charge).
- `more-not-returned`, `watch-coverage`: more listings exist than this run returned or checked.
- `budget-reached`: the run hit the maximum total charge you set; nothing past it was charged, and in watch mode a later run returns it.
- `bad-input`, `unreadable`, `blocked`, `not-allowed-by-robots-txt`: the input could not be used, or Crexi could not be read. Nothing is guessed.

### FAQ

**Is there a Crexi API for listings?**
Crexi does not sell a public listings API. This Actor reads the same public search data the Crexi website shows to visitors who are not signed in, and gives it to you as rows you can download or send to other tools through the Apify API.

**How do I get an alert when a new commercial property is listed on Crexi?**
Turn on `onlyNew`, set your locations and filters, and add a schedule in Apify (Schedules, then pick this task). Connect the dataset to Slack, email, Zapier or Make to get each new listing as a message.

**Can I export Crexi listings to Excel or Google Sheets?**
Yes. Every run's dataset downloads as CSV, Excel or JSON, and the Google Sheets integration can append each run's rows to a sheet.

**Does it get broker contact details?**
It returns the brokerage firm name for each listing. It does not collect broker names, phone numbers or e-mails, and phone numbers or e-mails written in a listing summary are replaced with `[phone removed]` / `[email removed]`.

### Good to know

- This is an unofficial tool, not made by or connected to Crexi (Commercial Real Estate Exchange, Inc.). It reads only public listing information that Crexi shows to signed-out visitors.
- Cap rate, NOI, building size and year built come from what the listing states, so they are `null` when the listing does not give them.
- Watch mode reads the listings updated most recently on Crexi, up to 600 per check, and stops when it reaches listings it already compared at the last check. If more than that changed between two checks you get a `watch-coverage` row; run the watch more often or narrow it.
- Price drops are tracked for properties for sale. Lease watches return new spaces for lease.
- City names are matched as Crexi writes them (case does not matter). If a city returns `no-results`, check the spelling on crexi.com or search the whole state.
- Up to 20 locations and 1,000 listings per location per run.

### Thanks for using this Actor

We build these tools for people who run them every day, and we improve them from what users tell us.

- **Missing a field, or need another filter?** Tell us in the **Issues** tab. If the data is there, we add it.
- **Found a bug or a wrong value?** Post the run ID in the **Issues** tab. Wrong data is the thing we fix first.

If this Actor saved you time, a short review helps other people find it.

# Actor input Schema

## `locations` (type: `array`):

One per line: a US state (TX or Texas) or a city with its state (Houston, TX). Each line is one search. Up to 20 per run. If empty, Houston, TX is searched.

## `listingType` (type: `string`):

Which Crexi listings to search: properties for sale or spaces for lease. Empty means for sale.

## `propertyTypes` (type: `array`):

Optional. Only these property types, as Crexi classifies them (a listing with several types matches if any of them is chosen). Empty means all types.

## `minPrice` (type: `integer`):

Optional, for sale only. Only properties with an asking price at or above this amount in US dollars. Unpriced listings are left out when this is set. Empty means no minimum.

## `maxPrice` (type: `integer`):

Optional, for sale only. Only properties with an asking price at or below this amount in US dollars. Unpriced listings are left out when this is set. Empty means no maximum.

## `minCapRate` (type: `number`):

Optional, for sale only. Only properties whose listed cap rate is at or above this percentage, for example 6.5. Listings without a cap rate are left out when this is set. Empty means any.

## `maxResultsPerSearch` (type: `integer`):

How many listings to return for each location, newest on Crexi first (1 to 1000). Empty means 50. In watch mode this caps what one run returns; the rest comes in later runs.

## `onlyNew` (type: `boolean`):

Run the same input on a schedule. The first run only saves the current listings as the starting point (nothing returned, nothing charged); later runs return listings that appeared on Crexi since then and price drops on listings the watch has seen. Empty means off (a plain search).

## `includePriceDrops` (type: `boolean`):

In watch mode, also return properties for sale whose asking price went down since the watch saw them, with the previous price. Empty means on.

## `watchName` (type: `string`):

Optional. Keeps separate memories for separate watches of the same location and filters (letters, digits, dot, dash, underscore). Empty means the default watch.

## `resetMonitoringState` (type: `boolean`):

Forget what this watch remembered and treat this run as the first check. Empty means off.

## Actor input object example

```json
{
  "locations": [
    "Houston, TX"
  ],
  "listingType": "sale",
  "maxResultsPerSearch": 50,
  "onlyNew": false,
  "includePriceDrops": true,
  "resetMonitoringState": false
}
```

# Actor output Schema

## `results` (type: `string`):

One row per Crexi commercial property, newest on Crexi first: listing ID, link, title, property type and subtype, status, asking price in USD, price per square foot and per acre, auction starting bid, cap rate, NOI, lease rate per square foot per year, rentable square feet, building and lot size, units, class, year built, tenancy, address, city, state, ZIP, county, coordinates, brokerage, listing and update time, days on market and the first photo. Watch mode marks rows new or price-drop (with the previous price). A location with no listings, nothing new, a check page or a run that hit its maximum charge comes back as a free row that says why.

# 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 = {
    "locations": [
        "Houston, TX"
    ],
    "maxResultsPerSearch": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/crexi-new-listings-monitor").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 = {
    "locations": ["Houston, TX"],
    "maxResultsPerSearch": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/crexi-new-listings-monitor").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 '{
  "locations": [
    "Houston, TX"
  ],
  "maxResultsPerSearch": 50
}' |
apify call neverempty/crexi-new-listings-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/crexi-new-listings-monitor"
        }
    }
}
```

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/hO9iiZlUTUuXYglrL/builds/COgaE6wGe9uT1DbDK/openapi.json
