# IronPlanet Scraper - Heavy Equipment Lots & Serials (`scrapersdelight/ironplanet-lot-scraper`) Actor

From $1.30 per 1,000 rows, no start fee. Every IronPlanet and Marketplace-E lot: year, make, model, meter reading, price and whether it is a bid or an ask, IronClad flag, inspection location with coordinates, sale date, features, serial or VIN. 17,178 counted; GovPlanet and Ritchie Bros excluded.

- **URL**: https://apify.com/scrapersdelight/ironplanet-lot-scraper.md
- **Developed by:** [Scrapers Delight](https://apify.com/scrapersdelight) (community)
- **Categories:** E-commerce, Business, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.30 / 1,000 per lot returneds

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

## 🏗️ IronPlanet Scraper — heavy equipment lots, meters, serials & locations

Every lot on **IronPlanet** and **Marketplace-E**, as clean rows: year, make, model, meter reading,
the price *and what that price actually is*, IronClad Assurance flag, the yard it sits in with
coordinates, the sale date, the features list — and, optionally, the **serial number or VIN** that
the search page never shows.

**From $1.30 per 1,000 rows. No run-start fee. You pay only for rows delivered.**

> **Not affiliated with IronPlanet, Ritchie Bros., RB Global or Marketplace-E.** Public pages only,
> no login, no account. See [Source & legal](#-source--legal).

***

### 🔍 What does this Actor do?

It walks IronPlanet's own search index and returns one row per lot. Everything you can filter on
is a **server-side facet** on ironplanet.com — make, US state or Canadian province, model-year
band, price band, IronClad flag, buying format, free text — so a narrow run fetches a narrow
result set and you are never charged for rows you filtered out afterwards.

**Measured on a complete live sweep, 2026-09-19:**

| | |
|---|---|
| IronPlanet lots | **4,328** |
| Marketplace-E lots | **12,851** |
| Total delivered in one run | **17,179** |
| Coverage against IronPlanet's own last-page count | **100.2%** / **100.1%** |
| Pages fetched | 288 |
| Duplicate rows delivered | **0** (2 cross-partition duplicates caught and skipped) |
| Parse failures | **0** |
| Transport failures | **0** |
| Rows delivered vs rows charged | **17,179 / 17,179 — exact** |

### 📊 What you get per lot

🏷️ `itemId` · `itemNumber` — IronPlanet's own item number
🚜 `title` · `year` · `make` · `model` · `modelAndType`
🏪 `marketplace` — `IronPlanet` or `Marketplace-E` · `marketplaceCode` · `sourceRealmId`
🔨 `buyingFormat` — Online Auction · Buy Now · Make Offer · Current High Offer
🚦 `listingStatus` · `listingStatusCode` — Bidding Open · Buy Now / Make Offer · Coming Soon
💵 `price` · `priceText` · `currency` — USD, CAD, NZD, EUR as published
🧾 **`priceBasis`** — *current bid* · *opening bid* · *Make Offer* · *Buy Now*. The same number
means different things to an appraiser, so the row says which one it is.
🛒 `buyNowAvailable`
⏱️ `meterReading` · `meterUnit` (hours / miles / kilometers) · `meterReadingText` (verbatim,
including IronPlanet's own `See Report`)
🛡️ `ironCladAssurance` — IronPlanet's inspection guarantee
📍 `location` · `country` · `latitude` · `longitude` — where the machine sits for inspection
🗓️ `saleDate` (ISO) · `saleDateText`
🧰 `features` — the full equipped-with list
📝 `catalogNotes` — transaction-fee and tax notes IronPlanet attaches to the lot
🔖 `consignmentRef` — IronPlanet's consignment/registration reference
🖼️ `imageUrl` · `thumbnailUrl`
🔗 `url` — the canonical ironplanet.com item page
🧭 `sourcePartition` · `sourceUrl` · `scrapedAt` — provenance on every single row

#### With **Fetch item pages** on, twelve more fields

🔩 **`serialNumber`** · `serialNumberLabel` (`Serial #` or `VIN`) · **`serialNumberMasked`**
⏱️ `meterReadingExact` — e.g. `7,477 Hours`, as rendered
👁️ `watcherCount` — how many buyers are watching the lot
➕ `bidIncrement` · `saleEndsAt` — the full auction close **timestamp**, not just the day
🗂️ `categoryPath` — e.g. `Construction > Dozers > Crawler Dozer`
📷 `photoCount`
📌 `locationCity` · `locationRegion` · `locationCountry` — the inspection address, split out
📋 `hasInspectionReport`

A listings-only run does **not** carry those twelve keys at all — they are absent, not present-and-null.

### 📈 Measured field fill (not estimates)

From the **4,328-row IronPlanet sweep**, listings mode:

| field | fill | | field | fill |
|---|---|---|---|---|
| `itemId`, `url`, `marketplace`, `buyingFormat`, `currency`, `priceBasis`, `location`, `country`, `saleDate` | **100%** | | `consignmentRef` | 64% |
| `title` | 100% | | `price` | 52% |
| `modelAndType` | 98% | | `features` | 50% |
| `latitude` / `longitude` | 95% | | `meterReading` | 39% |
| `listingStatus` | 75% | | `catalogNotes` | 24% |
| `make` | 71% | | | |
| `model` / `year` | 69% / 65% | | | |
| `imageUrl` | 65% | | | |

From a **120-row item-page run**: `categoryPath`, `locationCity/Region/Country`, `bidIncrement`,
`hasInspectionReport` **100%** · `photoCount` 53% · `saleEndsAt` 45% · `watcherCount` 33% ·
`serialNumberLabel` 25% · **`serialNumber` 22%** · `meterReadingExact` 14%.

Those are the numbers, not rounder ones. IronPlanet does not publish a price for a lot that has
not opened, a meter reading for an attachment, or a serial for every machine — and this Actor
returns `null` rather than a plausible zero.

### ⚙️ Input

| field | type | default | what it does |
|---|---|---|---|
| `marketplaces` | array | `["IronPlanet","Marketplace-E"]` | Which ironplanet.com marketplaces to collect |
| `make` | string | — | Manufacturer, server-side (`Cat`, `John Deere`, `Komatsu`, `Genie`, `JLG`…) |
| `location` | string | — | `USA-TX`, `USA-CA`, `CAN-AB`… IronPlanet's own location code, server-side |
| `yearBand` | enum | — | `2020-2024` · `2015-2019` · `2010-2014` · `2000-2009` · `before-2000` |
| `priceBand` | enum | — | Seven USD bands, server-side |
| `buyingFormat` | enum | — | `online-auction` or `buy-now` |
| `ironCladOnly` | boolean | `false` | Only IronClad-inspected lots (2,750 of 4,328 carry it) |
| `keyword` | string | — | Free text, run through IronPlanet's own search |
| `sort` | enum | `sale-date-asc` | The four orderings IronPlanet sorts deterministically |
| `includeDetails` | boolean | `false` | Fetch each item page for the serial/VIN and the other 11 fields |
| `maxItems` | integer | `200` | Your budget cap — you are charged per delivered row |
| `maxPages` | integer | `0` | Optional hard stop on result pages per marketplace |
| `pageConcurrency` | integer | `3` | Result pages in flight (max 4) |
| `detailConcurrency` | integer | `5` | Item pages in flight |
| `monitorMode` | boolean | `false` | Emit only new lots and price changes, tagged `changeType` |
| `proxyConfiguration` | object | Apify datacenter | Required — see below |

```json
{
  "marketplaces": ["IronPlanet"],
  "make": "Cat",
  "location": "USA-TX",
  "ironCladOnly": true,
  "includeDetails": true,
  "maxItems": 200
}
```

### 👥 Who uses it

- **Used-equipment dealers and brokers** — what is coming to auction, where, with hours and serials
- **Equipment lenders and appraisers** — year/make/model/hours comps with a source link per row
- **Valuation data vendors** — a clean, deduplicated, provenance-tagged feed
- **Fleet managers and exporters** — filter by state, then use `latitude`/`longitude` for haulage
- **Market analysts** — `monitorMode` on a schedule emits only new lots and price moves

### 💰 Pricing

| | |
|---|---|
| Per delivered row | **$0.0013** ($1.30 per 1,000) |
| Run-start fee | **none** |
| Apify auto-events | **removed** (both of them) |

You are charged **once per row delivered to your dataset**. A row skipped as a duplicate, a
sibling-marketplace row filtered out, or a page that could not be reached is **never billed** —
the charge and the push happen together, so the dataset and the invoice cannot disagree. Measured
on the live runs above: **delivered == charged, exactly, on every run.**

The price is the same whether or not you turn item pages on.

### 🚧 Honest limits — please read before you buy

**1. There are no hammer prices, and nobody else has them either.**
IronPlanet publishes sold prices only inside its **account-gated auction-results view**. Requesting
it (`sm=1`) redirects to `login-form.jsp` on plain HTTP, through the Apify datacenter, residential
and UNBLOCKER pools, and in a real Chromium — **headless and headful**. That is an account gate,
not a bot wall, and we will not scrape behind a login. What you get instead is the **live current
bid or asking price**, with `priceBasis` saying which. Every same-site rival on the Apify Store is
in the same position; one is even *titled* "Auction Results: Price Comps" while its own README
defines its price field as "Current bid, Opening price, or Buy Now".

**2. There is no consignor name, phone or email.** IronPlanet consignors are anonymous by
marketplace convention. What is published is the **item's location for inspection**, which this
Actor returns down to city and state. This is a **valuation and comps product, not a leads
product.** (If you have seen a statutory-contact claim attached to RB Global lots: that applies to
the **GovPlanet** federal-surplus corpus, a different site, and it is **not** true of IronPlanet's
commercial lots.)

**3. On-road vehicle VINs are MASKED by IronPlanet.** An equipment serial comes through whole
(`CAT00D6TVGCT03575`); a truck VIN arrives as `5PVNV8JP1K4SXXXXX`, with the last five characters
replaced by X at source. `serialNumberMasked` tells you which you got, per row.

**4. A serial is published on about a fifth of lots** (22% measured on a 120-row item-page run).
Item pages cost roughly 50× the bytes of a search page, so `includeDetails` is off by default —
the price is the same either way, but the run takes longer.

**5. IronPlanet's default result order is not stable.** Two consecutive requests for the same page
returned 55 of the same 60 lots, and a deep sweep under it lost a quarter of the catalogue. This
Actor always pins one of the four orderings IronPlanet *does* sort deterministically, and pins one
proxy session for a whole sweep — which is how the run above reached 100% of the site's own count
with zero duplicates. If you call the site yourself, this is the trap.

**6. A proxy is required.** ironplanet.com sits behind an AWS WAF. The Actor ships with the Apify
datacenter pool pinned as a schema default, so an empty input still works.

### 🚫 What this Actor deliberately does **not** return

ironplanet.com's search index also syndicates four sibling RB Global marketplaces. On a single
unfiltered page IronPlanet's own facet counted them at **GovPlanet 3,924 · Ritchie Bros.
Auctioneers 92,736 · TruckPlanet 2,849 · SalvageSale 508** — nearly 100,000 lots that are *not*
IronPlanet.

This Actor **excludes all four**, by partitioning on IronPlanet's own `Marketplaces` facet and then
re-checking every row, and it fails the run rather than letting one through. They are counted in
`RUN_SUMMARY` so you can see the exclusion happening. Those catalogues belong to their own sites and
their own scrapers; you are not paying us for them twice under an IronPlanet name.

### 📦 Run summary

Every run writes `RUN_SUMMARY` to the key-value store: each partition with IronPlanet's own
last-page count beside the rows actually parsed and the coverage percentage, the per-marketplace
facet counts, sibling rows excluded, duplicates skipped, parse failures, transport failures split
from account-gated redirects, proxy-session rotations, which path shape served each page, and bytes
fetched. If a run comes back short, the summary says why.

### ❓ FAQ

**Does this need an IronPlanet account?** No. Public pages only, no login, no cookies from a
signed-in session.

**Why is there no sold price?** IronPlanet puts auction results behind an account. See Honest
limits #1 — it is measured, not assumed.

**Is Marketplace-E the same as IronPlanet?** It is IronPlanet's Buy Now / Make Offer marketplace on
the same site, with its own 12,851 lots. Both are on by default; `marketplaces` turns either off.

**Can I get GovPlanet or Ritchie Bros. lots here?** No, by design — see above.

**How do I get serial numbers?** Set `includeDetails: true`. It fetches each lot's item page, where
IronPlanet writes the serial through an obfuscated script; this Actor decodes it.

**How fresh is the data?** Live at run time. `scrapedAt` is on every row.

**Can I track price changes?** Yes — `monitorMode: true` on an Apify Schedule emits only rows that
are new or whose price moved, tagged `changeType`.

**How many rows will a full run return?** 4,328 IronPlanet + 12,851 Marketplace-E = 17,179 as of
2026-09-19. Set `maxItems` to cap your spend.

**Why do some rows have no make?** A make is only filled when it matches IronPlanet's own
259-value make facet. A lot titled "Attachment Mounting Plate" gets `null`, not `make: "Attachment"`.

**What does `See Report` mean in `meterReadingText`?** IronPlanet published that literally: the
meter reading is in the inspection report. It is a different fact from "not published", so it is
kept verbatim and `meterReading` stays null.

**Does the row count match the bill?** Yes, exactly — verified on every run above.

**What is `consignmentRef`?** IronPlanet's own consignment/registration reference for the lot
(64% filled). It is not a contact.

### 📄 Source & legal

Data comes from public pages on `www.ironplanet.com`. No account, no login, no paywall.

`https://www.ironplanet.com/robots.txt`, verbatim, as of 2026-09-18:

```
User-agent: *
Disallow: /files/
Disallow: /jsp/acct/switchLocale.jsp
Disallow: /jsp/acct/my-account.jsp
Disallow: /jsp/acct/reg_login.jsp
Disallow: /jsp/acct/login.jsp
Disallow: /jsp/acct/kcc-sh-reg.jsp
Disallow: /jsp/equip/add-interested.jsp
Disallow: /jsp/equip/view-document.jsp
Disallow: /jsp/find/searchresults.jsp
Disallow: /jsp/category/category.jsp
Disallow: /jsp/share.jsp

Sitemap: https://www.ironplanet.com/sitemap.xml
```

No personal data is collected: consignors are anonymous and no contact field exists to collect.
Equipment serial numbers and item locations are published by IronPlanet on the public item page.
You are responsible for complying with applicable law and the site's terms in your own use of the
data. No warranty is given on accuracy or availability.

# Actor input Schema

## `marketplaces` (type: `array`):

Which ironplanet.com marketplaces to collect. IronPlanet = the weekly online auctions; Marketplace-E = the Buy Now / Make Offer marketplace on the same site. The sibling RB Global marketplaces syndicated into the same search index (GovPlanet, Ritchie Bros. Auctioneers, TruckPlanet, SalvageSale) are hard-excluded and are not offered here - they are separate sites with their own catalogues.

## `make` (type: `string`):

Manufacturer filter, applied server-side by IronPlanet's own Make facet. Use the site's spelling, e.g. Cat, John Deere, Komatsu, Volvo, JLG, Genie, Link-Belt. Leave empty for every make.

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

Location filter in IronPlanet's own code, applied server-side: USA-TX, USA-CA, USA-FL, CAN-AB, CAN-ON and so on. Leave empty for every location.

## `yearBand` (type: `string`):

Model-year band, applied server-side. These five bands are the ones IronPlanet publishes; a free-form year range is not supported by the site.

## `priceBand` (type: `string`):

US-dollar price band, applied server-side. Bands are IronPlanet's own; items priced in CAD, EUR or GBP are not matched by this filter.

## `buyingFormat` (type: `string`):

Restrict to one buying format, applied server-side. Leave empty for both.

## `ironCladOnly` (type: `boolean`):

Keep only lots carrying IronPlanet's IronClad Assurance inspection guarantee. Applied server-side. Measured on the live catalogue: 2,750 of 4,328 IronPlanet lots carry it.

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

Free-text search, applied server-side by IronPlanet's own search box. Narrows the result set before it is fetched, so it costs nothing extra.

## `sort` (type: `string`):

Order IronPlanet returns results in. These four are the only orderings the site sorts deterministically - its default relevance order is unstable between requests, so a deep sweep under it both repeats and silently skips lots. One of these is always applied.

## `pageConcurrency` (type: `integer`):

How many 60-lot result pages to fetch at once. All of them ride one pinned proxy session, because IronPlanet holds the ordered result set per session - capped at 4, which is the highest rate measured returning every row with no duplicates.

## `includeDetails` (type: `boolean`):

Fetch each lot's item page to add the serial number / VIN, the exact meter reading, the watcher count, the auction close timestamp, the full city-and-state inspection location, the bid increment and the category breadcrumb. These are not on the search tile. Item pages are ~430 KB each, so a run with this on is roughly 50x the bytes of one without - the row price is the same either way. Off by default.

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

Stop after this many delivered rows. You are charged per delivered row, so this is your budget cap. 0 is not accepted - use a large number for the whole catalogue (about 4,300 IronPlanet lots and 12,800 Marketplace-E lots as measured on 2026-09-18).

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

Optional hard stop on how many 60-lot result pages to fetch per marketplace, regardless of the row cap. 0 means no page limit.

## `detailConcurrency` (type: `integer`):

How many item pages to fetch at once when 'Fetch item pages' is on. Higher is faster but leans harder on the proxy pool.

## `monitorMode` (type: `boolean`):

Emit only lots that are new since the previous run, or whose price has changed, tagged with changeType. State is kept in a named key-value store so it survives between runs. The first run seeds the state and emits everything it sees.

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

Apify proxy settings. ironplanet.com sits behind an AWS WAF and this Actor needs a proxy to reach it reliably, so the datacenter pool is pinned here as a default rather than a prefill - an empty input still gets it. The last rung of the Actor's fallback ladder additionally uses the UNBLOCKER group when your account has it.

## Actor input object example

```json
{
  "marketplaces": [
    "IronPlanet"
  ],
  "make": "Cat",
  "location": "USA-TX",
  "ironCladOnly": false,
  "keyword": "excavator",
  "sort": "sale-date-asc",
  "pageConcurrency": 3,
  "includeDetails": false,
  "maxItems": 50,
  "maxPages": 0,
  "detailConcurrency": 5,
  "monitorMode": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "BUYPROXIES94952"
    ]
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per IronPlanet or Marketplace-E lot. Machine identity (title, model year, make read against IronPlanet's own 259-value make facet, model, model-and-type). The money (price, currency, the text IronPlanet rendered, and priceBasis saying whether that number is a current bid, an opening bid or an asking price - the same figure means different things to an appraiser). Condition and usage (meter reading with its unit, the verbatim meter text including IronPlanet's 'See Report', IronClad Assurance flag, the features list). The sale (buying format, listing status decoded from IronPlanet's own status codes, sale date, and with item pages on the exact close timestamp and the bid increment). Where it sits for inspection (location, country, latitude and longitude, and with item pages on the city, region and country split out). Identification (the serial number or VIN, decoded from the page's obfuscated writer, with the source's own label and a flag when IronPlanet has masked it). And the provenance of the row: which marketplace partition and which search URL produced it.

## `runSummary` (type: `string`):

RUN\_SUMMARY: the partitions actually walked and what each one declared versus delivered, the per-marketplace totals read from IronPlanet's own Marketplaces facet, how many sibling-marketplace rows (GovPlanet, Ritchie Bros. Auctioneers, TruckPlanet, SalvageSale) were seen and excluded rather than resold under this name, rows delivered which equals rows charged, duplicates skipped, which rung of the path-shape ladder each page came back on, every page that failed and whether it failed as a WAF block or a transport error, and an honest note on why hammer prices are absent.

# 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 = {
    "marketplaces": [
        "IronPlanet"
    ],
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/ironplanet-lot-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 = {
    "marketplaces": ["IronPlanet"],
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/ironplanet-lot-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 '{
  "marketplaces": [
    "IronPlanet"
  ],
  "maxItems": 50
}' |
apify call scrapersdelight/ironplanet-lot-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapersdelight/ironplanet-lot-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/3PkeFmSve3m6iVEHs/builds/7ndVD37n2CrFrhfmZ/openapi.json
