# GSA Auctions Scraper - Federal Surplus Vehicles & Equipment (`groundplanes/gsa-auctions-surplus-lots`) Actor

Live federal surplus lots from the official GSA Auctions API: vehicles, heavy equipment, electronics and more. Lot name, current bid, bidders, auction start and end, location and agency, newest first. Filter by keyword, state, closing date and price. Sales contacts off by default.

- **URL**: https://apify.com/groundplanes/gsa-auctions-surplus-lots.md
- **Developed by:** [Groundplane Labs](https://apify.com/groundplanes) (community)
- **Categories:** Business
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 auction lots

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

## GSA Auctions Scraper - Federal Surplus Vehicles & Equipment

Collect active federal government surplus auction lots from GSA Auctions (`gsaauctions.gov`) via the official GSA Personal Property Management System (PPMS) API. Filter by keyword, property state, closing window, maximum current bid, or federal agency, and automatically extract 17-character VINs and mileage/equipment hours when listed in the lot description.

### Free sample

Download a free sample (5 rows, real actor output, CSV): https://drive.google.com/uc?export=download\&id=1hZisWODcOQnIN2r28-GrH0GiEw8L9Z5v. Check the columns and data quality before you run anything.

### What it does

The U.S. General Services Administration (GSA) sells surplus federal assets—including fleet trucks, sedans, heavy construction equipment, boats, aircraft parts, industrial machinery, and scrap metal contracts—through GSA Auctions.

This Actor:

- Fetches the full active-auctions feed from the official GSA Auctions v2 endpoint (`https://api.gsa.gov/assets/gsaauctions/v2/auctions`).
- Normalizes bid metrics (`currentBidUsd`, `bidIncrementUsd`, `bidderCount`, `reservePrice`), dates (`auctionStart`, `auctionEnd`, `daysUntilEnd`), and property locations (`city`, `state`, `zip`).
- Strips HTML markup from `lotInfo` to produce clean plain-text `description` fields (trimmed to 2,000 characters) and extracts 17-character vehicle `vin` values and odometer/meter `hoursOrMiles` where present.
- Sorts lots either **newest listed first** (`NEWEST`, by `auctionStart` descending) or **closing soonest first** (`ENDING_SOON`, by `auctionEnd` ascending).

### Who it's for

- **Commercial fleet, equipment, and auto resellers** sourcing undervalued federal surplus vehicles, trailers, and machinery.
- **Scrap metal, recycling, and industrial liquidation buyers** monitoring term contracts and bulk surplus lots by state.
- **Marine and aviation brokers** tracking USCG boats, engines, and aerospace surplus.
- **Supply-chain and procurement analysts** benchmarking secondary-market government asset recovery prices.

### Input

- **`keyword`** *(string)* — Case-insensitive keyword matched against `itemName` and `lotInfo` (e.g. `"truck"`, `"ford"`, `"tractor"`, `"boat"`). Default: `""` (all active lots).
- **`states`** *(array of strings)* — Two-letter US state/territory codes where the property is located (e.g. `["CA", "TX", "VA"]`). Default: `[]` (all states).
- **`endingWithinDays`** *(integer)* — Return only auctions closing within this many calendar days from today (`0` = no limit). Default: `0`.
- **`maxCurrentBidUsd`** *(integer)* — Return only lots whose current high bid (or $0 if unbid) is at or below this dollar threshold (`0` = no limit). Default: `0`.
- **`agencyKeyword`** *(string)* — Case-insensitive keyword matched against `agencyName` or `bureauName`. Default: `""`.
- **`sortBy`** *(string enum)* — `"NEWEST"` (`auctionStart` descending) or `"ENDING_SOON"` (`auctionEnd` ascending). Default: `"NEWEST"`.
- **`includeSalesContact`** *(boolean)* — Defaults to `false` so federal contracting officer contact fields (`contractOfficer`, `coEmail`, `coPhone`) are omitted unless explicitly enabled for lot inspection or bidding inquiries.
- **Privacy:** with `includeSalesContact` off (the default), sales-officer emails and phone numbers are also removed from the `description` text.
- **`dataGovApiKey`** *(string, secret)* — Optional free API key from [api.data.gov](https://api.data.gov/signup/). If you enter a key, the Actor uses yours. If you leave this blank, the Actor uses its configured account key when one is attached; otherwise it falls back to the public `DEMO_KEY`, which api.data.gov limits per IP address (about 30 requests per hour and 50 per day), so heavy or scheduled use should add your own free key. The completed run status names which kind of key was used (never the key itself).
- **`maxItems`** *(integer)* — Maximum number of lots to return. Default: `200`.

#### Example input JSON

```json
{
  "keyword": "",
  "states": [],
  "endingWithinDays": 0,
  "maxCurrentBidUsd": 0,
  "agencyKeyword": "",
  "sortBy": "NEWEST",
  "includeSalesContact": false,
  "maxItems": 200
}
```

#### Example run options (default max charge $50)

Set a default maximum charge of **$50** (`"maxTotalChargeUsd": 50`) in your run options:

```json
{
  "maxTotalChargeUsd": 50
}
```

### Output

Results are deduplicated by `lotId` (`saleNo-lotNo`) and sorted by `auctionStart` descending (or `auctionEnd` ascending when `sortBy` is `"ENDING_SOON"`).

| Field | Type | Description |
|---|---|---|
| `lotId` | `string` | Unique lot identifier (`saleNo-lotNo`). |
| `saleNo` | `string` | GSA sale number. |
| `lotNo` | `string` | Lot number within the sale. |
| `itemName` | `string \| null` | Lot title. |
| `currentBidUsd` | `number \| null` | Current high bid in USD (`null` when no bids have been placed). |
| `bidIncrementUsd` | `number \| null` | Minimum bid increment in USD. |
| `bidderCount` | `integer` | Number of active bidders (`0` when unbid). |
| `reservePrice` | `boolean \| number \| string \| null` | Raw `reserve` flag/value from GSA. |
| `auctionStart` | `string \| null` | Auction start date (`YYYY-MM-DD`). |
| `auctionEnd` | `string \| null` | Auction close date (`YYYY-MM-DD`). |
| `daysUntilEnd` | `integer \| null` | Calendar days until `auctionEnd`. |
| `status` | `string \| null` | Auction status, normalized to upper case: `ACTIVE` (open for bids) or `PREVIEW`. **`PREVIEW` lots are not biddable yet**; bidding opens on `auctionStart`. |
| `isBiddable` | `boolean` | `true` only when `status` is `ACTIVE`. |
| `agencyName` | `string \| null` | Reporting federal agency name. |
| `bureauName` | `string \| null` | Reporting federal bureau name. |
| `city` | `string \| null` | Property location city. |
| `state` | `string \| null` | Property location 2-letter state code. |
| `zip` | `string \| null` | Property location ZIP code. |
| `vin` | `string \| null` | 17-character VIN parsed from `lotInfo` when present. |
| `hoursOrMiles` | `string \| null` | Odometer mileage or equipment hours parsed from `lotInfo` when present. |
| `imageUrl` | `string \| null` | Primary lot photo URL. |
| `lotUrl` | `string \| null` | Direct link to the lot preview page on `gsaauctions.gov`. |
| `description` | `string \| null` | Clean plain-text description trimmed to 2,000 characters. |
| `contractOfficer` | `string \| null` | Federal sales contracting officer name (only when `includeSalesContact` is `true`). |
| `coEmail` | `string \| null` | Contracting officer email (only when `includeSalesContact` is `true`). |
| `coPhone` | `string \| null` | Contracting officer phone (only when `includeSalesContact` is `true`). |

#### Sample output row (synthetic example)

```json
{
  "lotId": "4-1-QSC-I-27-999-001",
  "saleNo": "4-1-QSC-I-27-999",
  "lotNo": "001",
  "itemName": "2019 Ford F-250 Super Duty 4x4 Pickup Truck",
  "currentBidUsd": 8400.0,
  "bidIncrementUsd": 100.0,
  "bidderCount": 4,
  "reservePrice": true,
  "auctionStart": "2026-10-20",
  "auctionEnd": "2026-10-30",
  "daysUntilEnd": 7,
  "status": "ACTIVE",
  "isBiddable": true,
  "agencyName": "Department of the Interior",
  "bureauName": "Bureau of Land Management",
  "city": "Phoenix",
  "state": "AZ",
  "zip": "85004",
  "vin": "1FT7W2B65KEE12345",
  "hoursOrMiles": "64,210 miles",
  "imageUrl": "https://www.ppms.gov/gw/auction/ppms/api/v1/auction/image/example.jpg",
  "lotUrl": "https://www.gsaauctions.gov/auctions/preview/999999",
  "description": "2019 Ford F-250 Crew Cab 4x4, 6.2L V8 gas engine, automatic transmission. Odometer reads 64,210 miles. VIN: 1FT7W2B65KEE12345."
}
```

### How pricing works

Pay per result. You only pay for rows returned. Set `"maxTotalChargeUsd": 50` in your run options to cap spend automatically.

### Data source and terms

- **Official source:** [GSA Auctions API Documentation (open.gsa.gov)](https://open.gsa.gov/api/gsa-auctions-api/) and [api.data.gov Signup](https://api.data.gov/signup/).
- **Update frequency:** The GSA active-auctions feed updates daily / intra-day as new sales open and bids are placed.
- **Affiliation disclaimer:** This Actor is an independent tool and is **not affiliated with or endorsed by the U.S. General Services Administration (GSA)**.

### Compliance

When `includeSalesContact` is enabled, the dataset includes federal contracting officer business contact details (`contractOfficer`, `coEmail`, `coPhone`) published by GSA for auction administration and inspection scheduling. Buyers are solely responsible for complying with the Telephone Consumer Protection Act (TCPA), the National Do-Not-Call Registry, the CAN-SPAM Act, and all applicable federal and state privacy laws before any outreach. Do not use this Actor for consumer or individual marketing.

### Limits and FAQ

- **Does the GSA feed include an exact closing hour/minute or whether the reserve has been met?**
  No. The official GSA Auctions v2 JSON feed publishes `aucStartDt`, `aucEndDt`, `highBidAmount`, and the boolean `reserve` indicator, but does not expose intraday closing timestamps or reserve-met status.
- **When should I provide `dataGovApiKey`?**
  The built-in fallback (the Actor's account key when configured, otherwise the public `DEMO_KEY`) works out of the box because the Actor makes only one API request per run, but if you trigger many runs per hour, adding a free [api.data.gov](https://api.data.gov/signup/) key avoids shared rate limits.

### Changelog

- **v0.2 (2026-10-27):** api.data.gov key resolution is now three-way: the `dataGovApiKey` input when provided, otherwise the account `DATA_GOV_API_KEY` secret, otherwise the public `DEMO_KEY`; the completed run status names which kind of key was used. Added the free sample linked above.
- **v0.1 (2026-10-23):** Initial release supporting GSA Auctions v2 JSON feed, VIN/mileage regex extraction, and contact privacy controls.

# Actor input Schema

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

Case-insensitive keyword matched against itemName and lotInfo description (e.g. 'truck', 'ford', 'boat', 'tractor'). Leave empty for all active lots.

## `states` (type: `array`):

Two-letter US state/territory codes where the surplus property is located (e.g. \['CA', 'TX', 'VA']). Leave empty for all states.

## `endingWithinDays` (type: `integer`):

Keep only auctions ending within this many days from today. Set to 0 for no limit.

## `maxCurrentBidUsd` (type: `integer`):

Keep only lots where the current high bid (or 0 if no bids yet) is at or below this USD amount. Set to 0 for no limit.

## `agencyKeyword` (type: `string`):

Case-insensitive keyword matched against the reporting federal agencyName or bureauName.

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

Sort lots newest first by auctionStart date (NEWEST) or soonest closing first by auctionEnd date (ENDING_SOON).

## `includeSalesContact` (type: `boolean`):

Defaults to false to omit federal contracting officer personal contact fields (contractOfficer, coEmail, coPhone) unless explicitly needed for auction inspection/inquiries.

## `dataGovApiKey` (type: `string`):

Optional free api.data.gov key. If you provide one, the Actor uses yours. If you leave this blank, the Actor uses the account DATA_GOV_API_KEY secret when one is attached; otherwise it falls back to the public DEMO_KEY, which api.data.gov rate-limits per IP address, so heavy or scheduled use should add your own free key.

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

Maximum number of GSA auction lots to return.

## Actor input object example

```json
{
  "keyword": "",
  "states": [],
  "endingWithinDays": 0,
  "maxCurrentBidUsd": 0,
  "agencyKeyword": "",
  "sortBy": "NEWEST",
  "includeSalesContact": false,
  "maxItems": 200
}
```

# Actor output Schema

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

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

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

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "keyword": "",
    "states": [],
    "endingWithinDays": 0,
    "maxCurrentBidUsd": 0,
    "agencyKeyword": "",
    "sortBy": "NEWEST",
    "includeSalesContact": false,
    "maxItems": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("groundplanes/gsa-auctions-surplus-lots").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 = {
    "keyword": "",
    "states": [],
    "endingWithinDays": 0,
    "maxCurrentBidUsd": 0,
    "agencyKeyword": "",
    "sortBy": "NEWEST",
    "includeSalesContact": False,
    "maxItems": 200,
}

# Run the Actor and wait for it to finish
run = client.actor("groundplanes/gsa-auctions-surplus-lots").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 '{
  "keyword": "",
  "states": [],
  "endingWithinDays": 0,
  "maxCurrentBidUsd": 0,
  "agencyKeyword": "",
  "sortBy": "NEWEST",
  "includeSalesContact": false,
  "maxItems": 200
}' |
apify call groundplanes/gsa-auctions-surplus-lots --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,groundplanes/gsa-auctions-surplus-lots"
        }
    }
}
```

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/3n3eF6X8mOdwtDKO9/builds/soj6kSvghdkXRQi2e/openapi.json
