# LoopNet Scraper (`mlg14/loopnet-scraper`) Actor

Scrape LoopNet commercial property search results and listing details. Export addresses, prices, sizes, property types, broker names, photos, and descriptions from US and UK listings.

- **URL**: https://apify.com/mlg14/loopnet-scraper.md
- **Developed by:** [MLG Data](https://apify.com/mlg14) (community)
- **Categories:** Real estate, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$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.

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

Scrape LoopNet commercial real estate listings for sale, lease, and auction, then export the results as JSON or CSV. This LoopNet data extractor accepts filtered search URLs or individual property URLs and provides a practical LoopNet API alternative for public listing research across the United States and the United Kingdom.

The standard run collects one row per property from search results: location, property type, asking price or rent text, size, image, short description, and any broker name shown on the card. Optional listing detail visits add the full marketing description and building attributes where the property page publishes them. A verified run collected 40 Texas search results across two pages, including standard sale listings and auction cards.

### What data can you extract from LoopNet?

Every row uses the same set of field names. Search pages fill the fields visible on listing cards; the detail option fills additional fields from individual property pages. An empty value means the source did not publish that value in the page used for the row.

| Field | Description | Example |
| --- | --- | --- |
| `propertyId` | Stable listing identifier | `40977810` |
| `listingUrl` | Public property URL | `/Listing/4521-State-Highway-121-Plano-TX/40977810/` |
| `title` | Property or marketing headline | A property headline |
| `address` | Street address or site description | `4521 State Highway 121` |
| `city` | City or locality | `Plano` |
| `state` | State or region | `TX` |
| `postalCode` | ZIP or postal code | `75024` |
| `country` | Source country code | `US` |
| `propertyType` | Main property category | `Office` |
| `propertySubType` | Detail-page subtype when available | `Warehouse` |
| `listingType` | `For Sale`, `For Lease`, or `Auction` | `For Sale` |
| `priceText` | Advertised price, rate, or starting bid | `$1,837,500 - $2,112,000` |
| `currency` | Currency inferred from the shown symbol | `USD` |
| `sizeFormatted` | Size line shown on the card | `3,500 - 3,840 SF Condo Units` |
| `totalSize` | Building area from detail attributes, in square feet | `98544` |
| `lotSize` | Lot area with its displayed unit | `4.95 AC` |
| `buildingClass` | Building grade when stated | `B` |
| `yearBuilt` | Construction year when stated | `2005` |
| `capRate` | Capitalization rate text when shown | `5.47% Cap Rate` |
| `stats` | Other short facts displayed on a result card | `12 Units Available` |
| `summary` | Brief search-result description | `Standalone luxury office condos...` |
| `description` | Full description from the listing page | `The property is located...` |
| `photo` | Primary property image URL | `https://images1.loopnet.com/...` |
| `photos` | Image URLs exposed by the parsed page | Primary image URL in a list |
| `brokerName` | Publicly displayed listing broker | Present where shown |
| `brokerCompany` | Publicly displayed brokerage | Present where shown |
| `latitude` | Detail-page coordinate when embedded | `33.065662` |
| `longitude` | Detail-page coordinate when embedded | `-96.838581` |
| `dateModified` | Modification date in listing structured data | `2026-09-24` |
| `searchUrl` | Input search URL that found the property | A search page URL |
| `scrapedAt` | UTC time the row was created | ISO 8601 timestamp |

`priceText` deliberately remains text. A sale card may show a range, a lease card may quote a rate per square foot per year, and an auction card may show a starting bid. Turning all three into one numeric price would conceal material differences. Likewise, `sizeFormatted` preserves the context on the card: available area, a unit range, or total building area are different measures. Use `totalSize` only when a detail page explicitly identifies a building-area figure.

### How to scrape LoopNet

1. Open a public LoopNet search for the property type, location, and sale or lease category you need. Copy that search URL into `startUrls`. You can also paste one or more individual `/Listing/` URLs.
2. Set `maxItems` to the number of unique properties you need. For a short check, use 25. For a two-page sample, use 40 or 50. Set `maxPages` if you want to stop before the complete search has been visited.
3. Turn on `includeListingDetails` when you need full descriptions or building attributes. Each result then needs an additional listing-page request, so a summary-only run is faster.
4. Run the actor. The default dataset receives one item per unique property. Download its JSON or CSV export when the run finishes.

If you do not have a search URL, leave `startUrls` empty. Choose `country`, enter a two-letter `state` for a US state search if desired, and select `listingType`. This constructs a broad commercial-property search. A copied search URL is preferable for city, neighborhood, and property-type filters because it carries the exact route selected on the site.

### Input

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `startUrls` | Array of strings | Empty | Public LoopNet search or listing URLs. Multiple searches can be mixed in one run. |
| `country` | `US` or `GB` | `US` | Country for the built-in search when no URL is provided. |
| `state` | String | Empty | Two-letter US state code for the built-in search; empty means the full US search. |
| `listingType` | `for-sale` or `for-lease` | `for-sale` | Transaction route for the built-in search. Ignored when URLs are provided. |
| `maxItems` | Integer | `100` | Total number of unique rows across all input URLs. `0` removes this item cap. |
| `maxPages` | Integer | `30` | Maximum result pages per search, from 1 to 30. |
| `includeListingDetails` | Boolean | `false` | Visit each property page for its full description and other available attributes. |
| `proxyConfiguration` | Object | Default proxy pool | Connection settings. Leave the default unless your account requires a different setup. |

A realistic two-page sale search:

```json
{
  "startUrls": [
    "https://www.loopnet.com/search/commercial-real-estate/tx/for-sale/"
  ],
  "maxItems": 40,
  "maxPages": 2,
  "includeListingDetails": false
}
```

For a UK search without a pasted URL, set `country` to `GB`, leave `startUrls` empty, and choose the transaction type. An individual UK property URL can also be passed directly. When working with a narrow market or a specialized property class, first apply the filters on the public site and copy the resulting search URL. This prevents a broad national search from crowding out the inventory you actually need.

### Output example

This shortened item comes from the verified Texas search run. The live record also contains the remaining fields in the table above, including nulls for detail-only values because enrichment was off.

```json
{
  "propertyId": "40977810",
  "listingUrl": "https://www.loopnet.com/Listing/4521-State-Highway-121-Plano-TX/40977810/",
  "address": "4521 State Highway 121",
  "city": "Plano",
  "state": "TX",
  "postalCode": "75024",
  "country": "US",
  "propertyType": "Office",
  "listingType": "For Sale",
  "priceText": "$1,837,500 - $2,112,000",
  "currency": "USD",
  "sizeFormatted": "3,500 - 3,840 SF Condo Units",
  "stats": ["$1,837,500 - $2,112,000", "12 Units Available", "Office Condo"],
  "photo": "https://images1.loopnet.com/i2/sm7HucJxKgkyoyf3UbotLjiQuOfRLEhd9V4zUoP6DmI/117/office-property-for-sale-4521-state-highway-121-plano-tx-75024.png"
}
```

The dataset is flat so it can be sorted, filtered, and joined by `propertyId` without unpacking nested objects. `listingUrl` remains the public URL even though the actor may use a different site host to retrieve the page. Repeated properties appearing in multiple input searches are emitted once per run. The `searchUrl` field records the search that first supplied the listing.

### Use cases

- **Market inventory review:** collect sale and lease listings for a state or a copied city search, then compare advertised prices and available area across property types.
- **Property sourcing:** assemble a shortlist of office, retail, industrial, land, or specialty properties and retain the original listing URL for follow-up research.
- **Comparable research:** export the current asking terms, size text, address, and property type for a defined search. Use the displayed units and transaction type when comparing rows.
- **Auction discovery:** capture auction cards and their starting bid text alongside ordinary sale results. The `listingType` field marks them as `Auction` so they can be reviewed separately.
- **Geographic coverage checks:** run several narrower search URLs and deduplicate by listing identifier to measure how much inventory appears across adjacent areas.
- **Listing change workflows:** schedule a repeated search and compare saved exports by `propertyId`, `priceText`, and `dateModified`. A changed asking range can then be reviewed against the live listing.

These are public marketing listings, so they work best as a discovery and monitoring feed. An advertised asking price is not a closing price. An auction starting bid is not the expected final sale amount. A result card may also describe one unit within a development rather than the complete property. Keep these distinctions when using the data in analysis.

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

The configured result charge is **$1.20 per 1,000 dataset items**, or $0.0012 per delivered property. The charge follows the number of rows pushed, not the number of searches entered. At that rate, 40 results cost about **$0.048**, 500 results cost **$0.60**, and 2,000 results across several searches cost **$2.40** in result charges. Any separate proxy or account usage depends on the run configuration.

Set `maxItems` before a broad run so the result charge has a clear ceiling. A 100-item cap corresponds to $0.12 in item charges if 100 rows are delivered. If the site returns fewer matches, the actor pushes only the rows it finds. A listing that appears in two searches is charged once because the run deduplicates by `propertyId`.

Detail enrichment does not create extra dataset rows, but it makes one more property-page request for each result. That increases runtime and may increase network usage under your account settings. Summary-only mode is the practical choice when you need broad inventory, prices, and basic location fields. Use detail mode for a smaller, well-filtered shortlist when descriptions or building attributes justify the extra requests.

### Tips for best results

Use the exact search URL after applying filters on the site. LoopNet encodes the market, property category, transaction type, and sometimes additional criteria in that route. Passing the URL is more precise than entering a state alone. Confirm that it opens a results page and not a saved-search account page or an unrelated article.

For larger markets, split the work into distinct searches by state, city, property class, or transaction type. The site currently limits a single search to 750 visible results across 30 pages. Several narrower URLs can surface inventory that a broad search does not expose within that cap. The actor deduplicates overlapping properties in the same run. There is no automatic price-band splitting, so plan the slices you want before starting a large export.

Keep `includeListingDetails` off until you know that the summary fields are sufficient. Search cards already include a useful address, property category, size line, asking text, image, and sometimes a broker card. Detail mode is better suited to a limited number of properties because it adds an extra request per row and detail pages vary in what they publish.

Review `listingType` before comparing prices. A sale asking range, a lease rate, and an auction starting bid are not interchangeable. `currency` follows the symbol displayed on the page and can reflect the site's display settings rather than the property's country. Retain the original `priceText` and `sizeFormatted` strings when exporting to a spreadsheet so units and ranges remain visible.

If a geographic URL redirects, retry with the canonical route shown in your browser. Some UK city routes normalize their place name before returning results. Country-wide UK searches and direct UK listing URLs were reachable during development; a city URL with an outdated slug may need its current canonical form.

### Limits

A public search page currently shows 25 cards, and one search caps at 30 pages or 750 results. The actor stops at that boundary even when `maxItems` is higher. More inventory requires narrower searches supplied as separate start URLs. Search results can change order between runs, especially when featured listings or auctions move, so a page-number sample is a snapshot rather than a permanent catalog.

Some cards omit price, size, broker, or location details. A price shown as “upon request” is kept as text. Auction rows use the displayed starting bid, which can change. Detail pages publish different structured attributes by property, country, and listing class. `totalSize`, `yearBuilt`, coordinates, and the full description remain empty when the page does not expose them or when a detail request cannot be completed. The `photos` field currently contains the primary image exposed by the parsed page; it is not a promise to return every gallery image.

The public search service can challenge automated traffic. During development, ordinary page URLs returned a traffic challenge while an alternate public host served the same rendered search results. The actor recognizes a challenge response and retries with a rotated session. Access rules may change, so check the run log and dataset after any long pause between runs. The actor does not use account-only listings or require a login.

A direct listing URL yields one property row. Search URLs yield only cards visible to an anonymous visitor. Broker names and brokerage labels are limited to what the public listing itself displays; this actor does not compile a separate broker directory or infer contact details that are absent from the page.

### Use in automated workflows

An automated workflow can submit a search URL and a row limit with a request such as “Collect the first 100 public industrial properties for sale in this filtered market and return addresses, asking text, size, and listing links.” It can also submit several search URLs with “Combine these three neighboring markets, remove duplicate property IDs, and flag auctions before comparing asking terms.” Both requests map directly to the documented input fields and the flat dataset columns.

For recurring checks, save the same input and schedule it at a cadence that matches your research. Compare each new export with the previous one using `propertyId` as the key. Rows missing from a later sample may have sold, been removed, moved beyond a page cap, or changed rank; verify the live listing before treating a disappearance as a completed transaction.

### FAQ

#### Is it legal to collect these listings?

The actor reads public property marketing pages. Laws, site terms, and permitted use vary by place and purpose. Review the applicable terms and privacy rules, collect only what you need, and avoid misuse of any personal details shown on a public broker card. The output is a research feed, not a license to republish listing materials without permission.

#### Do I need to provide a proxy?

The default connection settings work for the verified sample. The actor begins with a normal session and can rotate to a residential session when a page is blocked. You can leave `proxyConfiguration` unchanged for an initial run. If a site block persists, the run log will show the failed source URL.

#### How fast is a run?

Summary mode needs roughly one page request per 25 listings. The verified 40-item, two-page run completed successfully in a few seconds after the container started. Detail mode adds one request for every property, and response times can vary with the site's traffic checks and your proxy settings.

#### Can I run it on a schedule and monitor changes?

Yes. Schedule the same input and compare successive datasets by `propertyId`. `priceText`, `dateModified`, and `scrapedAt` help you inspect changes, but the actor itself does not emit only changed rows or maintain a cross-run history. Store a previous export if you need a historical comparison.

#### Can I export the results to a spreadsheet?

Yes. Download the default dataset as CSV, or use JSON if you need the `stats` and `photos` arrays intact. The flat columns make spreadsheet filtering straightforward. Keep the text price and size columns alongside any numeric fields to preserve ranges and units.

#### Why is a field empty?

The listing may not publish it, the search card may show only a shortened view, or the optional detail request may have failed. Enable `includeListingDetails` for a narrow search when you need deeper attributes. Check the public listing page if a particular property is important, because availability and marketing content change.

#### Why did a broad search return fewer than `maxItems`?

`maxItems` is a ceiling, not a guaranteed count. The public search may have fewer matches, pagination may end, or the 750-result cap may stop one search. Add narrower URLs for additional inventory. If zero rows appear, inspect the run log for a redirect, challenge, or unsupported input URL.

### Integrations

Each run writes structured rows to the default dataset. Use its API endpoint, scheduled runs, webhooks, or a workflow service to deliver the JSON or CSV export to a database, spreadsheet, internal report, or alert pipeline. The stable `propertyId` and `listingUrl` fields make it straightforward to join the current export with earlier snapshots or with your own property records.

### Support

Open an issue on the Issues tab; we reply within 24h and add fields on request.

# Actor input Schema

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

Public loopnet.com search URLs or individual /Listing/ URLs. Search URLs can include property type, location, and transaction filters; leave empty to use the location fields below.

## `country` (type: `string`):

Build a broad search for US or UK commercial properties when startUrls is empty.

## `state` (type: `string`):

Two-letter US state code such as TX or CA. Leave empty to search across the United States.

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

Choose sale or lease listings for the built-in location search. A start URL takes precedence.

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

Hard cap on unique rows across all input URLs. Set 0 for no item cap; a single search still exposes at most 750 results.

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

How many 25-result pages to visit for each search URL, up to the site's 30-page limit.

## `includeListingDetails` (type: `boolean`):

Enrich each search row with the listing description, location, building attributes, and coordinates where available. This adds one request per property.

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

Proxy settings for requests. The actor starts with the default pool and rotates to a residential session if pages are blocked.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.loopnet.com/search/commercial-real-estate/tx/for-sale/"
  ],
  "country": "US",
  "state": "",
  "listingType": "for-sale",
  "maxItems": 100,
  "maxPages": 30,
  "includeListingDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All scraped items in the default dataset.

# 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 = {
    "startUrls": [
        "https://www.loopnet.com/search/commercial-real-estate/tx/for-sale/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/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 = { "startUrls": ["https://www.loopnet.com/search/commercial-real-estate/tx/for-sale/"] }

# Run the Actor and wait for it to finish
run = client.actor("mlg14/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 '{
  "startUrls": [
    "https://www.loopnet.com/search/commercial-real-estate/tx/for-sale/"
  ]
}' |
apify call mlg14/loopnet-scraper --silent --output-dataset

```

## MCP server setup

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