# LoopNet Scraper - Commercial Properties, Prices & Brokers (`abotapi/loopnet-scraper`) Actor

Scrape LoopNet commercial listings across the US, including office, retail, industrial, multifamily, land and hotels. Search by city or URL and extract 25+ fields including price, size, address, broker contacts and photos, with optional detail enrichment.

- **URL**: https://apify.com/abotapi/loopnet-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Real estate, Automation, Developer tools
- **Features:** Limited permissions
- **Stats:** 57 total users, 13 monthly users, 92.1% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 results

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

## LoopNet Scraper: Commercial Property Listings, Prices & Broker Contacts

LoopNet Scraper pulls commercial property listings from LoopNet across the United States and Canada (loopnet.com) and the United Kingdom and international markets (loopnet.co.uk): offices, retail, industrial, multifamily, land, hotels, restaurants, healthcare and more. Search by location and filters, or paste your own LoopNet search or listing URLs. Every listing comes back with 30+ fields including address, price, size, broker contacts, photos and a full description, plus one normalised deal sourcing block so US and UK rows share the same column names. Export to JSON, CSV or Excel, or pull results straight into your app through the API.

### Why This Scraper?

- **Two markets, one actor.** Choose loopnet.com (US / Canada) or loopnet.co.uk (UK / international), and URL mode auto-detects the site per pasted link.
- **Two modes.** Search by location and filters, or paste specific search or listing URLs, with multiple URLs supported in one run.
- **30+ fields per listing.** Address, price, size, year built, building class, broker contacts, photos, coordinates and more.
- **One normalised deal sourcing contract.** Every listing also carries a `commercial` block with the same column names on both LoopNet edges, so a downstream pipeline never has to branch on which site a row came from.
- **Optional detail enrichment.** Turn on `fetchDetails` to add coordinates, street address, broker phone, cap rate and occupancy per listing, only when you need them.
- **Built for schedules.** Incremental mode returns only new and changed listings on recurring runs, and Resume continues one interrupted run without re-collecting what it already saved.
- **Failures are reported as failures.** A run that could not reach LoopNet fails with a clear message instead of quietly returning an empty dataset.

### Use Cases

- **Deal sourcing and acquisitions teams:** build a normalised feed of for-sale and for-lease listings across markets and property types to find on-market and off-market deals.
- **Commercial brokers and analysts:** track competing listings, asking prices and size ranges for a city, submarket or property type.
- **Site selection for retailers and franchises:** filter available retail and industrial space by size and location to shortlist candidate sites.
- **Investment research:** aggregate asking prices, cap rates and occupancy where the source discloses them, to build market comparables.
- **Real estate tech and CRM pipelines:** feed listings straight into your own database or CRM, or push them into Notion, Airtable or Linear through the MCP connector.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live listing.

| Field | Example |
| --- | --- |
| `id` | `"00000001"` |
| `url` | `"https://www.loopnet.co.uk/listing/sample-road-london/00000001/"` |
| `title` | `"Sample Logistics Park, Unit 1"` |
| `listingType` | `"for-lease"` |
| `propertyType` | `"industrial-properties"` |
| `listingTypeName` | `"Industrial to Let"` |
| `addressRaw` | `"London, AA0 0AA"` |
| `city` | `"London"` |
| `state` | `null` (US rows carry a state; UK rows fill this with the county/region only when `fetchDetails` is on) |
| `zipCode` | `"AA0 0AA"` |
| `country` | `"GB"` |
| `price` / `priceRaw` | `13.25` / `"£13.25 - £15.25 sq ft pa"` |
| `size` / `sizeSqftMin` / `sizeSqftMax` | `"1,000 - 25,000 sq ft"` / `1000` / `25000` |
| `yearBuilt` / `buildingClass` | `2005` / `"Class A"` (mainly on the US edge, or with `fetchDetails` on) |
| `brokerNames` | `["Sample Agency", "Example Partners"]` |
| `description` | `"Full listing description text appears here."` |
| `primaryPhoto` | `"https://images.loopnet.co.uk/i2/000000000/117/sample.jpg"` |
| `scrapedAt` | `"2026-01-01T00:00:00.000Z"` |
| `detailStatus` | `"not_requested"`, `"enriched"` or `"failed"`, depending on whether `fetchDetails` was on and whether the detail page came back |
| `commercial` | the normalised deal sourcing block described below |

With `fetchDetails` on, rows also carry `latitude`/`longitude`, `street`, `brokerPhone`, `capRate` and `occupancy` when the source publishes them, plus the listing page's full photo gallery in `photos` (often 10 to 40 images, where a results card shows one or two), `unitAmenities` and `siteAmenities` (the page's own two amenity groups; `amenities` stays their union), `dateUpdated` (the page's "Last Updated" date) and, on loopnet.com, `transportation`: the nearby transit, bus, commuter rail and airport stops the listing page shows, each with its drive time and distance. Every row also carries `searchPosition`, the listing's position (1, 2, 3...) on the results page it was read from. With `incrementalMode` on, every row also carries `changeType`, `changedFields`, `firstSeenAt` and `lastSeenAt`.

#### The commercial row contract

LoopNet's two edges disagree on which fields they fill in: the US site populates `spaceUse`, `propertyTypeName` and `state`, while the UK site populates none of those and uses a postcode instead. So every row also carries a `commercial` object, a normalised shape with the same column names on both edges. It is purely additive: every field above is still there, unchanged, and every value in this block is derived from those fields. Pick the **Deal sourcing contract** view on the Output tab (or `?view=deal-sourcing` on the dataset API) to get just these columns.

| `commercial.*` field | What it is |
| --- | --- |
| contractVersion | `"cre-1.0"`. Bumped only on a breaking shape change, never on an addition. |
| recordKey / listingId | `"loopnet.co.uk:00000001"`, and the bare id. `recordKey` is unique across both sites. |
| assetClass / assetClassRaw / assetClassSource | Normalised to `office`, `retail`, `industrial`, `multifamily`, `land`, `hospitality`, `healthcare`, `restaurant`, `mixed-use`, `specialty` or `unknown`, plus the raw label and which field it was read from. |
| dealType | `"sale"`, `"lease"` or `"auction"`. |
| listingStatus | `"active"`, or `"removed"` on an `EXPIRED` row under incremental mode. |
| askingPrice / askingPriceCurrency / askingPriceBasis / askingPriceRaw | The parsed figure, its currency read from the symbol the site printed, whether it is a total or a per sq ft / per month rate, and the original text. |
| priceAvailability | `"disclosed"`, `"on_request"` (the site showed a price on application style placeholder) or `"not_published"` (no price surface at all). |
| financialsMasked / maskedFinancialFields | Whether an asking figure is published, and which of `askingPrice`, `capRate`, `occupancy` are empty on this row. |
| buildingSizeSqft (+ Min / Max) / landSizeAcres / sizeDisplay | Size, numeric and as printed. |
| address / city / region / postalCode / country | Location. `region` is the state on the US edge and the county/region on the UK edge. |
| hasCoordinates | Boolean flag so you can filter to mappable rows in one step. |
| brokerName / brokerNames / brokerageName / brokerageUrl / brokerPhone / hasBrokerContact | Broker and brokerage, plus a flag for rows with a reachable phone number. |
| description / primaryImage / images / imageCount | Copy and photos. |

A few things worth knowing before you build a pipeline on it: `capRate` and `occupancy` are published only on a property's own detail page, so they stay empty unless `fetchDetails` is on, `listedAt` is filled only on the rare listing that prints its own publication date so treat `scrapedAt` (or `firstSeenAt`/`lastSeenAt` under incremental mode) as the reliable date field, and the top level `currency` field falls back to `USD` whenever the detail page does not declare a currency (always the case with `fetchDetails` off), so `commercial.askingPriceCurrency` (read from the printed symbol) is the more reliable currency column for UK rows.

With `fetchDetails` on, a listing may also carry `propertyFacts`, `highlights`, `amenities` (`unitAmenities`/`siteAmenities` unioned), `documents`, `locationScores`, `brokers` (one entry per broker on the listing, each with name/title/phone/company/companyUrl), and, for lease listings, `spaces` (one entry per available unit/space, with size, term and rental rate). Multi-property US sale listings also carry `isPortfolio`, `portfolioPropertyCount` and `portfolioProperties`.

#### Dataset views and the run summary

Besides the default row-per-listing view, the Output tab offers: **Changes** (`?view=changes`), incremental-mode-only rows showing `changeType`/`changedFields`/`previousPrice`/`previousSiteListingStatus`/`firstSeenAt`/`lastSeenAt` for monitoring a recurring search; **Deal sourcing contract** (`?view=deal-sourcing`), the normalised `commercial.*` columns described above, one row per listing; **Property details** (`?view=property-details`); **Brokers** (`?view=brokers`), one row per broker on a listing (name/title/phone/company); and **Spaces** (`?view=spaces`), one row per available unit/space on a lease listing (size/term/rental rate). Pick a view from the Output tab dropdown or append `?view=<name>` to the dataset API URL.

A per-run summary (sources searched, listings pushed/enriched/rejected, whether the 750-result cap or price-band splitting was hit, and whether each source reached the end of its catalogue) is written to the run's key-value store under the key `OUTPUT` at the end of every run, alongside the overall totals already visible in the run log.

### How to Use

1. Pick a **mode**: `search` (build a URL from filters) or `url` (paste your own LoopNet links).
2. In search mode, choose the **site/region**, one or more **locations**, the **listing type** and **property type**. In URL mode, paste your links instead; the site, listing type and property type are read from each URL.
3. Set the optional price, size and sort filters, and **Max listings** to control run size and cost. Turn on **Fetch detail pages** if you need coordinates, street address, broker phone, cap rate or occupancy.
4. Click **Start**, then download the dataset as JSON, CSV or Excel, or read it through the API.

**Search the UK site nationwide (industrial to let):**

```json
{
  "mode": "search",
  "site": "co-uk",
  "locations": ["united-kingdom"],
  "listingType": "for-lease",
  "propertyType": "industrial-properties",
  "maxListings": 20
}
```

**Search a specific UK city:**

```json
{
  "mode": "search",
  "site": "co-uk",
  "locations": ["london-england--united-kingdom"],
  "listingType": "for-lease",
  "maxListings": 20
}
```

**Search the US site with detail pages enabled:**

```json
{
  "mode": "search",
  "site": "com",
  "locations": ["los-angeles-ca", "new-york-ny"],
  "listingType": "for-sale",
  "propertyType": "office-space",
  "fetchDetails": true,
  "maxListings": 30
}
```

**Paste your own URLs (site auto-detected per link), narrowed by size and sorted by size:**

```json
{
  "mode": "url",
  "urls": [
    "https://www.loopnet.co.uk/search/commercial-property/london-england--united-kingdom/for-rent/",
    "https://www.loopnet.co.uk/search/office-space/manchester-gtm--united-kingdom/for-rent/"
  ],
  "minSqft": 2000,
  "sortBy": "size-desc",
  "maxListings": 50
}
```

#### Locations: a plain name, or the exact LoopNet slug

Type a plain name - `London`, `Los Angeles`, `New York`, `Paris`, `Toronto` and other major markets on both sites resolve automatically through a list built from LoopNet's own "popular locations" pages. Not on that list yet? Paste the exact slug from a LoopNet URL instead, exactly as it appears in the address bar - for loopnet.com a city slug like `los-angeles-ca` or `new-york-ny` (or `usa` for nationwide), for loopnet.co.uk a full slug like `london-england--united-kingdom` (or `united-kingdom` for nationwide). A bare city name with no state/region - `london`, `los-angeles` - is NOT a valid slug on either site and returns a 404-style empty search; this actor resolves those specific plain names automatically, and warns loudly in the run log (naming this escape hatch) for any other name it doesn't recognize instead of silently returning nothing.

#### Supported URL shapes

Every shape below is live, auto-detected per URL (no `site` input needed in URL mode), and exercised by its own `test_plan.json` case:

| Shape | Example | Notes |
| --- | --- | --- |
| .com search results (SERP) | `https://www.loopnet.com/search/commercial-real-estate/los-angeles-ca/for-lease/` | `listingType`/`propertyType` are read from the URL, not the input. |
| .com search results, a US state slug | `https://www.loopnet.com/search/commercial-real-estate/ca/for-sale/` | A bare 2-letter state slug (e.g. `ca`, `tx`) works the same as a city slug. |
| .com search results + live filter/sort params | `...for-sale/?min-price=1000000&max-price=2000000&price-type=sf&min-size=5000&min-cap-rate=5_5&is-portfolio=exclude&keyword=warehouse` | These are the params **the site itself honours** (verified live: the response echoes the same price/size/keyword back in its own filter state) - not the old `sk=`/`price-min`/`bsf-min` trio below, which the site silently ignores. The actor's own `minPrice`/`maxPrice`/`priceType`/`minSqft`/`minCapRate`/`portfolioFilter`/`keyword` inputs build the identical params in search mode and also apply on top of a pasted URL carrying them (both modes). |
| .com search results + an inert legacy param (harmless, no effect) | `...for-lease/?sk=PriceHighToLow&price-min=100000&bsf-min=1000` | `sk` is the site's own saved-search **state key** (set by its UI, not a sort name); `price-min`/`bsf-min` are dead query names from an older site version. All three are preserved verbatim on a pasted URL but do not filter anything - use the live param names in the row above instead. |
| .com search results + a real saved-search state key | `...for-sale/?sk=1ff2e71097507fb8dc56538d9837600c&e=u` | A **real** `sk` value (minted by the site's own UI when a user saves a search) IS honoured server-side - this actor cannot mint one, but preserves whatever key a pasted URL already carries. |
| .com auctions | `https://www.loopnet.com/search/commercial-real-estate/usa/auctions/` | `listingType` resolves to `auction`. The UK site has no auction surface. |
| .com individual listing | `https://www.loopnet.com/Listing/434-Urenda-Loop-Carrizo-Springs-TX/41597858/` | Fetched by the **numeric id** in the URL, never by the slug text - a listing whose title/address changed since the URL was copied still resolves correctly. Always billed as a detail fetch (see below). |
| .co.uk search results, a city | `https://www.loopnet.co.uk/search/commercial-property/london-england--united-kingdom/for-rent/` | |
| .co.uk search results, nationwide | `https://www.loopnet.co.uk/search/commercial-property/united-kingdom/for-rent/` | |
| .co.uk individual listing | `https://www.loopnet.co.uk/listing/10-prestons-rd-london/35346928/` | Same by-id guarantee as the .com listing shape above. |
| .com search results, a page-N URL | `https://www.loopnet.com/search/commercial-real-estate/los-angeles-ca/for-lease/2/` | A pasted `/N/` URL starts at page N and continues forward. |
| .com property-type category | `https://www.loopnet.com/search/office-space/los-angeles-ca/for-lease/` | Property type is read from the URL. |
| .com search results, nationwide for-sale | `https://www.loopnet.com/search/commercial-real-estate/usa/for-sale/` | |
| .com search results + `?sk=x&sb=1` | `https://www.loopnet.com/search/commercial-real-estate/los-angeles-ca/for-sale/?sk=x&sb=1` | Query string is preserved verbatim; see the saved-search key rows above. |
| .co.uk search results, a page-N URL | `https://www.loopnet.co.uk/search/commercial-property/london-england--united-kingdom/for-rent/2/` | Starts at page N. |
| .co.uk property-type category | `https://www.loopnet.co.uk/search/office-space/london-england--united-kingdom/for-rent/` | |
| .co.uk search results, nationwide for-sale | `https://www.loopnet.co.uk/search/commercial-property/united-kingdom/for-sale/` | |
| Tracking / UTM params on any search or listing URL | `...for-lease/?utm_source=newsletter&utm_medium=email` | Passed straight through to the fetch; they don't affect parsing. |
| A non-listing LoopNet page (businesses-for-sale, broker profile/directory, tax-parcel record) | `https://www.loopnet.com/biz/businesses-for-sale`, `.../commercial-real-estate-brokers/profile/...`, `.../commercial-real-estate-brokers/united-states/ca/`, `.../property/900-n-18th-st-kansas-city-ks-66102/20209-054121/` | Not a commercial-property results page this actor can parse (own CoStar vertical, broker page, or tax record - no `listing`/search-result markup) - logs a loud warning ("did not look like a LoopNet results page") rather than silently returning 0 results. A `/property/` parcel page in particular embeds a "similar listings" carousel for **other, unrelated** listings, which this actor explicitly excludes so it is never mistaken for that URL's own search results. |

**Default URLs.** URL mode run with `urls` omitted or left unedited now scrapes the 12 example URLs (one per shape above) in order until `maxListings` (default 20) is reached, so the last examples may be cut at the default. Each listing appears at most once per run: a listing that an earlier example URL's results page already listed is skipped, even if it was not saved because of the per-URL share of Max listings, so the page-2 and individual-listing examples can return no rows in this default run. Paste a URL on its own to scrape it fully. Leaving the list untouched scrapes these examples instead of scraping nothing. Clear the list and paste your own links to scrape only those.

A pasted **individual listing URL** (the two "individual listing" rows above) is always fetched and always billed as a detail-enrichment row, **regardless of the `Fetch detail pages` toggle** - there is no results-page card to fall back on for a single pasted listing, so this is the one case where that toggle doesn't change what happens.

LoopNet's map/draw-area search is client-side JS state (the area is derived from the resolved location, not a URL parameter), not a separate pastable link - there is no `/map/` or `?bbox=` URL shape to support.

loopnet.com lists at most 750 results for any one search (30 pages of 25); a search matching more than that logs a warning and returns only the first 750. Turn on `splitLargeSearches` to walk a capped for-sale search again in price bands and reach listings beyond the first 750; a search that is still capped after banding is reported as such rather than silently truncated.

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/loopnet-scraper").call(run_input={
    "mode": "search",
    "site": "co-uk",
    "locations": ["united-kingdom"],
    "listingType": "for-lease",
    "maxListings": 20,
})
for listing in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(listing["title"], listing["priceRaw"], listing["size"])
```

JavaScript:

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/loopnet-scraper').call({
  mode: 'search',
  site: 'co-uk',
  locations: ['united-kingdom'],
  listingType: 'for-lease',
  maxListings: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### Resuming a full catalogue walk

For an unbounded pull (`maxPages` empty, `maxListings` set to `0`), two things protect a long walk: **`resumeFromRunId`** lets you continue in a brand new run by pasting the id of a previous run or its dataset, and this run skips every listing `id` already saved there, appending only new listings. A **checkpoint** is also saved automatically as the run progresses, so if the run is interrupted by a platform migration, or you click Resurrect on a failed run, it picks back up in the same run without re-saving, or re-charging for, listings it already collected. No input is needed for this part, it is automatic.

#### Incremental mode for recurring monitoring

Distinct from `resumeFromRunId` above, which continues ONE specific interrupted run. `incrementalMode` is for scheduling this actor to run the same search again and again (daily or weekly) and getting only what changed. The first run returns every matching listing marked `NEW`. Later runs return only `NEW`, `UPDATED` and `REAPPEARED` listings, unchanged listings are suppressed (not pushed, not billed) unless you turn on `emitUnchanged`. State is kept per `stateKey`, or an automatic hash of the search scope, in a dedicated key value store, so it costs no extra requests against LoopNet. `emitExpired` only produces rows once a run has fully scanned the tracked search; a page capped, resumed, partially rejected, or started from a pasted URL's page 2+ run leaves previously tracked listings' state as is instead of guessing they are gone.

In URL mode, the search scope that keys the automatic state includes the price/size filters below (they apply to a pasted results URL too, see "Filters and sort" above), not just the pasted URLs themselves, so changing a filter starts a fresh baseline instead of reusing one that would otherwise look like every now-excluded listing had disappeared. An explicit `stateKey` always wins over the automatic scope; it is only EXPIRED-safe when every run that shares it also shares the same filters/mode/URLs/`fetchDetails`, sharing one `stateKey` across genuinely different scopes reintroduces the same false-EXPIRE risk on purpose.

Three fields are always returned but never, by themselves, make a listing `UPDATED`: `searchPosition` (it moves whenever other listings are added or re-ranked), `dateUpdated` (the site bumps it for edits that change none of the fields this actor reads; a real price or description change is still caught by those fields) and `transportation` (a nearby-stops panel the site computes around the listing). The first run after an upgrade that starts filling a field the previous version left empty (for example the title and price of the site's compact results cards, or the full photo gallery) does not re-report your tracked listings as `UPDATED` either.

`ignoreFieldsForChanges` excludes specific fields from change detection ONLY, the field still comes back in every row, it is just never, by itself, the reason a listing is classified UPDATED. This actor's own fields measured stable across repeat scrapes are already excluded automatically with no input needed; use this when you personally observe a different field drifting for your search. Accepts a plain field name (`exposureLevel`) or a dotted path into the nested `commercial` block (`commercial.primaryImage`), or `commercial.*` to ignore every field in that block at once. Turning this on, or changing it, never triggers a one-time `UPDATED` wave of its own.

### Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the scrape, the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human readable **summary** of each record, not the full JSON. Each item becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify > Settings > Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you have not authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where items should be created.

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 a URL from the search fields below, `url` passes through your own URLs. The price, size and sort filters apply in both modes. |
| `site` | string | `co-uk` | `co-uk` for UK / international listings, `com` for US / Canada listings. Auto-detected per link in URL mode. |
| `locations` | array | (none) | Search mode only. A plain name (`London`, `Los Angeles`, `New York`...) resolves automatically on major markets; see "Locations: a plain name, or the exact LoopNet slug" above. Otherwise, the exact slug: US `los-angeles-ca`, or `usa` for nationwide; UK `london-england--united-kingdom`, or `united-kingdom` for nationwide. Multiple locations supported. Ignored in URL mode. |
| `listingType` | string | `for-sale` | Search mode only. `for-sale`, `for-lease` (For Rent on the UK site) or `auction` (US site only). Ignored in URL mode, where it is read from each pasted URL. |
| `propertyType` | string | `all-commercial` | Search mode only. Office, retail, industrial, multifamily, land, hotels and more. Ignored in URL mode, where it is read from each pasted URL. |
| `urls` | array | 12 example URLs, one per shape (scraped in order until Max listings) | URL mode only. LoopNet search or listing URLs; the site is auto-detected per URL. See "Supported URL shapes" above for the full list (SERP, auctions, individual listing, both sites). The search mode fields above are ignored, but the price, size and sort fields below still apply to every pasted search URL. A pasted individual listing URL is saved as is, and always billed as a detail fetch regardless of `fetchDetails`. |
| `minPrice` | integer | (none) | Applies in both modes, including a pasted search URL. Keeps listings priced at or above this value. Listings with no total price shown are kept. |
| `maxPrice` | integer | (none) | Applies in both modes, including a pasted search URL. Keeps listings priced at or below this value. Listings with no total price shown are kept. |
| `priceType` | string | `total` | For-sale searches on loopnet.com. Whether `minPrice`/`maxPrice` above is the total asking price (default), or a price per sq ft, per acre or per unit. A per-unit filter is applied by the site; this actor does not re-filter it against the card's total price. |
| `minSqft` | integer | (none) | Applies in both modes, including a pasted search URL. Keeps listings sized at or above this value. Listings with no size shown are kept. |
| `maxSqft` | integer | (none) | Applies in both modes, including a pasted search URL. Keeps listings sized at or below this value. Listings with no size shown are kept. |
| `sortBy` | string | `default` | Applies in both modes, including a pasted search URL. On loopnet.com for-sale and for-lease searches in search mode the site itself applies newest, price and size order, so the run reads listings in that order across all pages (with `maxListings` 50 and price high to low you get the 50 most expensive). In URL mode and on loopnet.co.uk and auction searches the order is applied within each results page. |
| `minCapRate` | number | (none) | For-sale searches on loopnet.com. Only listings with a published cap rate at or above this percentage, e.g. `5.5`. |
| `maxCapRate` | number | (none) | For-sale searches on loopnet.com. Only listings with a published cap rate at or below this percentage. |
| `keyword` | string | (none) | Search mode on loopnet.com. Free-text keyword the site matches against the listing text, e.g. "parking" or "drive-in". |
| `listedInLastDays` | integer | (none) | Only listings put on the market within this many days. loopnet.com applies it site-side in search mode; the site serves four windows (24 hours, 7 days, one month, three months), so your number walks the smallest window that contains it and results may include slightly older listings than requested. loopnet.co.uk applies it to the "Date on Market" each results card prints; cards without a date are left out. Leave empty for all dates. |
| `portfolioFilter` | string | `include` | For-sale searches on loopnet.com. Include multi-property portfolio sales (default), show only them, or leave them out. |
| `condosFilter` | string | `include` | For-sale searches on loopnet.com. Include condo units (default), show only condos, or leave them out. Applied by the site in search mode and enforced again on every row the run parses. |
| `subType` | string | `any` | loopnet.com for-sale searches only. Narrows the search to one property sub-type the site lists: warehouses, data centers, flex, cold storage, medical offices, strip malls, banks, gas stations, hotels, self-storage, mobile home parks, farms and more (51 in all). It replaces the property type (each sub-type belongs to one). The site has no sub-types for lease, auction or loopnet.co.uk searches, so it is ignored there with a warning. |
| `fetchDetails` | boolean | `false` | Adds coordinates, street address, broker phone, cap rate, occupancy and other detail page fields per listing. |
| `maxPages` | integer | (none) | Safety ceiling on results pages walked per location or URL. Leave empty to walk every result page; pages stop early once Max listings is reached. |
| `maxListings` | integer | `20` | Stop after this many listings across all searches. `0` means unlimited. |
| `splitLargeSearches` | boolean | `false` | loopnet.com lists at most 750 results for one search (see "Supported URL shapes" above). When a for-sale search hits that cap and Max listings asks for more, walk it again in price bands (splitting any band that is still capped) to reach listings beyond the first 750. Listings with no published price fall in no band. Off by default. |
| `resumeFromRunId` | string | (none) | A previous run id or dataset id to continue a full catalogue walk across separate runs. Listings already collected there (matched by `id`) are skipped. |
| `incrementalMode` | boolean | `false` | Turn on for recurring, scheduled monitoring of the same search. Classifies each listing as NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED against the actor's own remembered state. |
| `stateKey` | string | (none) | Incremental mode only. Names a monitoring campaign so its state stays stable, or to deliberately share state across differently configured runs. Left empty, a key is derived automatically from the search scope. |
| `emitUnchanged` | boolean | `false` | Incremental mode only. Also return, and bill, listings that have not changed since the last run, marked UNCHANGED. |
| `emitExpired` | boolean | `false` | Incremental mode only. Also return, and bill, listings that were tracked before but are no longer found, marked EXPIRED. Only produced once a run fully scans the tracked search. |
| `ignoreFieldsForChanges` | array | (none) | Incremental mode only. Field names (or dotted nested paths like `commercial.primaryImage`, or `commercial.*`) to exclude from change detection. The field is still returned, it just never drives an UPDATED classification. |
| `proxy` | object | Apify Residential | Connection settings. Both loopnet.co.uk and loopnet.com work on Apify Residential out of the box (see "Plan Requirement" below); free plans have no Apify proxy and automatically fall back to a backup connection, so supply your own residential proxy URLs here for guaranteed access on any plan. |
| `mcpConnectors` | array | (none) | Optional. Send a summary of each record to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | (none) | Notion connector only. Page under which item pages are created. |
| `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.

```json
{
  "id": "00000001",
  "url": "https://www.loopnet.co.uk/listing/sample-road-london/00000001/",
  "title": "Sample Logistics Park, Unit 1",
  "listingType": "for-lease",
  "propertyType": "industrial-properties",
  "propertyTypeName": null,
  "spaceUse": null,
  "listingTypeName": "Industrial to Let",
  "exposureLevel": "featured",
  "addressRaw": "London, AA0 0AA",
  "street": null,
  "city": "London",
  "state": null,
  "zipCode": "AA0 0AA",
  "country": "GB",
  "marketId": null,
  "propertyId": null,
  "latitude": null,
  "longitude": null,
  "price": 13.25,
  "priceRaw": "£13.25 - £15.25 sq ft pa",
  "priceUnit": "per_sqft_year",
  "priceLabel": null,
  "listedAt": null,
  "currency": "USD",
  "size": "1,000 - 25,000 sq ft",
  "sizeSqft": 25000,
  "sizeSqftMin": 1000,
  "sizeSqftMax": 25000,
  "sizeAcres": null,
  "yearBuilt": null,
  "buildingClass": null,
  "capRate": null,
  "occupancy": null,
  "brokerNames": ["Sample Agency", "Example Partners"],
  "brokerCompanyUrl": null,
  "brokerName": "Sample Agency",
  "brokerCompany": null,
  "brokerPhone": null,
  "description": "Full listing description text appears here.",
  "primaryPhoto": "https://images.loopnet.co.uk/i2/000000000/117/sample.jpg",
  "photos": ["https://images.loopnet.co.uk/i2/000000000/117/sample.jpg"],
  "scrapedAt": "2026-01-01T00:00:00.000Z",
  "detailStatus": "not_requested",
  "commercial": {
    "contractVersion": "cre-1.0",
    "recordKey": "loopnet.co.uk:00000001",
    "listingId": "00000001",
    "sourceSite": "loopnet.co.uk",
    "sourceUrl": "https://www.loopnet.co.uk/listing/sample-road-london/00000001/",
    "assetClass": "industrial",
    "assetClassRaw": "Industrial to Let",
    "assetClassSource": "listingTypeName",
    "dealType": "lease",
    "listingStatus": "active",
    "askingPrice": 13.25,
    "askingPriceCurrency": "GBP",
    "askingPriceBasis": "per_sqft_year",
    "askingPriceRaw": "£13.25 - £15.25 sq ft pa",
    "priceAvailability": "disclosed",
    "financialsMasked": false,
    "maskedFinancialFields": ["capRate", "occupancy"],
    "capRate": null,
    "occupancy": null,
    "buildingSizeSqft": 25000,
    "buildingSizeSqftMin": 1000,
    "buildingSizeSqftMax": 25000,
    "landSizeAcres": null,
    "sizeDisplay": "1,000 - 25,000 sq ft",
    "address": "London, AA0 0AA",
    "city": "London",
    "region": null,
    "postalCode": "AA0 0AA",
    "country": "GB",
    "latitude": null,
    "longitude": null,
    "hasCoordinates": false,
    "listedAt": null,
    "brokerName": "Sample Agency",
    "brokerNames": ["Sample Agency", "Example Partners"],
    "brokerageName": null,
    "brokerageUrl": null,
    "brokerPhone": null,
    "hasBrokerContact": false,
    "description": "Full listing description text appears here.",
    "primaryImage": "https://images.loopnet.co.uk/i2/000000000/117/sample.jpg",
    "images": ["https://images.loopnet.co.uk/i2/000000000/117/sample.jpg"],
    "imageCount": 1
  }
}
```

### Plan Requirement

This actor runs on the default Apify Residential proxy connection, which works for both loopnet.co.uk (UK / international) and loopnet.com (US / Canada); your own proxy URLs are optional. The actor requests pages with LoopNet's own mobile-app client profile: on 2026-10-13 pages were served on both Apify Datacenter and Residential connections on both sites, while a plain desktop-browser style request is refused on both. The default stays Residential, and LoopNet changes what it accepts from time to time, so a run can still be refused on some days. Free plans have no Apify proxy at all: if your plan's Apify Residential connection is refused, the run automatically continues on a backup connection instead of failing outright, and some pages may still be unavailable on that backup connection. For guaranteed residential access, upgrade your plan at [apify.com/pricing](https://apify.com/pricing) or supply your own proxy URLs. Your own proxy URLs, or an explicitly disabled connection, are used exactly as given with no automatic backup. If a run ever does fail with a connection message, retry later or supply your own residential proxy URLs in the Connection field.

### FAQ

#### How much does it cost?

You pay per listing returned, plus a small per listing surcharge for every listing enriched with a detail-page fetch. That happens whenever `fetchDetails` is on, and also for a pasted individual listing URL even with `fetchDetails` off, since there is no results-page card to fall back on for a single pasted listing (see "Supported URL shapes" above). The **Pricing** tab shows the current rates. Use **Max listings** to cap the cost of any run.

#### Is it legal to scrape LoopNet?

This actor collects only publicly available commercial property listings. You are responsible for how you use the data: follow LoopNet's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Listing facts such as price and size are generally not protected, but photos and broker-provided copy may carry 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**. The first scheduled run returns everything as NEW; every later run returns only NEW, UPDATED and REAPPEARED listings, and unchanged listings are not billed unless you turn on `emitUnchanged`.

#### What is the difference between Resume and Incremental mode?

`resumeFromRunId` continues ONE specific interrupted run: paste a previous run or dataset id and this run skips every listing id already saved there. `incrementalMode` is for running the SAME search again and again on a schedule; it keeps its own state between runs and classifies each listing as NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED. Use Resume to finish an interrupted one-off pull, and Incremental mode for ongoing monitoring.

#### Are there limitations I should know about?

No login or paywall gates any field this actor reads - everything documented above is public. Several filters are sale-only or lease-only and say so in their own description: `minCapRate`/`maxCapRate`/`priceType`/`keyword`/`portfolioFilter`/`condosFilter`/`subType` apply to for-sale searches on loopnet.com only, `auction` listings exist on loopnet.com only (the UK site has no auction surface), and `spaces` (per-unit availability) only appears on lease listings. Pricing is sometimes withheld by the listing itself rather than by this actor: `priceAvailability` and `commercial.financialsMasked`/`maskedFinancialFields` tell you, per row, whether a figure was published at all.

#### Why did my run fail instead of returning an empty dataset?

If every result page was refused by the site, the run fails with a message naming the affected site rather than reporting a false "no listings found". That is a connection problem, so retry later, or supply your own residential proxy URLs. If the search itself genuinely matches nothing, for example an overly narrow price or size filter, the run still succeeds with an empty dataset and a status message suggesting a broader search.

#### 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 results into Notion, Linear or Airtable.

### 🔗 Want more travel data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🏨 <a href="https://apify.com/abotapi/hipflat-scraper"><b>Hipflat Scraper Thailand Property &amp; Projects</b></a><br>Scrape hipflat.co.th property listings and new-project data across Thailand: price, price...</td><td>🏨 <a href="https://apify.com/abotapi/wego-com-scraper"><b>Wego Scraper</b></a><br>Scrape Wego hotels by city or search URL. Get rates, providers, booking links...</td></tr>
<tr><td>🏨 <a href="https://apify.com/abotapi/trip-com-scraper"><b>Trip.com Hotels, Rooms &amp; Reviews Scraper</b></a><br>Pull structured hotel listings from trip.com with prices, room types, amenities...</td><td>🏨 <a href="https://apify.com/abotapi/expedia-universal-scraper"><b>Expedia Hotels Scraper</b></a><br>From $1/1K. Scrape Expedia.com hotel listings and reviews for any destination. Extract...</td></tr>
<tr><td>🏨 <a href="https://apify.com/abotapi/yanolja-com-scraper"><b>Yanolja Scraper</b></a><br>Scrape Yanolja (NOL), Korea's largest accommodations marketplace, by region or URL. Every...</td><td>🏨 <a href="https://apify.com/abotapi/hostelworld-scraper"><b>Hostelworld Scraper</b></a><br>Scrape Hostelworld hostels, hotels, guesthouses, B&amp;Bs, apartments and campsites by city...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/loopnet-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

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

search = build URLs from the search fields below; url = pass through your own LoopNet URLs. The price, size and sort filters apply in both modes.

## `site` (type: `string`):

Which LoopNet site to search. loopnet.co.uk covers the UK and international listings; loopnet.com covers US and Canada listings. Both work on Apify Proxy out of the box. In URL mode this is auto-detected from each URL.

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

Search mode only. Type a plain name — "London", "Los Angeles", "New York", "Paris", "Toronto" and other major markets resolve automatically per site. Not recognized yet? Paste the exact LoopNet slug instead: for loopnet.com a city slug like "los-angeles-ca" or "new-york-ny" (or "usa" for nationwide); for loopnet.co.uk a full slug like "london-england--united-kingdom" (or "united-kingdom" for nationwide). A name that resolves to nothing is logged as a warning and passed through as-is, which is likely to return 0 results — use URL mode with a pasted LoopNet search URL as the reliable fallback. Multi-location supported. In URL mode this field is ignored, because each pasted URL carries its own location.

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

Search mode only. For Sale, For Lease (For Rent on the UK site), or Auction (US site only). In URL mode this field is ignored and the listing type is read from each pasted URL instead.

## `propertyType` (type: `string`):

Search mode only. Choose All Types for every category. In URL mode this field is ignored and the property type is read from each pasted URL instead.

## `urls` (type: `array`):

URL mode only. Paste loopnet.com or loopnet.co.uk search or listing URLs. The site is auto-detected per URL. The search-mode fields above (Locations, Listing type, Property type) are ignored here, but the price, size and sort fields below still apply to every pasted search URL. A pasted individual listing URL is saved as-is (and always billed as a detail fetch, whether or not "Fetch detail pages" is on, since a pasted listing URL is itself the detail page). Results continue from the page number in the URL onward. The default and prefill below carry one example of every supported URL shape — see the README's URL shapes table for the full list, including auctions and sort/price/size query params. Leaving this field untouched (or omitting it) in URL mode scrapes these 12 examples, one per shape, in order until Max listings is reached (default 20), so later examples may be cut or return no rows.

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

Applies in both search mode and URL mode, including a pasted search URL. Keeps listings priced at or above this value. Listings whose results page shows no total price (a per sq ft or per month rent, for example) are kept. A pasted individual listing URL is always saved as-is.

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

Applies in both search mode and URL mode, including a pasted search URL. Keeps listings priced at or below this value. Listings whose results page shows no total price (a per sq ft or per month rent, for example) are kept. A pasted individual listing URL is always saved as-is.

## `minSqft` (type: `integer`):

Applies in both search mode and URL mode, including a pasted search URL. Keeps listings with size at or above this value. Listings whose results page shows no size are kept. A pasted individual listing URL is always saved as-is.

## `maxSqft` (type: `integer`):

Applies in both search mode and URL mode, including a pasted search URL. Keeps listings with size at or below this value. Listings whose results page shows no size are kept. A pasted individual listing URL is always saved as-is.

## `sortBy` (type: `string`):

Applies in both search mode and URL mode, including a pasted search URL. On loopnet.com for-sale and for-lease searches in search mode the site applies newest, price and size orders across the whole results walk; in URL mode and on loopnet.co.uk listings are ordered within each results page.

## `listedInLastDays` (type: `integer`):

Only listings put on the market within this many days. loopnet.com applies this on the site itself in search mode (a pasted results URL keeps its own filters). The site serves four windows — 24 hours, 7 days, one month, three months — so your number walks the smallest window that contains it (1-7 -> last 7 days, 8-31 -> last month, 32+ -> last 3 months) and results may include slightly older listings than requested.

## `priceType` (type: `string`):

For-sale searches on loopnet.com. Whether Minimum/Maximum price above is the total asking price (default), or a price per sq ft, per acre or per unit. A per-unit filter is applied by the site; this actor does not re-filter it against the card's total price.

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

For-sale searches on loopnet.com. Only listings with a published cap rate at or above this percentage, e.g. 5.5.

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

For-sale searches on loopnet.com. Only listings with a published cap rate at or below this percentage.

## `portfolioFilter` (type: `string`):

For-sale searches on loopnet.com. Include multi-property portfolio sales (default), show only them, or leave them out.

## `condosFilter` (type: `string`):

For-sale searches on loopnet.com. Include condo units (default), show only condos, or leave them out. Applied by the site in search mode (a pasted results URL keeps its own filters) and enforced again on every row the run parses.

## `keyword` (type: `string`):

Search mode on loopnet.com. Free-text keyword the site matches against the listing text, e.g. "parking" or "drive-in".

## `subType` (type: `string`):

Narrow a loopnet.com for-sale search to one sub-type of property: warehouses, data centers, medical offices, strip malls, hotels, self-storage and more. Search mode only; it replaces the Property type above (each sub-type belongs to one property type). The site has no sub-types for lease, auction or loopnet.co.uk searches, so it is ignored there with a warning. Leave on Any for no sub-type filter.

## `fetchDetails` (type: `boolean`):

Adds extra fields per listing (coordinates, street address, broker phone, JSON-LD). The UK site already returns title, address, size, full description and broker names on the results page, so leaving this off is faster and cheaper.

## `maxPages` (type: `integer`):

Safety ceiling on results pages walked per location/URL. Leave empty to walk every result page. Pages stop early once Max listings is reached.

## `maxListings` (type: `integer`):

Stop after this many listings across all searches. 0 = unlimited.

## `splitLargeSearches` (type: `boolean`):

loopnet.com lists at most 750 results for one search. When a for-sale search hits that cap and Max listings asks for more, walk it again in price bands (splitting any band that is still capped) to reach listings beyond the first 750. Listings with no published price fall in no band. Off by default.

## `proxy` (type: `object`):

The default Apify Residential connection works for both loopnet.co.uk and loopnet.com (regional connections are handled automatically). Your own proxy URLs are optional.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue a full-catalogue walk across separate runs. Listings already collected there (matched by listing id) are skipped, so this run only saves the new ones. Leave empty for a normal fresh run. This continues ONE specific interrupted run — for recurring monitoring of the same search, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily/weekly or other recurring monitoring of the SAME search. The first run returns every matching listing as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED listings. Turn on "Emit unchanged" or "Emit expired" only when you also want those returned (and billed). State is kept separately per site/location/filter/fetchDetails setup; use State key to name or deliberately share a monitoring campaign. Defaults off, so existing schedules are unaffected. To continue one specific interrupted run instead, use Resume from a previous run above.

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

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the site/locations/filters/fetchDetails — different searches then never mix state with each other.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return listings that have not changed since the last run, marked UNCHANGED. This returns — and bills — extra rows you already have, so leave it off unless you specifically want the full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return listings that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search — not when Max pages/Max listings capped it or when Resume was used. This returns — and bills — extra synthetic rows, so leave it off unless you need expiry tracking.

## `ignoreFieldsForChanges` (type: `array`):

Optional, incremental mode only. Field names that should never, by themselves, mark a listing UPDATED — the field still comes back in every row, it is just excluded from change detection. Use a plain field name (e.g. "exposureLevel") or a dotted path into the nested commercial block (e.g. "commercial.primaryImage"), or "commercial.\*" to ignore every field inside that block. Leave empty for the default: this actor's own fields measured stable across repeat scrapes are already excluded automatically, so you only need this for noise you personally observe.

## `mcpConnectors` (type: `array`):

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify → Settings → Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON — the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "site": "co-uk",
  "locations": [
    "united-kingdom"
  ],
  "listingType": "for-sale",
  "propertyType": "all-commercial",
  "urls": [
    "https://www.loopnet.com/search/commercial-real-estate/los-angeles-ca/for-lease/",
    "https://www.loopnet.com/search/commercial-real-estate/los-angeles-ca/for-lease/2/",
    "https://www.loopnet.com/search/commercial-real-estate/usa/auctions/",
    "https://www.loopnet.com/search/office-space/los-angeles-ca/for-lease/",
    "https://www.loopnet.com/search/commercial-real-estate/los-angeles-ca/for-sale/?sk=x&sb=1",
    "https://www.loopnet.com/search/commercial-real-estate/usa/for-sale/",
    "https://www.loopnet.com/Listing/11340-W-Olympic-Blvd-Los-Angeles-CA/29122043/",
    "https://www.loopnet.co.uk/search/commercial-property/london-england--united-kingdom/for-rent/",
    "https://www.loopnet.co.uk/search/commercial-property/london-england--united-kingdom/for-rent/2/",
    "https://www.loopnet.co.uk/search/office-space/london-england--united-kingdom/for-rent/",
    "https://www.loopnet.co.uk/search/commercial-property/united-kingdom/for-sale/",
    "https://www.loopnet.co.uk/listing/10-prestons-rd-london/35346928/"
  ],
  "sortBy": "default",
  "priceType": "total",
  "portfolioFilter": "include",
  "condosFilter": "include",
  "subType": "any",
  "fetchDetails": false,
  "maxListings": 20,
  "splitLargeSearches": false,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `deal-sourcing` (type: `string`):

No description

## `property-details` (type: `string`):

No description

## `changes` (type: `string`):

No description

## `brokers` (type: `string`):

No description

## `spaces` (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 = {
    "mode": "search",
    "locations": [
        "united-kingdom"
    ],
    "urls": [
        "https://www.loopnet.com/search/commercial-real-estate/los-angeles-ca/for-lease/",
        "https://www.loopnet.com/search/commercial-real-estate/los-angeles-ca/for-lease/2/",
        "https://www.loopnet.com/search/commercial-real-estate/usa/auctions/",
        "https://www.loopnet.com/search/office-space/los-angeles-ca/for-lease/",
        "https://www.loopnet.com/search/commercial-real-estate/los-angeles-ca/for-sale/?sk=x&sb=1",
        "https://www.loopnet.com/search/commercial-real-estate/usa/for-sale/",
        "https://www.loopnet.com/Listing/11340-W-Olympic-Blvd-Los-Angeles-CA/29122043/",
        "https://www.loopnet.co.uk/search/commercial-property/london-england--united-kingdom/for-rent/",
        "https://www.loopnet.co.uk/search/commercial-property/london-england--united-kingdom/for-rent/2/",
        "https://www.loopnet.co.uk/search/office-space/london-england--united-kingdom/for-rent/",
        "https://www.loopnet.co.uk/search/commercial-property/united-kingdom/for-sale/",
        "https://www.loopnet.co.uk/listing/10-prestons-rd-london/35346928/"
    ],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/loopnet-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 = {
    "mode": "search",
    "locations": ["united-kingdom"],
    "urls": [
        "https://www.loopnet.com/search/commercial-real-estate/los-angeles-ca/for-lease/",
        "https://www.loopnet.com/search/commercial-real-estate/los-angeles-ca/for-lease/2/",
        "https://www.loopnet.com/search/commercial-real-estate/usa/auctions/",
        "https://www.loopnet.com/search/office-space/los-angeles-ca/for-lease/",
        "https://www.loopnet.com/search/commercial-real-estate/los-angeles-ca/for-sale/?sk=x&sb=1",
        "https://www.loopnet.com/search/commercial-real-estate/usa/for-sale/",
        "https://www.loopnet.com/Listing/11340-W-Olympic-Blvd-Los-Angeles-CA/29122043/",
        "https://www.loopnet.co.uk/search/commercial-property/london-england--united-kingdom/for-rent/",
        "https://www.loopnet.co.uk/search/commercial-property/london-england--united-kingdom/for-rent/2/",
        "https://www.loopnet.co.uk/search/office-space/london-england--united-kingdom/for-rent/",
        "https://www.loopnet.co.uk/search/commercial-property/united-kingdom/for-sale/",
        "https://www.loopnet.co.uk/listing/10-prestons-rd-london/35346928/",
    ],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/loopnet-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 '{
  "mode": "search",
  "locations": [
    "united-kingdom"
  ],
  "urls": [
    "https://www.loopnet.com/search/commercial-real-estate/los-angeles-ca/for-lease/",
    "https://www.loopnet.com/search/commercial-real-estate/los-angeles-ca/for-lease/2/",
    "https://www.loopnet.com/search/commercial-real-estate/usa/auctions/",
    "https://www.loopnet.com/search/office-space/los-angeles-ca/for-lease/",
    "https://www.loopnet.com/search/commercial-real-estate/los-angeles-ca/for-sale/?sk=x&sb=1",
    "https://www.loopnet.com/search/commercial-real-estate/usa/for-sale/",
    "https://www.loopnet.com/Listing/11340-W-Olympic-Blvd-Los-Angeles-CA/29122043/",
    "https://www.loopnet.co.uk/search/commercial-property/london-england--united-kingdom/for-rent/",
    "https://www.loopnet.co.uk/search/commercial-property/london-england--united-kingdom/for-rent/2/",
    "https://www.loopnet.co.uk/search/office-space/london-england--united-kingdom/for-rent/",
    "https://www.loopnet.co.uk/search/commercial-property/united-kingdom/for-sale/",
    "https://www.loopnet.co.uk/listing/10-prestons-rd-london/35346928/"
  ],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/loopnet-scraper --silent --output-dataset

```

## MCP server setup

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