Crexi Scraper - Commercial Property Listings for Sale
Pricing
from $1.00 / 1,000 listing results
Crexi Scraper - Commercial Property Listings for Sale
Scrape crexi.com commercial listings: asking price, cap rate, square footage, units, year built, lot size, APN, zoning, address, coordinates, status, photos plus agent and brokerage contacts and 100+ fields. Search by city, ZIP or keyword with type, price and sort filters, or paste URLs.
Pricing
from $1.00 / 1,000 listing results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
1
Bookmarked
7
Total users
5
Monthly active users
7 days ago
Last modified
Categories
Share
Crexi Scraper: Commercial Property Listings, Pricing & Broker Contacts
Crexi Scraper turns Crexi into structured commercial real estate data you can pull on demand. Search any city, ZIP or keyword with property type, price and sort filters, or paste listing URLs directly, and get a clean JSON record per listing: asking price, cap rate, square footage, units, year built, lot size, APN, zoning, full address with coordinates, status, the complete photo gallery, and the listing agent and brokerage contact details. Resume an interrupted crawl, or turn on Incremental mode for recurring monitoring, then export to JSON, CSV or Excel, or pull the results straight into your app through the API.
Why This Scraper?
- Two ways to start. Search by location, property type and price filters, or paste crexi.com listing URLs directly. No need to build search queries by hand.
- Deep per-listing detail. Square footage, units, year built, stories, lot size, cap rate, net operating income, APN, zoning and the full marketing description.
- Agent and brokerage contact cluster on every listing, including license numbers and the public profile id.
- The complete photo gallery, not just the thumbnail.
- Filter and sort control. Property type, asking-price band, priced-only, and four sort orders in either direction.
- Resume or monitor. Resume an interrupted crawl with a pasted run id, or turn on Incremental mode for recurring runs that return only what is new or changed.
- Optional export into the apps you already use, via MCP connectors, without ever changing the dataset.
Use Cases
- Commercial real estate investors: track new listings and price or cap-rate changes across markets to spot deals early.
- Brokerages and appraisers: build comparable-listing and market datasets by property type and location.
- PropTech and data teams: feed listing data into internal tools, dashboards or CRMs through the API or MCP connectors.
- Market researchers: monitor inventory levels, pricing and days-on-market trends over time with Incremental mode.
- Lead generation: collect agent and brokerage contact details tied to currently active listings.
Data You Get
Sample shape: values are illustrative placeholders, not from a live listing.
| Field | Example |
|---|---|
id | 1000000 |
name | "Maple Court Apartments" |
property_type | "Multifamily" |
status | "Active" |
askingPrice | 4250000 |
capRate | 6.25 |
squareFootage | 48000 |
specifications.units | 64 |
yearBuilt | 1998 |
stories | 3 |
lotSize | 2.4 |
netOperatingIncome | 265000 |
apn | "000-000-000" |
zoning | "R-4" |
fullAddress | "100 Main St, Austin, Travis County, TX 78701" |
city / state | "Austin" / "TX" |
latitude / longitude | 30.2672 / -97.7431 |
url | Crexi link to the listing |
images | full gallery image URLs (when Fetch full details is on) |
brokerageName | "Example Commercial Realty" |
agent_info.name | "Jane Doe" |
agent_info.phone | "+10000000000" |
Each listing also carries subtypes, marketingDescription, licenseNumbers, numberOfAssets, createdOn, activatedOn, updatedOn, detailFetched and a set of boolean flags such as isSold and isPrivate; detail-only fields stay empty when Fetch full details is off, since only the search card is returned in that case.
How to Use
- Pick a mode:
search(build from your locations and filters) orurl(paste crexi.com listing URLs). - For search mode, set Locations or keywords and any of property types, price band or sort order.
- Turn on Fetch full details for square footage, cap rate, gallery and agent contact, and set Max items to control run size and cost.
- Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.
Search by location and property type:
{"mode": "search","locations": ["Austin, TX"],"propertyTypes": ["Multifamily"],"maxItems": 20,"fetchDetails": true}
Search a price band, priced listings only, sorted by cap rate:
{"mode": "search","locations": ["Dallas, TX"],"priceMin": 1000000,"priceMax": 25000000,"includeUnpriced": false,"sortBy": "capRate","sortDirection": "Descending","maxItems": 50}
Paste listing URLs directly:
{"mode": "url","urls": ["https://www.crexi.com/properties/1000000/austin-maple-court-apartments","https://www.crexi.com/properties/1000001/dallas-oak-plaza"],"fetchDetails": true}
Monitor a search on a schedule and get only what changed:
{"mode": "search","locations": ["Austin, TX"],"propertyTypes": ["Multifamily"],"maxItems": 200,"incrementalMode": true}
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/crexi-commercial-listings-scraper").call(run_input={"mode": "search", "locations": ["Austin, TX"], "maxItems": 20})for listing in client.dataset(run["defaultDatasetId"]).iterate_items():print(listing["name"], listing["askingPrice"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/crexi-commercial-listings-scraper').call({mode: 'search',locations: ['Austin, TX'],maxItems: 20,});const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
Resume and recurring updates
- Resume (
resumeFromRunId) continues ONE specific interrupted crawl: paste a previous run id or dataset id and the actor skips every listing already collected there, so a large pull that got cut off can finish without duplicating or re-charging what it already returned. - Incremental mode (
incrementalMode) is for running the SAME search again and again, for example daily. The actor remembers what it saw itself, in its own storage, so there is no id to paste. The first run returns everything asNEW. Later runs returnNEWandUPDATEDlistings;UNCHANGEDlistings are suppressed and not billed unlessemitUnchangedis on. Each returned record also getschangeType,changedFields,firstSeenAtandlastSeenAt. Turning onemitExpiredalso returns, and bills, listings no longer found, markedEXPIRED, but only after a run that fully scanned the tracked search; a run capped by Max items, or combined with Resume, never producesEXPIREDrows.REAPPEAREDis only produced for a listing that was previously markedEXPIREDthis way and is then found again on a later run; a search that has never usedemitExpiredwill never see aREAPPEAREDrow, since nothing was ever marked gone in the first place.stateKeynames or shares the stored state; leave it empty and the actor derives one automatically from the search or URL and detail settings, so different searches never mix state with each other. Turning on Incremental mode together with Resume on a search that already has saved state fails the run immediately, before anything is collected or charged. With Incremental mode off, output is exactly as before.
Send results into your apps (MCP connectors)
Optionally send the scraped listings into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape, and the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each listing, not the full JSON. Each listing becomes one entry with a title and its key fields, asking price, property type, square footage, cap rate, year built, address, status, brokerage, agent and URL, flattened to plain text, plus the opening of the marketing description. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify, Settings, API & Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. If the picker is empty, you have not authorized a connector yet.
- For Notion, also set
notionParentPageUrlto the page where listing pages should be created; Notion gets a full page per listing, other connectors get a best-effort write or digest.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search builds searches from your locations and filters, url loads the listing URLs you paste. |
locations | array | [] (prefill: ["Austin, TX"]) | Locations or keywords to search, for example Austin, TX or 90001. Each entry is its own search. Empty searches all listings nationwide. Search mode only. |
propertyTypes | array | none | Property types to narrow the search, for example Multifamily, Office, Retail, Industrial, Land. Search mode only. |
priceMin | integer | none | Only return listings with an asking price at or above this amount. |
priceMax | integer | none | Only return listings with an asking price at or below this amount. |
includeUnpriced | boolean | true | Include listings without a published asking price (auctions, call-for-offers). |
sortBy | string | rank | rank (best match), price, activatedOn (newest), or capRate. |
sortDirection | string | Descending | Descending or Ascending. |
urls | array | none (prefill: one example crexi.com listing URL) | crexi.com listing detail URLs to load. URL mode only; filter fields are ignored. |
resumeFromRunId | string | none | Paste a previous run id or dataset id to continue a large crawl without returning or re-charging for listings already collected there. For recurring monitoring of the same search, use Incremental mode instead. |
incrementalMode | boolean | false | Turn on for recurring monitoring. Returns only NEW/UPDATED listings (plus REAPPEARED where it applies) after the first run; state is remembered by the actor itself. |
stateKey | string | none | Name a monitoring campaign to keep its state stable, or deliberately share state across differently-configured runs. Leave empty to derive a key automatically from the search or URL and detail settings. Incremental mode only. |
emitUnchanged | boolean | false | Also return, and bill, listings unchanged since the last run, marked UNCHANGED. Incremental mode only. |
emitExpired | boolean | false | Also return, and bill, listings no longer found, marked EXPIRED. Only produced after a run that fully scanned the tracked search, not when Max items capped it or Resume was used. Incremental mode only. |
fetchDetails | boolean | true | Load each listing page for square footage, units, year built, stories, lot size, cap rate, APN, marketing description, the full image gallery and agent and brokerage contact details. Adds a small per-item charge. |
maxItems | integer | 20 | The single cap on the run: stop after this many listings, across all searches and URLs. 0 = unlimited. |
maxPages | integer | none (unlimited; defers to Max items) | Optional safety bound on result pages per search. Left empty or 0, it does not cap the run; the run stops at Max items. |
proxy | object | {"useApifyProxy": true} | Connection settings. The default tier is the lowest-cost option and works on every plan. |
maxResidentialMB | integer | 0 | When using a Residential connection group, cap residential traffic at this many MB, then automatically switch to the lower-cost tier. 0 = unlimited. |
mcpConnectors | array | none | Optional MCP connectors to pipe results into (Notion, Linear, Airtable, Apify). Never changes the dataset. |
notionParentPageUrl | string | none | Notion page URL or id under which listing pages are created. Notion connector only. |
maxNotifyListings | integer | 50 | Cap on items written to each connector per run. Does not affect the dataset. |
Output Example
Sample shape: values are illustrative placeholders, not from a live listing.
{"id": 1000000,"name": "Maple Court Apartments","url": "https://www.crexi.com/properties/1000000/austin-maple-court-apartments","property_type": "Multifamily","subtypes": ["Apartment Building"],"status": "Active","askingPrice": 4250000,"capRate": 6.25,"squareFootage": 48000,"specifications": { "units": 64, "year_built": 1998 },"yearBuilt": 1998,"stories": 3,"lotSize": 2.4,"netOperatingIncome": 265000,"apn": "000-000-000","zoning": "R-4","marketingDescription": "Well-maintained 64-unit garden-style community in central Austin.","fullAddress": "100 Main St, Austin, Travis County, TX 78701","city": "Austin","state": "TX","zip": "78701","latitude": 30.2672,"longitude": -97.7431,"images": ["https://images.crexi.com/assets/1000000/placeholder_3000x2000_resize.jpg"],"brokerageName": "Example Commercial Realty","agent_info": { "name": "Jane Doe", "company": "Example Commercial Realty", "phone": "+10000000000" },"firstName": "Jane","lastName": "Doe","publicProfileId": "janedoe","licenseNumbers": ["TX 000000"],"numberOfAssets": 12,"scrapedAt": "2026-01-01T00:00:00.000Z"}
Plan Requirement
This actor runs on any Apify plan. The default connection tier is the lowest-cost option and returns the same data, and the actor automatically tries a different connection when one is refused. A residential connection group is available if you prefer it: pick it under Connection and optionally set a residential traffic budget, and the run automatically switches back to the lower-cost tier once that budget is used.
FAQ
How much does it cost?
You pay per listing returned, with the optional detail fetch (square footage, cap rate, gallery, agent contact) billed only when it is switched on, and only for listings that are actually returned. The Pricing tab shows the current rates. Use Max items to cap the cost of any run.
Is it legal to scrape Crexi?
This actor collects only publicly available commercial listing data. You are responsible for how you use it: follow Crexi's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Listing facts such as price and square footage are generally not protected, but photos, marketing descriptions and agent details may be subject to third-party rights.
Can I get only new or changed listings on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only NEW and UPDATED listings by default, and unchanged ones are not billed unless you turn on emitUnchanged. A listing only comes back marked REAPPEARED if it was previously marked EXPIRED by a run that had emitExpired on and completed a full scan of the tracked search; if you never turn on emitExpired, a listing that drops out and later comes back is simply classified UPDATED or UNCHANGED like any other rescrape, never REAPPEARED.
What is the difference between Resume and Incremental mode?
Resume from a previous run continues ONE specific interrupted crawl using a pasted run or dataset id, and does not remember anything between separate scheduled runs. Incremental mode is for running the SAME search again and again, for example daily; the actor remembers what it saw itself, so there is no id to paste. Using both together on a search that already has saved incremental state fails the run immediately rather than mixing the two.
Why did my run fail instead of returning an empty dataset?
If every request was refused before any data could be read, the run fails with a clear connection-problem message, so "no listings today" is never confused with "nothing could be read". Check the connection warnings in the run log and try again. A genuinely empty search, where requests went through but the query just has no matches, succeeds with zero rows and a status message suggesting a broader location or different property types. An invalid Resume id also fails the run immediately, before anything is collected or charged.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push a summary of each listing into Notion, Linear or Airtable once the run finishes.
๐ Want more real estate data?
Pair this actor with these related scrapers from the same team:
| ๐ Redfin Scraper Scrape redfin.com home listings: price, beds, baths, sqft, lot, year built, coordinates... | ๐ Apartments.com Scraper From $1.50/1K. Scrape Apartments.com rentals by city, ZIP, neighborhood, address or URL... |
| ๐ Zumper Scrape Zumper.com rental listings: apartments, houses, condos and rooms. Pulls 60+ fields... | ๐ Trulia Scraper Scrape Trulia.com property listings by location, filters, or URL. Extract prices, beds... |
| ๐ BizQuest Scraper From $1/1K. Extract business-for-sale, franchise, and asset listings from BizQuest... | ๐จ LoopNet From $1.2/1K. Scrape LoopNet across the US including office, retail, industrial... |
๐ Browse all abotapi scrapers
๐ฌ Support & custom scrapers
- ๐ Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- ๐ ๏ธ Need another site, extra fields or a private build? Email abotapi@proton.me or message Telegram @abotapi.
- โญ Enjoying it? A quick review on the actor page helps other users find it.