# LoopNet Scraper — Commercial Listings for Sale & Lease (`nice_dev/loopnet-listings-scraper`) Actor

Scrape LoopNet commercial real estate for sale or lease: address, GPS, price or rent, sizes, year built, class, cap rate, brokers, plus optional broker phone, email, owner and Walk Scores. Search by city, state and property type, watch new listings or paste listing IDs. Export to JSON, CSV or Excel.

- **URL**: https://apify.com/nice_dev/loopnet-listings-scraper.md
- **Developed by:** [Nice Dev](https://apify.com/nice_dev) (community)
- **Categories:** Real estate, Lead generation, MCP servers
- **Features:** Limited permissions
- **Stats:** 1 total users, 1 monthly users, 98.2% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.42 / 1,000 listings

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

### 🏢 What is LoopNet Scraper?

**LoopNet Scraper** extracts **commercial real estate listings from [LoopNet](https://www.loopnet.com)** — for sale and for lease — and, as options, **each broker's phone number and e-mail address**, the **owner or manager of the property** and its **Walk, Transit, Bike and Drive scores**. Every listing comes with its address, GPS position and price or rent as LoopNet shows it; sizes, year built, building class and cap rate when the listing states them. Built for brokers, investors, lenders and data teams who need LoopNet leads and comps in a spreadsheet.

Type a **city and state** (`Austin, TX`) and pick the **property types**, or paste LoopNet listing IDs or URLs, click **Start**, and download the listings in JSON, CSV or Excel. No login, nothing to set up, no limit of 750 results per search. Measured on Apify: 1,000 listings with the brokers' phone and e-mail, the owner and the scores in about 7 minutes; 1,000 listings without options in about 2.5 minutes; the 200 newest listings of a state in under 30 seconds.

### 📋 What data can you extract from LoopNet?

One item per listing, 86 fields (22 of them come from LoopNet's full listing page and stay empty for now, see **Known limitations**):

| Category | What you get |
| --- | --- |
| 🏷️ **Listing** | title, building name, link, LoopNet id, sale / lease, property type and subtype — `For Lease`, `Office`, `Medical` |
| 📍 **Location** | full address, street, city, state, ZIP code, country, latitude and longitude — `Austin, TX 78705` |
| 💰 **Price** | asking price (sale), price or rent as shown, rent per SF per year (lowest and highest), price per SF, cap rate, deal score and its reasons, currency — `$46.34 /SF/YR`, `5.73%` |
| 🏗️ **Building** | space available, number of spaces, smallest and largest space, building size, lot size, year built, building class — `725 SF`, `Class B`, `1920` |
| 📞 **Brokers** | every broker of the listing with company, city, **phone and e-mail** (option) — `512-555-0142` |
| 🏠 **Owner** | owner or manager of the property: name, website, description (option; rare: about 1 listing in 700, measured November 2026) |
| 🚶 **Scores** | Walk, Transit, Bike and Drive scores, 0-100 (option) |
| 📝 **Text and photo** | short description, highlights, photo link |
| 🕒 **Dates** | day LoopNet last changed the listing |

Every field, with an example, is listed in the **Output** section below.

### ✅ Why use LoopNet Scraper?

- 📞 **Broker phone AND e-mail** as an option, for every listing that has one, one lookup per broker for the whole run — not just a name.
- 🏠 **Owner or property manager** and **location scores** as options too, charged only on the listings that get them.
- 🚫 **No 750-result limit**: a whole state, or the whole of LoopNet, in one search — newest changes first.
- 🗂️ **Several searches in one run**: cities × property types, with a cap per search so that one big market cannot eat the whole budget.
- 🆕 **New listings of the last 30 days** as a source of its own, newest first — made for scheduled runs.
- 🎯 **Filters**: price, building size, space size for lease, property subtype, cap rate, year built, building class, lot size, rent per SF, number of units, listing tags, keywords, with a broker phone only.
- 🔔 **Monitoring and price drop alerts built in**: tick **Only new listings**, schedule the Actor, and each run returns (and charges) only what it has never delivered before. Tick **Track price changes** too, and the listings whose price moved come back with the old price and the change in %, at no extra cost.
- 🔌 API, scheduling, integrations (Make, Zapier, n8n, Google Sheets…), JSON/CSV/Excel export via the Apify platform.

### 🚀 How to scrape LoopNet

1. Create a free Apify account.
2. Open **LoopNet Scraper**, choose **For sale** or **For lease**, type a **Location** (`Austin, TX`, `TX`, `Toronto, ON`, `London, United Kingdom`) and pick the **Property types** (empty = all commercial real estate).
3. Or list **Listing IDs**, or paste LoopNet listing or search URLs into **Start URLs**.
4. Tick **Broker phone and e-mail**, **Owner or property manager** and **Walk, Transit, Bike and Drive scores** if you need them (all three are off by default).
5. Set **Max listings** (100 by default, 0 = no limit) — and **Max listings per search** when you run several searches — then click **Start**.
6. Download the dataset in JSON, CSV, Excel or via API.

### 💰 How much does it cost to scrape LoopNet?

This Actor uses **pay per event** pricing: **$0.45 per 1,000 listings** without a subscription, $0.44 on Bronze, $0.43 on Silver and **$0.42 on Gold and above**; the options, per 1,000 listings that get them: broker phone and e-mail **$0.89** ($0.83 on Gold), owner or manager **$2.49** ($2.34 on Gold), Walk / Transit / Bike / Drive scores **$0.047** ($0.017 on Gold); **$0.02 per run start**; a listing a filter drops **$0.04 per 1,000** — see the **Pricing** tab. **From November 20, 2026** the options get cheaper: broker phone and e-mail **$0.45** ($0.21 on Gold), owner or manager **$2.29** ($2.26 on Gold). Example: 1,000 office listings with their brokers' phone and e-mail ticked cost $0.45 + $0.89 + $0.02 = $1.36 without a subscription ($0.92 from November 20). An option you ticked but that a listing does not have (no owner shown, no scores for its place) is not charged. Platform usage (compute, proxy) is included in the price.&#x20;

### ⚙️ Input

```json
{
    "listingType": "for-lease",
    "location": "Austin, TX",
    "propertyTypes": ["office", "flex"],
    "includeBrokerContact": true,
    "maxItems": 200
}
```

Several markets, a cap per search, filters, owner and scores, only the listings not delivered before:

```json
{
    "listingType": "for-lease",
    "propertyTypes": ["office"],
    "locations": ["Dallas, TX", "Houston, TX", "Austin, TX"],
    "maxItemsPerQuery": 100,
    "minRentPerSfYear": 20,
    "buildingClasses": ["A", "B"],
    "includeOwner": true,
    "includeScores": true,
    "onlyNew": true,
    "stateKey": "texas-office-lease"
}
```

The listings LoopNet added in the last 30 days in two states, investment sales only:

```json
{
    "source": "newListings",
    "listingType": "for-sale",
    "locations": ["TX", "FL"],
    "minCapRate": 6,
    "maxItems": 500
}
```

Or with your own IDs and URLs:

```json
{
    "listingIds": ["41920405"],
    "startUrls": [{ "url": "https://www.loopnet.com/search/retail-space/miami-fl/for-lease/" }],
    "maxItems": 500
}
```

| Field | Notes |
| --- | --- |
| `listingIds`, `startUrls` | LoopNet listing IDs (the number at the end of a listing URL, `41920405`) and URLs; a pasted search URL (https://www.loopnet.com/search/office-space/austin-tx/for-lease/) is redone with its place, property type and deal. They replace the search fields below. |
| `source` | `search` (default) = every listing of each location × property type, newest change first; `newListings` = the listings LoopNet added in the last 30 days. |
| `listingType` | `for-sale` (default) or `for-lease`; `auctions` with the clientProxy access only. |
| `propertyTypes` | `office`, `retail`, `industrial`, `flex`, `land`, `multifamily`, `hospitality`, `healthCare`, `restaurant`, `specialPurpose`, `coworking`; empty = all. One search per type. LoopNet has no lease search for `multifamily`, `hospitality` and `specialPurpose`, and `coworking` is for lease only. |
| `location`, `locations` | `Austin, TX`, `TX`, `Texas`, `Toronto, ON`, `London, United Kingdom`, `USA`, `Canada`, or a city alone (`Austin`: every state); `locations` adds more places (max 500 searches per run). Empty = everywhere. ZIP codes and counties are not supported. |
| `includeBrokerContact` | Each broker's phone and e-mail (default off, charged as the broker-contact event; `requirePhone` switches it on). |
| `includeOwner` | Owner or manager of the property: name, website, description — rare (about 1 listing in 700, measured November 2026); charged as the owner-profile event only when there is one. |
| `includeScores` | Walk, Transit, Bike and Drive scores (charged as the location-scores event). |
| `includeLimitedListings` | Also save the listings LoopNet publishes with almost nothing (no broker, price and size on request); skipped by default, never charged. |
| `maxItems` | Stop after this many listings for the whole run (`0` = unlimited). |
| `maxItemsPerQuery` | Cap for EACH search (location × property type, or search URL); the listing IDs count as one search. `0` = no per-search cap. |
| `minPrice`, `maxPrice`, `minBuildingSize`, `maxBuildingSize`, `minCapRate`, `maxCapRate`, `minYearBuilt`, `maxYearBuilt`, `minLotSizeAcres`, `maxLotSizeAcres`, `minRentPerSfYear`, `maxRentPerSfYear`, `minUnitCount`, `maxUnitCount` | Ranges (price in US dollars, sizes in SF, lot in acres, cap rate in %). A price LoopNet shows rounded ($1.7M) counts as 1,700,000. A listing without the value is dropped once a bound is set. |
| `minSpaceSize`, `maxSpaceSize` | Size in SF of a space to lease: a listing is kept when the size range of its spaces touches yours. A listing without a space size, so every sale, is dropped. |
| `propertySubtypes` | Keep only the listings whose property subtype contains one of these texts (`Warehouse`, `Medical`, `Strip Center`…; case and accents ignored). A listing without a subtype is dropped. |
| `buildingClasses` | `A`, `B`, `C`, `F`; once one is chosen, a listing that states no class (about 2 in 3) is dropped. |
| `listingTags` | Keep only the listings that carry at least one of the tags you pick (`owner-user`, `renovated`, `motivated-seller`, `medical`, `income-producing`, `development-site`, `net-lease`), read from the listing description. |
| `keywords`, `excludeKeywords` | Keep only the listings whose title, highlights or description contain one of `keywords`; drop those whose title, building name, highlights or description contain one of `excludeKeywords` as WHOLE words (case and accents ignored): `art` drops "Art deco", not "Smart building"; several words in one entry (`hand made`) must appear together; Chinese, Japanese, Korean, Thai, Lao, Khmer and Burmese words are found anywhere in the text (no spaces there). |
| `requirePhone` | Keep only the listings with a broker phone number (switches `includeBrokerContact` on: the option is charged). |
| `onlyNew`, `stateKey`, `resetState` | Monitoring: only the listings never delivered under this memory key; `resetState` forgets the memory. |
| `trackPriceChanges` | With `onlyNew`: a listing already delivered comes back, whole, when its asking price (or lowest rent) has moved, with `previousPrice`, `priceChange`, `priceChangePct` and `priceChangedAt`. No extra charge. |
| Advanced | `accessMode` (`standard` by default; `clientProxy` = LoopNet's search pages through your own unblocking proxy, each listing as its search card shows it; no pasted listing, and of the filters only `excludeKeywords`), `proxyConfiguration` (Apify proxy by default, included in the price; the residential proxy is not available), `maxConcurrency`, `maxRequestsPerMinute`, `minRequestIntervalMs`, `maxRequestRetries`, `debugLog`. |

### 📦 Output

A real item (an office for lease, with the broker contact and scores options; the broker's contact replaced by an example):

```json
{
    "id": "42302333",
    "url": "https://www.loopnet.com/Listing/42302333/",
    "title": "1006 W 31st St, Austin, TX 78705",
    "propertyName": null,
    "listingType": "For Lease",
    "propertyType": "Office",
    "propertySubtype": "Office",
    "address": "1006 W 31st St, Austin, TX 78705",
    "street": "1006 W 31st St",
    "city": "Austin",
    "state": "TX",
    "postalCode": "78705",
    "country": "US",
    "latitude": 30.30064,
    "longitude": -97.74664,
    "price": null,
    "priceText": "$46.34 /SF/YR",
    "sizeText": "725 SF",
    "currency": "USD",
    "pricePerSf": null,
    "capRate": null,
    "discountToMarketPct": null,
    "capRateSpread": null,
    "dealScore": null,
    "dealReasons": [],
    "previousPrice": null,
    "priceChange": null,
    "priceChangePct": null,
    "priceChangedAt": null,
    "listingTags": [],
    "saleType": null,
    "saleCondition": null,
    "minRentPerSfYear": 46.34,
    "maxRentPerSfYear": 46.34,
    "spaces": [],
    "spacesCount": 1,
    "unitCount": null,
    "minSpaceSf": 725,
    "maxSpaceSf": 725,
    "totalSpaceAvailableSf": 725,
    "buildingSizeSf": null,
    "typicalFloorSizeSf": null,
    "lotSizeAcres": null,
    "lotSize": null,
    "yearBuilt": 1920,
    "yearRenovated": null,
    "buildingClass": "B",
    "stories": null,
    "tenancy": null,
    "zoning": null,
    "parking": null,
    "parcelNumber": null,
    "landAssessment": null,
    "improvementsAssessment": null,
    "totalAssessment": null,
    "propertyFacts": null,
    "tenants": [],
    "highlights": [],
    "amenities": [],
    "description": "Bright, remodeled and available immediately! Formerly used as an art gallery and an office for tech start-up. Ideal for professional…",
    "brokerName": "John Carter",
    "brokerCompany": "Realty Austin - North",
    "brokerPhone": "512-555-0142",
    "brokerEmail": "john.carter@realty.test",
    "brokerProfileUrl": null,
    "brokers": [
        {
            "name": "John Carter",
            "company": "Realty Austin - North",
            "city": "Austin",
            "region": "TX",
            "profileUrl": null,
            "photoUrl": null,
            "phone": "512-555-0142",
            "email": "john.carter@realty.test"
        }
    ],
    "ownerName": null,
    "ownerWebsite": null,
    "ownerDescription": null,
    "walkScore": 80,
    "transitScore": 50,
    "bikeScore": 80,
    "driveScore": 60,
    "images": ["https://images1.loopnet.com/i2/zYd7-MXqzioDtnzIF22SJdPjTL7wzDhZXLvyoX-8Bt4/110/image.jpg"],
    "imageUrl": "https://images1.loopnet.com/i2/zYd7-MXqzioDtnzIF22SJdPjTL7wzDhZXLvyoX-8Bt4/110/image.jpg",
    "brochures": [],
    "matterportTours": [],
    "videos": [],
    "isAuction": null,
    "isActive": true,
    "publishedAt": null,
    "daysOnMarket": null,
    "updatedAt": "2026-09-24",
    "detailLevel": "summary",
    "searchUrl": "https://www.loopnet.com/search/office-space/austin-tx/for-lease/",
    "scrapedAt": "2026-09-26T07:02:05.240Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV or Excel.

#### All 86 fields

| Fields | What you get |
| --- | --- |
| `id`, `url`, `title`, `propertyName` | `42302333`, the listing link, `1006 W 31st St, Austin, TX 78705`, `Miron Place` |
| `listingType`, `propertyType`, `propertySubtype`, `isAuction`, `isActive` | `For Lease`, `Office`, `Medical`, `null`, `true` |
| `address`, `street`, `city`, `state`, `postalCode`, `country`, `latitude`, `longitude` | `1006 W 31st St, Austin, TX 78705`, `US`, `30.30064`, `-97.74664` |
| `price`, `priceText`, `sizeText`, `currency`, `capRate`, `saleType` | `1800000`, `$46.34 /SF/YR`, `725 SF`, `USD`, `5.73`, `Investment or Owner User` |
| `pricePerSf`, `discountToMarketPct`, `capRateSpread`, `dealScore`, `dealReasons` | `300`, `23.4`, `1.4`, `62`, `Price per SF 23% below the Office median` (sales only, see the FAQ) |
| `previousPrice`, `priceChange`, `priceChangePct`, `priceChangedAt` | `2000000`, `-150000`, `-7.5`, `2026-11-06T09:00:00.000Z` (only with **Track price changes**, on a listing that comes back because its price moved) |
| `unitCount`, `listingTags` | `24`, `["income-producing", "net-lease"]` (housing units of a multifamily listing; keyword tags read from the description, see the FAQ) |
| `minRentPerSfYear`, `maxRentPerSfYear`, `spacesCount`, `minSpaceSf`, `maxSpaceSf`, `totalSpaceAvailableSf` | `46.34`, `1`, `725` |
| `buildingSizeSf`, `lotSizeAcres`, `lotSize`, `yearBuilt`, `buildingClass` | `6000`, `3.58`, `3.58 AC`, `1920`, `B` |
| `brokerName`, `brokerCompany`, `brokerPhone`, `brokerEmail`, `brokers` | every broker with company, city, phone and e-mail (phone and e-mail only with the option **Broker phone and e-mail**) |
| `ownerName`, `ownerWebsite`, `ownerDescription` | owner or manager of the property (option) |
| `walkScore`, `transitScore`, `bikeScore`, `driveScore` | `80`, `50`, `80`, `60` (option) |
| `description`, `highlights`, `images`, `imageUrl` | short description, notes, photo link (not downloaded) |
| `updatedAt`, `detailLevel`, `searchUrl`, `scrapedAt` | `2026-09-24`, `summary`, the LoopNet search of the same place, type and deal, ISO timestamp |
| `saleCondition`, `spaces`, `typicalFloorSizeSf`, `yearRenovated`, `stories`, `tenancy`, `zoning`, `parking` | empty for now: LoopNet's full listing page, not available at the moment |
| `parcelNumber`, `landAssessment`, `improvementsAssessment`, `totalAssessment`, `propertyFacts`, `tenants`, `amenities` | empty for now: LoopNet's full listing page, not available at the moment |
| `brokerProfileUrl`, `publishedAt`, `daysOnMarket` | empty for now: LoopNet's full listing page, not available at the moment |
| `brochures`, `matterportTours`, `videos` | links to the PDF documents (brochure, floor plans), Matterport 3D tours and videos — empty for now: LoopNet's full listing page, not available at the moment |

### 💡 Tips

#### How to get more results

There is no limit per search: a whole state (`TX`) or the whole of LoopNet (empty location) is read, newest change first, until **Max listings**. Split a big run into cities (`locations`) and property types (`propertyTypes`) with `maxItemsPerQuery` to get a bit of every market.

#### How to reduce costs

The price is per listing, so the levers are `maxItems`, `maxItemsPerQuery`, the filters (a filtered-out listing costs only the filter fee, lower than a listing), the options you tick, and `onlyNew` for recurring runs (you never pay twice for the same listing).

#### Several searches in one run

Fill `propertyTypes` and / or `locations`: the Actor runs one search per property type × location (3 types × 4 cities = 12 searches, up to 500 per run). The single `location` field still works and is added to the list. A listing found by several searches is saved — and charged — once. Set `maxItemsPerQuery` to give every search its own cap: without it the first searches can use up the whole `maxItems` budget.

#### Monitoring: only the new listings

Tick **Only new listings** (`onlyNew`) and schedule the Actor — ideally with `source: newListings`. The first run returns everything; each later run skips the listings already delivered: they are not saved and not charged. The memory lives in a named key-value store of your account (`loopnet-listings-scraper-seen`, up to 150,000 listings per key) and is only updated with listings that really reached the dataset, so a failed run never hides anything. Give each schedule its own `stateKey` (two schedules sharing a key would hide each other's listings), and tick `resetState` once to start over. Searches are read newest change first and stop once they meet 125 listings in a row that you already have.

#### Price drop alerts

Tick **Track price changes** (`trackPriceChanges`) together with **Only new listings** and schedule the Actor. Besides the new listings, each run returns, whole, the listings already delivered whose price moved since: `previousPrice` is the price when the listing was last delivered, `priceChange` the difference (negative = cheaper), `priceChangePct` the change in % (a $2,000,000 listing cut to $1,850,000 gives `-7.5`) and `priceChangedAt` when the run saw it. A listing at the same price stays out. There is no extra charge: a listing that comes back is one normal result.

- The price followed is the asking price of a sale, or the lowest asking rent per SF per year of a lease. A listing with its price on request is never reported as changed, and a sale price is never compared with a rent.
- A price change is caught on the listings a run reads: the top of each search, down to where the run stops (125 listings in a row that you already have; a listing whose price moved does not count in that row). The listings pasted in **Listing IDs** are all checked on every run. With `source: newListings`, only the listings added in the last 30 days are followed: keep the default `search` to follow your whole searches.
- With the `clientProxy` access, each search reads up to 750 listings, as without the option; lease cards there show no rent, so rent changes are followed with the standard access only.
- If your memory key already holds listings delivered without the option, the first run with it learns their prices and reports no change; changes show from the next run.

#### Listings with few details

LoopNet publishes some listings with almost nothing — no broker, price and size on request, no map position — often the newest ones. They are skipped by default (not saved, not charged). Tick **Include listings with few details** to get them too.

### 🔌 Integrations and API

Call the Actor via the Apify API, the JavaScript or Python clients, or connect it with integrations and webhooks (Make, Zapier, n8n, Google Sheets, Slack, Airtable…). The dataset can be fetched as JSON or CSV from any tool.

### 🤖 Use with AI agents (MCP)

AI agents (Claude, ChatGPT, Cursor…) can find and run this Actor through the [Apify MCP server](https://mcp.apify.com), billed to their Apify account like any run. It returns one item per LoopNet listing. Actor id: `nice_dev/loopnet-listings-scraper`; MCP server with this Actor only: `https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/loopnet-listings-scraper`.

Smallest input, for a cheap first call:

```json
{
    "listingType": "for-lease",
    "location": "Austin, TX",
    "maxItems": 10
}
```

Key output fields: `url`, `title`, `listingType`, `propertyType`, `address`, `priceText`, `capRate`, `brokerName`, `brokerPhone` and `brokerEmail`.

Cost: pay per event, see the pricing section above. Cap each call with `maxItems` and, through the API, with the run option `maxTotalChargeUsd`.

### ❓ FAQ

#### Is it legal to scrape LoopNet?

The Actor only reads what LoopNet shows publicly to any anonymous visitor. It logs in to nothing and solves no captcha. Broker names, phone numbers and e-mail addresses are professional contact details published by the brokers; they can still be personal data under GDPR or CCPA: do not store them without a legitimate reason, and follow the anti-spam rules of your country (CAN-SPAM, GDPR) before you e-mail anyone. The Actor gives the links of the photos and never copies the photos themselves: they are protected by copyright. You are responsible for using the data in compliance with LoopNet's Terms of Use and applicable law. This Actor is not affiliated with LoopNet or CoStar Group.

#### Does it need a login or a proxy?

No login. The proxy is included in the price: leave the default setting (the residential proxy is not available). A request LoopNet turns away is read again on a new IP; without a proxy, it is retried after a pause of 5 seconds, doubled at each retry up to 150 seconds.

#### Is the data live or from a pre-collected database?

There is no pre-collected database. The listings are read on LoopNet at run time, so you get what the site shows when your run reads it, not a copy collected days ago.

#### How are the price per SF and the deal score worked out?

`pricePerSf` is the asking price divided by the building size, for sales that show both; it stays empty for leases, land without a building, prices on request and price ranges. `dealScore` (0 to 100, sales in the United States only) compares the listing with reference medians by property type, measured in November 2026: up to 50 points for a price per SF below the median of its type and up to 50 for a cap rate above it; with only one of the two figures the score is scaled to 100 on that one. `discountToMarketPct` is how far below (positive) or above (negative) the median price per SF the listing is, `capRateSpread` is its cap rate minus the median in percentage points, and `dealReasons` says it in short sentences, such as `Price per SF 23% below the Office median`. A property type without enough listings behind its median, and a listing outside the United States, get no comparison. All five fields come with every listing at no extra charge. They are a quick way to sort a list, not a valuation.

#### What are the listing tags and the unit count?

`listingTags` are keyword tags read from the listing description: `owner-user`, `renovated`, `motivated-seller`, `medical`, `income-producing`, `development-site` and `net-lease`. The field is always there and empty when no tag applies. The app's description is a short teaser, so a tag can be missed; a tag can also be given to a listing that only mentions the word, so use them to narrow a list, not as a verdict. `income-producing` and `net-lease` are read on sales only. Pick tags in `listingTags` to keep the listings that carry at least one of them. `unitCount` is the number of housing units of a multifamily listing when LoopNet prints it, empty otherwise; `minUnitCount` and `maxUnitCount` keep the listings inside a range and drop those without a unit count.

#### Why is a listing I see on LoopNet missing?

The Actor covers every listing of LoopNet's public catalog plus every listing added in the last 30 days. A listing added a little earlier may not be in the catalog yet: paste its ID in **Listing IDs** and it is read. The property type is read from each listing's summary; a listing LoopNet files under an unusual type may be missed by a search of one type — search all types (empty **Property types**) to be sure.

#### Is the data safe to open in Excel or to show on a web page?

Titles and descriptions are the brokers' own words, copied as they are. A text can begin with `-`, `+`, `=` or `@` (a phone number such as `+1 210-383-0007`): Excel and Google Sheets may read such a cell of a CSV file as a formula or as a number. The Actor leaves the text as it is, so that the JSON and the API give the real value: when you open a CSV, import these columns as text. On a web page, escape every field like any text written by a stranger.

#### Can I get alerts when a price drops?

Yes. Schedule the Actor with **Only new listings** and **Track price changes**: each run returns the new listings plus the ones whose price moved since they were delivered, with the old price, the change and the change in % (see [Price drop alerts](#price-drop-alerts)). No extra charge.

#### Known limitations

- The Actor gives each listing's summary. The spaces one by one, parcel and tax values, tenants, all building facts, the date on market, brochures, 3D tours and videos are on LoopNet's full listing page, which the Actor does not read at the moment: their 22 columns stay empty.
- Auctions, ZIP codes and counties are not supported with the standard access.
- Prices are in US dollars, for listings abroad too.
- `onlyNew` alone remembers which listings you have, not their price: tick **Track price changes** to also get the listings whose price moved. A change is caught on the listings a run reads (the top of each search), not across the whole catalog.
- Two runs sharing the same `stateKey` at the same time may both return the same new listing.

**A run that reaches its timeout** stops itself about 45 seconds before it: no new page is asked, what it read is saved and, with `onlyNew`, remembered, and the run ends *Succeeded* with "Stopped before the run's timeout". Resurrect it to go on from there, or give the next run a longer timeout (Run options). On Apify the standard access saved 1,000 listings in about 2 min 30 s, and in about 7 minutes with the broker contact, owner and scores.

**A run the platform stops without warning** (out of memory)

- Resurrect it: it goes on from where it stood at most a minute before the stop, with the same list of listings. What it had read since is read again, and the listings already saved are skipped: none is delivered or charged twice, and `maxItems` still counts them.
- With `onlyNew`, the memory is saved after each write (at most once every 2 seconds): resurrect the stopped run and the listings it had saved meanwhile join the memory; leave it stopped for good, and the next run may return the listings of its last 2 seconds once more.

#### Something doesn't work?

The last line of the log counts the listings saved, filtered out, saved without an option they asked for, and the requests that failed after every retry. Those requests are listed, with the reason, in the `FAILED_REQUESTS` record of the run's key-value store. A run that saved nothing and had failed requests fails, and its last message gives the cause.

If LoopNet changes, you are told instead of paying for blank rows. If the first 20 listings read all lack their title, city, price, map position or photo, or LoopNet stops answering for 500 listings in a row, the run saves nothing more, stops and fails, and its last message says why. A run of fewer listings is checked at its end, from 5 listings: its rows are kept, and the run fails all the same.

### 🛟 Support

Open an issue in the **Issues** tab with a link to your run: the run log and the `FAILED_REQUESTS` record of the key-value store show exactly which requests failed and why.

# Actor input Schema

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

LoopNet listing URLs (`https://www.loopnet.com/Listing/.../41920405/`) or search URLs (`https://www.loopnet.com/search/office-space/austin-tx/for-lease/`: the Actor redoes the same place, property type and deal; the site's own extra filters in the URL are not read — use the Filters below). When this list is not empty, the search fields below (listing type, property types, locations, source) are ignored; filters, caps and monitoring still apply. Max 1 000 URLs.

## `listingIds` (type: `array`):

LoopNet listing IDs to read directly (the number at the end of a listing URL, e.g. `41920405`). Added to the start URLs. Max 1 000.

## `source` (type: `string`):

`search` = every listing of each location × property type, newest change first (no 750-result cap). `newListings` = only the listings LoopNet added in the last 30 days, newest first — the best source for a scheduled run. Ignored when Start URLs or Listing IDs are given.

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

For sale or for lease. Auctions: with the "clientProxy" access mode only.

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

One search per property type (times each location). Empty = all commercial real estate. LoopNet has no lease search for multifamily, hospitality and specialty, nor a sale search for coworking: such a pair is refused.

## `location` (type: `string`):

US city and state (`Austin, TX`), US state (`TX` or `Texas`), Canadian city and province (`Toronto, ON`) or province, a city abroad (`London, United Kingdom`), a country (`USA`, `Canada`, `United Kingdom`, `France`, `Spain`), or a city alone (`Austin`: in every state). ZIP codes and counties are not supported. Empty = every listing (US, Canada and abroad).

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

Several locations in one run: every property type is searched in every location (3 types × 4 locations = 12 searches, max 500). Added to **Location**.

## `maxItems` (type: `integer`):

Maximum number of listings to save for the whole run (after deduplication and filters). 0 = no limit.

## `maxItemsPerQuery` (type: `integer`):

Cap for EACH search (location × property type, or search URL; the listing IDs count as one search), so that the first search cannot use up the whole **Max listings** budget. 0 = no per-search cap.

## `includeBrokerContact` (type: `boolean`):

Add each broker's phone number and e-mail address (and title) to the listing. Charged per listing that gets at least one of them (event `broker-contact`). Off by default; **With a broker phone only** switches it on.

## `includeOwner` (type: `boolean`):

Add the owner or manager of the property as LoopNet shows it: name, website, description. LoopNet shows one for about 1 listing in 100; charged per listing that has one (event `owner-profile`).

## `includeScores` (type: `boolean`):

Add the four location scores (0-100). Charged per listing that gets them (event `location-scores`).

## `includeLimitedListings` (type: `boolean`):

LoopNet also publishes listings with almost nothing: no broker, price and size on request, no map position (often the newest ones). Off (default) = they are skipped, not saved and not charged; on = they are saved with what they have. Listing IDs you give are always read.

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

Only listings with an asking price at or above this amount (sale listings, in US dollars, for listings abroad too; a price LoopNet shows rounded, such as $1.7M, counts as 1,700,000). Listings without a price are dropped.

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

Only listings with an asking price at or below this amount. Listings without a price are dropped.

## `minBuildingSize` (type: `integer`):

Only buildings of at least this many square feet (building size, rentable or gross leasable area). Listings without a size are dropped.

## `maxBuildingSize` (type: `integer`):

Only buildings of at most this many square feet.

## `minSpaceSize` (type: `integer`):

Only listings with a space to lease of at least this many square feet: kept when the size range of its spaces reaches this size. This is the size of a space for lease, not of the building: a listing without a space size, so every sale, is dropped.

## `maxSpaceSize` (type: `integer`):

Only listings with a space to lease of at most this many square feet: kept when the size range of its spaces starts at or below this size. A listing without a space size, so every sale, is dropped.

## `propertySubtypes` (type: `array`):

Only listings whose property subtype contains at least one of these texts (case and accents ignored), e.g. `Warehouse`, `Medical`, `Strip Center`. A listing without a subtype is dropped.

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

Only sale listings that state a cap rate at or above this value (e.g. `6.5`). Listings without a cap rate are dropped.

## `maxCapRate` (type: `number`):

Only sale listings that state a cap rate at or below this value.

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

Only buildings built in or after this year. Listings without a year are dropped.

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

Only buildings built in or before this year.

## `buildingClasses` (type: `array`):

Only buildings of these classes; once a class is chosen, listings that state no class (about 2 in 3) are dropped. Empty = any class, and listings without a class are kept.

## `minLotSizeAcres` (type: `number`):

Only listings whose lot is at least this many acres. Listings without a lot size are dropped.

## `maxLotSizeAcres` (type: `number`):

Only listings whose lot is at most this many acres.

## `minRentPerSfYear` (type: `number`):

Lease listings: only those with at least one space asking this rent per square foot per year or more. Listings without a rent in $/SF/year are dropped.

## `maxRentPerSfYear` (type: `number`):

Lease listings: only those with at least one space asking this rent per square foot per year or less.

## `minUnitCount` (type: `integer`):

Multifamily: only listings that state at least this many housing units. Listings without a unit count are dropped.

## `maxUnitCount` (type: `integer`):

Multifamily: only listings that state at most this many housing units.

## `listingTags` (type: `array`):

Only listings that carry at least one of these tags, read from the listing description (see `listingTags` in the output). With the standard access the description is a short summary, so a tag can be missed. Empty = any.

## `keywords` (type: `array`):

Only listings whose title, highlights or description contain at least one of these words (case and accents ignored), e.g. `drive-thru`, `NNN`. With the standard access the description is the listing's short summary.

## `excludeKeywords` (type: `array`):

Drop the listings whose title, building name, highlights or description contain one of these WHOLE words (case and accents ignored): `art` drops "Art deco" but keeps "Smart building". Several words in one entry (`hand made`) must appear together, in that order. Chinese, Japanese, Korean, Thai, Lao, Khmer and Burmese words are found anywhere in the text (no spaces there).

## `requirePhone` (type: `boolean`):

Keep only the listings with a broker phone number (switches **Broker phone and e-mail** on: that option is then charged).

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

Skip the listings that a previous run (same **Memory key**) already delivered: they are not saved and not charged. First run = everything is new.

## `stateKey` (type: `string`):

Name of the memory used by **Only new listings**. Give each schedule / task its own key (e.g. `austin-office`) so that they do not share their memory. Letters, digits, `-` and `_`.

## `resetState` (type: `boolean`):

Forget everything remembered under this **Memory key** before the run: this run returns (and charges) every listing again. Untick it afterwards.

## `trackPriceChanges` (type: `boolean`):

Needs **Only new listings**. A listing already delivered by a previous run comes back, whole, when its asking price (or lowest rent) has moved, with `previousPrice`, `priceChange`, `priceChangePct` and `priceChangedAt`; one at the same price stays out. No extra charge: a listing that comes back is a normal result.

## `accessMode` (type: `string`):

`standard` (default, included in the price) = the listing summary, with the options above. `clientProxy` = LoopNet's search pages (the listing as its search card shows it), read through YOUR proxies (Proxy configuration > own proxies): only for a proxy that already gets past LoopNet's bot protection.

## `proxyConfiguration` (type: `object`):

Apify Proxy or your own proxies. Keep the default: it is included in the price. The residential Apify proxy is not available in this Actor.

## `maxConcurrency` (type: `integer`):

Maximum number of requests processed in parallel.

## `maxRequestsPerMinute` (type: `integer`):

Most requests to LoopNet in any 60 seconds, all of them counted: pages of listings (50 listings each with the standard access, 25 with the clientProxy access). The options (broker, owner, scores) are read with their page and not counted. A budget, not an even pace. Lower it if the log shows HTTP 429 / 403.

## `minRequestIntervalMs` (type: `integer`):

Smallest gap between two requests, in milliseconds. Unlike the per-minute rate, this spreads the requests evenly instead of letting them go out in a burst. 0 = no gap.

## `maxRequestRetries` (type: `integer`):

Retries per request before it is marked as failed. A request LoopNet turns away is first read again from a new IP — up to 10 times — without using up these retries.

## `debugLog` (type: `boolean`):

Include debug messages in the run log.

## Actor input object example

```json
{
  "startUrls": [],
  "listingIds": [],
  "source": "search",
  "listingType": "for-sale",
  "propertyTypes": [],
  "location": "Austin, TX",
  "locations": [],
  "maxItems": 100,
  "maxItemsPerQuery": 0,
  "includeBrokerContact": false,
  "includeOwner": false,
  "includeScores": false,
  "includeLimitedListings": false,
  "propertySubtypes": [],
  "buildingClasses": [],
  "listingTags": [],
  "keywords": [],
  "excludeKeywords": [],
  "requirePhone": false,
  "onlyNew": false,
  "stateKey": "default",
  "resetState": false,
  "trackPriceChanges": false,
  "accessMode": "standard",
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 4,
  "maxRequestsPerMinute": 120,
  "minRequestIntervalMs": 250,
  "maxRequestRetries": 5,
  "debugLog": false
}
```

# Actor output Schema

## `results` (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 = {
    "listingType": "for-sale",
    "location": "Austin, TX",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nice_dev/loopnet-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 = {
    "listingType": "for-sale",
    "location": "Austin, TX",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("nice_dev/loopnet-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 '{
  "listingType": "for-sale",
  "location": "Austin, TX",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call nice_dev/loopnet-listings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/loopnet-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/YxSdHnskissOZLAdY/builds/XFhfqwviPWZrquXXJ/openapi.json
