Commercial Real Estate Cap Rate Scraper avatar

Commercial Real Estate Cap Rate Scraper

Pricing

from $4.00 / 1,000 commercial property extracteds

Go to Apify Store
Commercial Real Estate Cap Rate Scraper

Commercial Real Estate Cap Rate Scraper

Search Crexi and extract accessible commercial property listings with asking prices, published cap rates, NOI, area and broker contacts. Filter by state, property type, price or cap rate and export structured data for investment research.

Pricing

from $4.00 / 1,000 commercial property extracteds

Rating

0.0

(0)

Developer

Arezdev

Arezdev

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share

Find commercial properties, compare published cap rates, and export acquisition-ready financials and broker contacts in one run.

Search Crexi by US state and property type, or supply public commercial listing URLs. Get flat JSON, CSV, Excel, or an API dataset for acquisitions, investment screening, lending, and market research. The Actor uses direct HTTP requests and streams responses without running a browser.

Best for commercial acquisition screening and broker research. Start with the small Crexi sample or a public listing URL. Published values and calculated estimates are distinguished; gated financials and access to every marketplace are not guaranteed.

Ready-to-run example

The Input form includes public example values. A fresh apify run uses the input schema defaults. To explicitly select the small sample, including when input was saved by an earlier local run, use:

apify run --input-file .actor/input.example.json

You can also paste .actor/input.example.json into the JSON input editor in Apify Console. Replace the example sources with your own targets and increase the limits when you are ready.

How to use

  1. Select states and property types, or paste direct listing URLs.
  2. Optionally set a minimum cap rate or maximum asking price.
  3. Set the maximum number of records and click Start.
  4. Open the dataset to review results or export JSON, CSV, or Excel.

The Console prefill searches Texas industrial listings and saves up to three rows. The input defaults are Texas, Industrial and Retail, and 30 rows. Free Apify accounts can save at most 25 rows per run. These are live listings; availability and response times depend on the source.

Search example:

{
"states": ["TX"],
"propertyTypes": ["Industrial", "Retail"],
"minCapRate": 6,
"maxAskingPrice": 5000000,
"maxItems": 30,
"timeoutSecs": 15
}

Direct URL example:

{
"directListingUrls": [
"https://www.crexi.com/properties/2658948/texas-932-s-cesar-chavez-rd"
],
"maxItems": 3
}

Providing directListingUrls selects direct mode. State and property-type search settings then do not restrict those URLs; financial filters still apply. Each direct URL must identify one listing. Arbitrary public commercial listing pages, including broker sites and LoopNet pages, are supported when they expose server-rendered listing information, Schema.org data, OpenGraph values, or attribute tables. Sources requiring JavaScript rendering, authentication, or protected financial data may return an ERROR row or unavailable fields.

What data can you extract?

FieldsMeaning
platform, assetId, statusSource, source asset identifier, and SUCCESS or ERROR
propertyName, propertyTypeListing name and published property categories
askingPrice, capRatePercentAsking price in USD and published cap rate in percentage points
estimatedAnnualNoiPublished annual NOI when available, otherwise an estimate from price and cap rate
squareFootage, pricePerSqFtPublished building area and published or calculated price per square foot
lotSizeAcres, yearBuiltPublished acreage and construction year
occupancyPercent, tenancyTypeOccupancy percentage and published tenancy
streetAddress, city, state, zipCodeProperty address; ZIP codes remain strings
brokerName, brokerCompany, brokerPhoneFirst public broker contact, when available
listingUrl, scrapedAt, errorMessageListing link, UTC collection timestamp, and safe failure information

Every row contains these exact fields. Unavailable, masked, ambiguous, or invalid source values remain null. Contacts and property details may be incomplete because the source limits public access. Values reflect what the source publishes; the Actor cannot verify the accuracy of listing information.

The Actor calculates estimatedAnnualNoi = Math.round(askingPrice * (capRatePercent / 100)) only when no published NOI is available. It calculates price per square foot only when the source does not publish it and a positive building area is available. The calculated value is rounded to two decimals and stored in pricePerSqFt, in the flat output schema. It does not derive a cap rate from NOI or infer masked financial values. Zero asking prices and zero cap rates remain valid values; zero building area never causes division by zero.

Input settings

SettingDefaultDetails
states["TX"]Two-letter US state or territory codes; search mode only
propertyTypes["Industrial", "Retail"]Crexi categories, including Industrial, Retail, Multifamily, Office, Land, and Mixed Use
minCapRateUnsetNumber from 0 to 25; excludes listings with unknown or lower cap rates
maxAskingPriceUnsetNonnegative USD price ceiling; excludes listings with unknown asking prices
directListingUrlsUnsetUp to 2,000 public HTTP/HTTPS listing URLs; switches to direct mode
maxItems30Total saved rows, including errors; range 1–2,000; Console prefill is 3
timeoutSecs15Request deadline including redirects and retries; range 3–60 seconds
maxConcurrency25Requested property concurrency, range 1–100; automatically reduced for available memory
proxyConfigurationUnsetOptional Apify or custom proxy settings

Crexi search uses POST https://api.crexi.com/assets/search with 60-item pages, offsets, state/type filters, financial filters, and the required includeUnpriced flag. Public details and broker endpoints enrich results. Duplicate assets and repeated search pages are detected. Filtered-out listings do not count toward maxItems; actual saved SUCCESS and ERROR rows do.

Connections start directly. On HTTP 403, 429, or 503 the Actor tries the available SHADER datacenter proxy group and then the account's default datacenter proxy. A configured or residential Apify proxy is attempted only when useApifyProxy is explicitly true. Custom proxyUrls are used exclusively; useApifyProxy: false without custom URLs forces direct connections. Proxy availability depends on the account and injected proxy credentials. No user-profile API is called, making the Actor suitable for LIMITED_PERMISSIONS.

Output example

Illustrative successful row:

{
"platform": "Crexi",
"assetId": "1234567",
"status": "SUCCESS",
"propertyName": "Example Industrial Center",
"propertyType": "Industrial",
"askingPrice": 2500000,
"capRatePercent": 7.5,
"estimatedAnnualNoi": 187500,
"squareFootage": 20000,
"pricePerSqFt": 125,
"lotSizeAcres": 2.5,
"yearBuilt": 2001,
"occupancyPercent": 95,
"tenancyType": "Multi Tenant",
"streetAddress": "100 Commerce Drive",
"city": "Dallas",
"state": "TX",
"zipCode": "75201",
"brokerName": "Jordan Example",
"brokerCompany": "Example Commercial",
"brokerPhone": "+1 (214) 555-0100",
"listingUrl": "https://www.crexi.com/properties/1234567/texas-example-industrial-center",
"scrapedAt": "2026-10-19T12:00:00.000Z",
"errorMessage": null
}

Failed targets preserve the same flat schema, with status: "ERROR", unavailable fields set to null, the attempted URL, and a safe message such as [example.com] HTTP 403 [HTTP_403]. A failed detail or broker request does not discard valid search data. If the search request itself fails, the Actor saves one uncharged ERROR row identifying the search endpoint and stops gracefully.

Use the results in Clay, n8n, Make, or your API

  • Clay: Import the dataset CSV, map listingUrl as the property key, and use address and broker fields in your acquisition workflow.
  • n8n: Run the Actor with an Apify node, wait for completion, and fetch dataset items. Route SUCCESS rows into your CRM and ERROR rows into a retry workflow.
  • Make: Use Apify's Run an Actor and Get Dataset Items modules to schedule searches and synchronize selected properties.
  • API: Start the Actor using Apify's run API, read defaultDatasetId from the run, and fetch /v2/datasets/{datasetId}/items?format=json&clean=true. Authenticate through your integration's secret store. Use webhooks or polling to wait for run completion.

See the official Actor run API and dataset items API for integration details.

Pricing and limits

The custom pay-per-event event is property-scraped. Only saved SUCCESS rows use that event. ERROR rows cost $0 in event fees and still count toward the saved-row limit. Compute, storage, and authorized proxy usage may have separate Apify costs.

Concrete example: if the Store price is $2 per 1,000 successful properties ($0.002 per event), 1,000 successful properties cost $2 in custom event fees. A run saving 900 successes and 100 errors costs $1.80 in custom event fees at that rate. This is an illustration; the current Store price controls actual charges.

Free Apify accounts are capped at 25 total saved rows per run. Paid accounts and local runs support the requested maxItems, up to 2,000. When the event budget is exhausted, new work stops, already buffered eligible results are saved, and the run ends cleanly. Larger searches can finish with fewer items when the source runs out of listings or financial filters exclude unavailable values.

For deployment, configure the property-scraped event and its price in the Apify Console monetization settings. Keep automatic default-dataset-item event pricing disabled so error rows stay free of event charges; the Actor rejects that incompatible pricing setup with a clear status. Local repository files do not activate Store monetization. See Apify pay-per-event documentation.

FAQ and troubleshooting

Why are cap rates, NOI, year built, or phone numbers missing?

Crexi and other sources may mask those fields, omit them from public endpoints, or require account access. The Actor returns null and never attempts to reconstruct protected values. A cap rate explicitly published in a listing description can still be extracted. Public asking price and cap rate can supply an estimated NOI.

Why are there fewer results after setting a financial filter?

Unknown values cannot satisfy a financial filter. State and type filters are rechecked on search results, and financial filters are checked on both search and direct results. A successful fetch can therefore produce no saved row when its data does not meet the filters.

What happens on HTTP blocks, malformed responses, or timeouts?

The Actor applies the permitted proxy cascade and protocol fallback. Unreachable targets become uncharged ERROR rows. Valid search data remains usable if enrichment fails. The run exits cleanly on individual source failures; invalid user input exits with code 1. A successful run status alone does not imply that every row succeeded; inspect status in the dataset.

Can partially downloaded pages still succeed?

Yes, if a successful HTTP response has already supplied at least 16 KiB of HTML before a timeout or socket reset and the prefix contains usable listing information. HTML responses are capped at 4 MiB after decompression; oversized HTML can also use its parsed prefix. JSON is capped at 2 MiB and is never salvaged partially. The internal client tracks truncation without adding fields to the specified dataset schema.

How are requests protected?

All target and redirect URLs use public HTTP/HTTPS endpoints on default ports. Every DNS answer must be public. Validated addresses are pinned while preserving the target hostname and TLS certificate checks. Proxy tunnels also use pinned destination addresses. Local networks, metadata services, unsafe protocols, URL credentials, HTTPS downgrades, and excessive redirects are blocked. Parsers limit nesting and text windows, ignore external entities, and remove prototype-pollution keys. Dataset strings are protected against CSV formulas while valid phone numbers remain readable.

How do I build and test locally?

Use Node.js 22 or later:

npm ci --ignore-scripts
npm run build
npm test
npm run smoke
npm start

npm test uses node:test with mocked networking and covers SSRF, DNS pinning, redirects, proxy policy, byte limits, interrupted streams, parser accuracy, billing, free-plan caps, schemas, and pagination. npm run smoke performs a real direct Texas industrial search for three items, prints timings and memory usage, and does not initialize Apify billing or storage. npm start runs the complete Actor and uses the standard local Apify storage input. No environment file is loaded by the repository scripts.

The Docker image uses a multi-stage node:22-alpine build and runs as a non-root user with 256 MiB as the default Apify allocation. UV_THREADPOOL_SIZE=64 and production mode are set before the application and test child processes start. Result writes are serialized, grouped into batches of 100 or flushed every five seconds. SIGINT, SIGTERM, and Apify abort events cancel pending requests and flush accepted rows.

Repository initialized by Arezgit.