# Public Surplus — US and Canadian public-agency surplus auctions

> Search LIVE Public Surplus auctions — the marketplace US and Canadian cities, counties, school districts, universities, transit agencies and utilities use to sell surplus vehicles, heavy equipment, computers, lab and medical gear, furniture and scrap. Filter by keyword, category, state or province, distance from a ZIP, price band, closing window and listing age. Every row carries the current price, the exact end time in UTC and the seconds left by the source's own clock. All parameters are optional; with none the call browses every live lot.
> ReefAPI engine `publicsurplus` · 4 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/publicsurplus/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/publicsurplus/v1/search — 2 credits
Search LIVE Public Surplus auctions — the marketplace US and Canadian cities, counties, school districts, universities, transit agencies and utilities use to sell surplus vehicles, heavy equipment, computers, lab and medical gear, furniture and scrap. Filter by keyword, category, state or province, distance from a ZIP, price band, closing window and listing age. Every row carries the current price, the exact end time in UTC and the seconds left by the source's own clock. All parameters are optional; with none the call browses every live lot.

**Parameters:**
- `query` (string, optional) — Keyword text, 1-200 characters. The source matches it against the lot title AND the description (agency boiler-plate included), so each row says title_matches_query.
- `category_id` (integer, optional) — Public Surplus category id from the categories action — a top-level id (4 Motor Pool, 1 Computers, 17 Heavy Equipment) or a sub-category id (106 Notebooks). Measured to bite: laptop 171 → 159 with category 1, 60 with 106.
- `state` (string, optional) — Two-letter US state, territory or Canadian province code of the selling agency, for example TX, CA, ON. 68 codes are accepted; truck went from 245 lots to 24 with CA.
- `zip` (string, optional) — Five-digit US ZIP to search around. Requires radius_miles.
- `radius_miles` (enum, optional) — Radius around zip, one of the source's own steps. Measured to bite: truck 245 → 13 within 200 miles of 85718. [one of: 20, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000]
- `min_price` (number, optional) — Lowest current price in dollars. Measured to bite: truck 245 → 89 with min 1000.
- `max_price` (number, optional) — Highest current price in dollars. Measured to bite: truck 245 → 84 with max 100.
- `closing_within_hours` (enum, optional) — Only lots whose auction closes within this many hours (the source's own steps). Measured: truck 6 h → 1, 24 h → 25, 120 h → 76 of 245. [one of: 1, 6, 24, 120, 240]
- `listed_within_hours` (enum, optional) — Only lots first listed within this many hours. Measured: truck 168 h → 138 of 245. [one of: 1, 24, 48, 168]
- `sort` (enum, optional, default "ending_soonest") — Result ordering. Each value was verified to reorder the result; the source's auction-number sort reproduced the default and is not published. [one of: ending_soonest, ending_latest, price_high, price_low, title]
- `page` (integer, optional, default 1) — One-based page; 25 lots per page (fixed by the source). Past the last page the source answers with an empty page, reported as page_beyond_last.

**Returns:** total (the source's own result count), page, page_size (25), last_page, has_more, page_beyond_last, measured_at_utc (the source's own clock), and listings[]: item_id, auction_id, title, url, current_bid, price_display (the source's string), ends_at (ISO-8601 UTC), seconds_remaining (by the source's clock), ended, source_time_left, state, image_url and, when query is given, title_matches_query. The search page does not name the selling agency or the bid count — detail returns both.

**Example request body:**
```json
{
  "query": "truck",
  "sort": "ending_soonest"
}
```

### POST https://api.reefapi.com/publicsurplus/v1/detail — 2 credits
Everything Public Surplus publishes about one live auction: the selling agency (id, name, logo, storefront), the pick-up address, start and end time (end in UTC plus the source's own label), whether it can extend, bid count, current price, increment, minimum next bid, currency, bid deposit, the high bidder's handle exactly as the source masks it, condition, the agency's full description with its labelled fields (odometer, engine, VIN where typed), every photo, payment methods, shipping terms, buyer's premium and the agency disclaimer.

**Parameters:**
- `auction_id` (string, required) — The auction number search returns (item_id / auction_id). Closed lots are not public on this source and come back NOT_FOUND.

**Returns:** item_id, auction_id, title, url, reserve_status, starts_at_local, ends_at (UTC), ends_at_local, ends_at_zone_label, ends_at_offset_hours, seconds_remaining, ended, might_extend, source_time_left, measured_at_utc, seller{seller_id, name, logo_url, storefront_url, listings_url}, pickup_location{name, address, city, state, zip}, region, payment_methods[], online_card_limit, wire_transfer, shipping, contact_public, auction{current_bid, price_display, price_basis (current_bid or opening_price), bid_count, bid_increment, minimum_bid, currency, bid_deposit, high_bidder (source-masked), high_bidder_years_registered, other[]}, condition, description, description_fields[]{label, value}, buyers_premium_percent, images[], image_count, disclaimer, terms_url.

**Example request body:**
```json
{
  "auction_id": "4098212"
}
```

### POST https://api.reefapi.com/publicsurplus/v1/seller_listings — 2 credits
Every live auction one selling agency currently has open, 50 per page — the agency id is detail's seller.seller_id. The way to watch one city, county or university's surplus as it is listed.

**Parameters:**
- `seller_id` (integer, required) — The agency id (the source's orgid), from detail's seller.seller_id.
- `page` (integer, optional, default 1) — One-based page; 50 lots per page.

**Returns:** seller_id, seller_name, page, page_size (50), last_page (from the source's own pager), has_more, page_beyond_last, measured_at_utc and listings[] in the search row shape (without state). The source publishes no total count on this page, so none is invented.

**Example request body:**
```json
{
  "seller_id": 19311
}
```

### POST https://api.reefapi.com/publicsurplus/v1/categories — 2 credits
The Public Surplus category tree with the live lot count on every sub-category, as the site's own all-categories page prints it. Ids are what search's category_id takes.

**Parameters:**
- `flat` (boolean, optional, default false) — Return every sub-category in one flat list (with parent_id) instead of nested.

**Returns:** categories[]: id, name, parent_id, live_lot_count (top level = sum of its sub-categories) and children[]; plus top_level_count and total_count.

**Example request body:**
```json
{
  "flat": false
}
```

## 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=publicsurplus
- Human docs page: https://reefapi.com/docs/publicsurplus
- Overview page: https://reefapi.com/publicsurplus-api
- Every ReefAPI API in one file (for your AI): https://reefapi.com/llms-full.txt
