# LoopNet API — live US commercial real estate scraper: search offices, retail, industrial, land, multifamily and shopping centres for sale, for lease or at auction by market, submarket, county, ZIP or address, filtered by price, rent, building and lot size, year built, cap rate and unit count, plus full listing detail with the asking figure, property facts, available spaces, coordinates, photos and the broker and brokerage — no API key, no login

> Commercial listings in a market. Give a `location` ('Austin, TX', '78704', 'Downtown Austin') or a `location_id` from the `location_search` action, and pick a `transaction` (for_lease, for_sale or auction). Every filter offered here was proven to narrow the result set against an unfiltered control; the ones LoopNet accepts and ignores are deliberately not offered. `total_results` is the source's own count and saturates at 750 — `total_is_capped` tells you when it did.
> ReefAPI engine `loopnet` · 4 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/loopnet/v1/<action>` with a JSON body.
- **Auth:** header `x-api-key: <YOUR_REEFAPI_KEY>` — create one free (1,000 credits, no card): https://reefapi.com/signup
- **Response (every call):** `{ ok: boolean, data: ..., meta: { record_count, credits, ... }, error: { code, message } }` — branch on `ok`. Failed calls are free except verified SHEIN NOT_FOUND on product/detail and price (4 credits).
- **One key + one shared credit pool** across every ReefAPI API. Per-call credits are listed on each endpoint below.
- **Use it from an AI agent (MCP):** connect `https://api.reefapi.com/mcp` (remote streamable-http). Send the key as `Authorization: Bearer <key>`, or put it in the URL (`?key=<key>`) when the client has no header field, as ChatGPT does.

## Endpoints

### POST https://api.reefapi.com/loopnet/v1/search — 2 credits
Commercial listings in a market. Give a `location` ('Austin, TX', '78704', 'Downtown Austin') or a `location_id` from the `location_search` action, and pick a `transaction` (for_lease, for_sale or auction). Every filter offered here was proven to narrow the result set against an unfiltered control; the ones LoopNet accepts and ignores are deliberately not offered. `total_results` is the source's own count and saturates at 750 — `total_is_capped` tells you when it did.

**Parameters:**
- `location` (string, optional) — Where to search: a US market ('Austin, TX'), a LoopNet submarket ('CBD - Austin, TX'), a county ('Travis County, TX'), a state ('Texas'), a ZIP code ('78704'), a street address, or 'usa' for a nationwide search. Resolved through LoopNet's own geography service, which recognises ITS submarket names rather than every local nickname — run the `location_search` action first to see what it matches, and pass the `location_id` of the one you want. An address is widened by LoopNet to the area around it, and the response says so.
- `location_id` (string, optional) — A geography id from the `location_search` action, used ALONGSIDE `location` to pick which of the matches to search. LoopNet's geography service is searched by text, so the text is still required; the id only decides which match wins (e.g. location='Austin, TX' with location_id='251' searches the CBD submarket).
- `transaction` (enum, optional, default "for_lease") — Which side of the market to search. The filters available differ between them — rent, space size and lease type only exist for lease; price, cap rate, unit count, lot size and year built only exist for sale. [one of: for_lease, for_sale, auction]
- `property_type` (array, optional) — Property types to keep, from LoopNet's own category tree. For lease: office, industrial, retail, flex, medical, restaurant, lab, land, coworking. For sale: office, industrial, retail, shopping-center, multifamily, specialty, health-care, hospitality, sports-entertainment, land, residential-income, restaurant, lab. A type that does not exist on the chosen side is rejected rather than ignored.
- `page` (integer, optional, default 1) — Which page of 25 listings to return. The source serves 25 per page and will page through at most 30.
- `limit` (integer, optional, default 25) — How many listings to return, up to 100. The source serves 25 per page and each page is a large document, so a bigger limit costs one upstream page per 25. Use `page` to walk further — the source itself stops at page 30. `all_listing_ids` already returns every id in the result set (up to 750) in the FIRST call, so paging is only needed for the rows.
- `sort` (enum, optional, default "default") — Result order, applied BY THE SOURCE. Only the orders whose effect could be verified against the published figures are offered here. [one of: default, price_low_to_high, price_high_to_low]
- `min_price` (integer, optional) — Lowest asking SALE price in USD. For-sale searches only — LoopNet has no price filter on the lease side.
- `max_price` (integer, optional) — Highest asking SALE price in USD. For-sale searches only.
- `min_rent` (number, optional) — Lowest asking rent, in dollars per square foot per year. FOR-LEASE searches only. Listings that publish no rent (very common — 14 of 25 rows in the measured Austin page) cannot match a rent filter.
- `max_rent` (number, optional) — Highest asking rent in dollars per square foot per year. For-lease searches only.
- `min_building_size` (integer, optional) — Smallest building size, in `size_unit`.
- `max_building_size` (integer, optional) — Largest building size, in `size_unit`.
- `size_unit` (enum, optional, default "sf") — Unit for the building and space-size filters. [one of: sf, sm]
- `min_space_available` (integer, optional) — Smallest available space, in `size_unit`. FOR-LEASE searches only — on the sale side LoopNet accepts this and does not apply it, so it is not sent.
- `max_space_available` (integer, optional) — Largest available space, in `size_unit`. For-lease only.
- `min_lot_size` (number, optional) — Smallest lot size, in `lot_size_unit`. FOR-SALE searches only.
- `max_lot_size` (number, optional) — Largest lot size, in `lot_size_unit`. For-sale only.
- `lot_size_unit` (enum, optional, default "ac") — Unit for the lot-size filter. [one of: ac, ha, sf]
- `min_year_built` (integer, optional) — Earliest year built. FOR-SALE searches only — the lease side accepts the value and ignores it.
- `max_year_built` (integer, optional) — Latest year built. For-sale only.
- `min_cap_rate` (number, optional) — Lowest capitalisation rate, in percent. For-sale only.
- `max_cap_rate` (number, optional) — Highest capitalisation rate, in percent. For-sale only.
- `min_units` (integer, optional) — Fewest units (multifamily and similar). For-sale only.
- `max_units` (integer, optional) — Most units. For-sale only.
- `keywords` (string, optional) — Free-text keyword the listing must match. Proven to narrow (750 → 227 for 'warehouse'); a keyword with no matches returns an empty result set, not an error.
- `sublease_only` (boolean, optional, default false) — Keep only sublease listings. For-lease searches only.
- `net_leased_only` (boolean, optional, default false) — Keep only net-leased investments. For-sale only.

**Returns:** results[]{listing_id, property_id, url, name, address, city, state, postal_code, country, property_type, space_use, transaction, year_built, size{display,min,max,unit}, price{display,shape,value_min,value_max,currency,rate_term}, description, broker, brokerage, images[], exposure_level}, total_results, total_is_capped, all_listing_ids[], page, limit, location{…}, filters_applied

**Example request body:**
```json
{
  "location": "Austin, TX",
  "transaction": "for_lease",
  "property_type": [
    "office"
  ],
  "limit": 25
}
```

### POST https://api.reefapi.com/loopnet/v1/property_detail — 2 credits
One listing by id or URL: the asking figure with its SHAPE, property type and subtype, building and lot size, year built and renovated, units, cap rate, NOI where published, zoning, address and coordinates, photos, the marketing description, every available space with its own size, term and rate, and the brokers with their firms. `detail_level` says whether the deep record came back ('full') or only the listing-card record ('placard') — see the API notes.

**Parameters:**
- `listing_id` (string, required) — The LoopNet listing id (the number at the end of the listing URL) or the full listing URL.

**Returns:** property{listing_id, url, name, description, transaction, price{…}, property_type, property_subtype, building_class, building_size{…}, space_available{…}, lot_size{…}, floors, year_built, year_renovated, units, cap_rate, noi, price_per_sf, zoning, parking, amenities[], address{street,city,state,postal_code,country}, latitude, longitude, images[], brokers[]{name,brokerage,city,state,profile_url}, spaces[]{space,size,term,space_use,available,rate}, last_updated, detail_level}

**Example request body:**
```json
{
  "listing_id": "21695896"
}
```

### POST https://api.reefapi.com/loopnet/v1/listings — 1 credit
Up to 30 listing ids in ONE upstream call. Each row carries the address, city, state, ZIP, property type, the asking figure with its shape and the lead photo — the compact record, not the deep one. Use it to price or re-check a saved set of ids; use `property_detail` when you need the facts table, the spaces or the coordinates.

**Parameters:**
- `listing_ids` (array, required) — Listing ids or listing URLs, up to 30. Ids the source does not return are listed in `missing` rather than silently dropped.

**Returns:** results[]{listing_id, property_id, url, address, city, state, postal_code, country, property_type, transaction, price{…}, image, exposure_level}, requested, returned, missing[]

**Example request body:**
```json
{
  "listing_ids": [
    "21695896",
    "4036830"
  ]
}
```

### POST https://api.reefapi.com/loopnet/v1/location_search — 1 credit
Turn a typed place into the geography LoopNet's search takes: the id, the type (state, city, postal_code, submarket, county, country — or property/address/building for a single site), the coordinates and the bounding box. `searchable` says whether a search can be scoped to that row: only the area types carry a bounding box. Pass a row's `location_id` back to `search` alongside the same `location` text to pick that match instead of the top one.

**Parameters:**
- `query` (string, required) — What was typed: a city, a county, a ZIP code, a neighbourhood or a street address.
- `limit` (integer, optional, default 12) — How many matches to return.

**Returns:** locations[]{location_id, display, display_long, type, city, state, county, country, postal_code, latitude, longitude, bounding_box{…}, searchable}

**Example request body:**
```json
{
  "query": "austin"
}
```

## At scale
- **Volume:** 5M+ requests a day, measured at 60 requests a second across the fleet with no
  central bottleneck. Per-key limits are raised for high-volume accounts; volume pricing on request.
- **Missing a source:** tell us a site we do not cover and it becomes an engine. A customer asked
  for bestprice.gr on 21 Sep 2026 and it was in the catalog on 22 Sep.
- **Support:** 2 minute median time from a question in the live chat to the first answer. Setup
  help included, no support tier to buy.
- **One key, one credit pool** across every API. No per-site plans, no separate subscriptions.

## More
- Try it live, no code: https://reefapi.com/playground?engine=loopnet
- Human docs page: https://reefapi.com/docs/loopnet
- Overview page: https://reefapi.com/loopnet-api
- Every ReefAPI API in one file (for your AI): https://reefapi.com/llms-full.txt
