GovDeals Scraper — Government Surplus Auction Lots
Pricing
$3.00 / 1,000 per lot returneds
GovDeals Scraper — Government Surplus Auction Lots
Government-surplus auction lots from GovDeals: title, seller agency, category, current bid, bid count, bid increment, buyer's premium, buy-now price, closing time, quantity, condition, VIN, meter, city/state and photos. Paste a GovDeals URL, or filter by state, category, keyword or seller.
Pricing
$3.00 / 1,000 per lot returneds
Rating
0.0
(0)
Developer
Scrapers Delight
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
10 days ago
Last modified
Categories
Share
🏛️ GovDeals Scraper — Government Surplus Auction Lots
Turn GovDeals — the largest US marketplace for government and
municipal surplus — into a structured lot feed. One row per lot, carrying title,
agency, category, current_bid, opening_bid, bid_increment, buyer_premium_pct,
buy_now_price, total_bids, end_date, quantity, condition, make, model,
vin_serial, meter_reading, city/state, latitude/longitude, seller contact and the
full photo gallery — 67 fields in all.
Paste a govdeals.com URL, or filter by state, category, keyword, seller, sale type, seller type and bid range. No login. No API key. No cookies. No CAPTCHA solving. No browser.
26,193 open lots in the whole live catalogue, counted through the API on 2026-08-12 — not quoted from a marketing page. Every run logs the live count for the scope you asked for.
⚡ Quick start
Click Try for free and hit Start. This is the input the Actor ships with, and it returns rows with zero edits:
{"searchType": "all","fetchDetails": true,"maxLots": 50}
Measured on that exact input, 2026-08-12: 50 lots in 38 seconds, 103 HTTP calls, 50/50 with a detail record and 50/50 with a live bid box.
Or skip the picker entirely and paste what is in your address bar:
{"startUrls": ["https://www.govdeals.com/search?kWord=forklift&stateName=Texas","https://www.govdeals.com/asset/13273/21430"],"maxLots": 50}
Search pages and single lot pages can be mixed in the same list. Go to GovDeals, apply your filters, copy the URL, paste it here.
🎯 The wedge: the three fields that turn a headline bid into a decision
Most surplus scrapers hand you a title and a current bid. The bid is not the price and the listing is not the opportunity:
buyer_premium_pct— the seller's buyer premium, straight from GovDeals' own bid box (commonly 9.5–15%; the two sample rows below carry 12.5% and 10%). It is the difference between an $81,000 hammer and a $91,125 landed cost. The direct competitor's own published field list runs alphabetically fromaccountIdtomakeand contains no buyer premium, no bid increment and no buy-now price.view_count/visitor_count/watcher_count— GovDeals' own demand signals: how many times a lot has been opened, by how many distinct visitors, and how many people are watching it. A high watcher count on a lot closing soon means you are about to be outbid. A zero-watcher lot closing today is where the arbitrage is.- Category child-code expansion — a GovDeals lot only carries its own leaf category
code, so filtering on a family code silently misses almost everything:
206("Clothing, Jewelry, and Accessories") returns 85 lots, while its child56("Jewelry") returns 1,096. This Actor resolves"Material Handling Equipment"+36into 39 codes and queries all of them at once. Pasting?category=36resolved to 26 codes on 2026-08-12.
📕 Read this before you buy rows
- GovDeals is a mixed catalogue. Jewelry, office furniture and IT gear sit next to dump
trucks.
make/model/vin_serial/meter_readingare populated on vehicles and plant only. Their fill is a function of what you asked for, not a defect — see the two tables below. - A capped run is not a random sample.
sortBydecides which lots you get whenmaxLotsis small relative to the scope. The default is closing-soonest, which is right for sniping and wrong for market research — usenewestorbidHighfor that. - Closed lots are dropped before you are billed. GovDeals leaves just-closed lots in its
open search index for a while. With the default sort that would otherwise hand you a page of
dead lots. Set
includeClosedLots: trueonly if you are building sold comps, and expect null quantity/condition/gallery on those rows. current_bidis a snapshot. This is a live auction. Rows are true atscraped_at, and a lot closing in minutes will move between your run and your read.- There is no radius search. GovDeals' proximity parameters return nothing for anonymous
callers, so this Actor does not pretend to support them. Use
states, or filterlatitude/longitudeyourself.
📦 What you get — one row per lot
| Field group | Fields |
|---|---|
| Identity | lot_id, asset_id, account_id, auction_id, inventory_id, lot_number, lot_url |
| The item | title, description, category, category_code, parent_category, quantity, quantity_unit, condition, condition_code |
| Money | current_bid, opening_bid, bid_increment, buy_now_price, buyer_premium_pct, currency |
| Competition | total_bids, has_bid, high_bidder_id, has_reserve, reserve_met, auto_extension, view_count, visitor_count, watcher_count |
| Vehicles / plant | make, model, model_year, vin_serial, meter_reading, meter_unit, meter_accurate |
| Seller | agency, agency_id, agency_url, seller_type, seller_contact_name, seller_contact_email, seller_contact_phone, audience |
| Location | address, city, state, zipcode, country, latitude, longitude |
| Timing | start_date, end_date, time_left_seconds, sale_type, status |
| Terms | inspection_terms, payment_terms, pickup_terms, special_terms |
| Media / run | images (array), thumbnail, image_count, detail_fetched, scraped_at |
All dates are ISO-8601 UTC. All money fields are numbers, never strings. GovDeals publishes its clocks in US/Eastern wall time; this Actor converts using the offset each record itself reveals, so you never have to guess EST vs EDT.
Every run also writes a RUN_STATS record to the key-value store — rows seen, unique lots,
measured duplicate rate, detail and bid-box fill, closed lots skipped and HTTP call counts — so
you can audit any claim on this page against your own run.
📊 Field fill — measured on 200 lots
From a live proxy run of 200 lots (searchType: "all", default sortBy: "closing",
fetchDetails: true) — 30 states, 51 selling agencies, 8.6 photos per lot on average, and
200/200 lots enriched:
| 100% filled | Partly filled | Depends on the item |
|---|---|---|
lot_id, asset_id, account_id, auction_id, lot_url, title, description, category, category_code, sale_type, status, currency, current_bid, opening_bid, bid_increment, buyer_premium_pct, total_bids, has_bid, has_reserve, reserve_met, auto_extension, view_count, visitor_count, quantity, quantity_unit, condition, condition_code, agency, agency_id, seller_type, audience, address, city, state, zipcode, country, start_date, end_date, time_left_seconds, inspection_terms, payment_terms, pickup_terms, special_terms, images, thumbnail, image_count | latitude/longitude 98.5%, watcher_count 67.5%, parent_category 63%, high_bidder_id 53.5% (null until somebody bids), make 45.5%, agency_url 43.5% | model 37.5%, inventory_id 35.5%, vin_serial 15.5%, seller_contact_name/email/phone 13.5%, model_year 13%, meter_unit 12%, meter_accurate 11%, meter_reading 10.5%, buy_now_price 6%, lot_number <1% |
The same fields on a vehicle-scoped run
The headline that could mislead you: on an unfiltered run this is not a VIN product. Scope
it and it becomes one. On searchType: "keyword", "dump truck", 30 lots, the same fields
measured make 90%, model 90%, VIN 83%, model_year 80%, meter_reading 60%. A 25-lot
dump truck run on 2026-08-12 returned 18 of 25 rows carrying make + VIN + meter together.
seller_contact_email(13.5% overall) is the agency's published surplus contact where the seller chose to publish one — a real, useful lead field, but do not plan on every row having it. The switch is GovDeals' own per-lothidePocinfoflag: re-measured 2026-08-19 on 25 raw detail records, 24 of 25 hadhidePocinfo: trueand exactly the 1 lot with itfalsecarried name + email + phone. Scope where sellers publish and it inverts: a 30-lotsaleType: "5"(Make Offer) run returned 30/30 with a seller contact name.buy_now_priceis only set on Buy-Now / strike-price lots (2–6% on the default closing-soonest mix, 20.5% on another 200-lot slice). Scope to it and it is complete: a 30-lotsaleType: "4"run measured 30/30. It tracks the sale formats in your scope, nothing else.lot_numberis GovDeals' sale-event lot number, and it is a sale-format field, not a rare one. Re-measured 2026-08-19 straight off GovDeals' own search API, 50 lots per format: Online Auction 0/50 · Buy Now 0/50 · Make Offer 47/50 — and a 30-lotsaleType: "5"run through this Actor returned 27/30. Because timed online auctions are the bulk of the catalogue, an unfiltered run shows ~0%. GovDeals also writes-1on some records when there is none; this Actor returnsnullfor both the sentinel and a missing value, never"-1".
Every run prints its own measured fill in the log and in RUN_STATS.
Listing-only mode (fetchDetails: false)
Measured over 1,000 lots on 10 contiguous pages (1,000 unique, 0 duplicates): you keep title,
description, category, current_bid, opening_bid, total_bids, sale_type, agency,
city/state/zip, start_date/end_date, one photo and the lot URL at 100% fill, in
29 seconds and 12 HTTP calls. What you give up: quantity, condition, VIN, meter,
seller contact, terms, the full gallery, and every bid-box field
(bid_increment, buy_now_price, buyer_premium_pct, view/visitor/watcher counts).
The price per lot is identical either way — this buys speed, not money.
🧭 How to run it
Paste a URL (the fastest path)
{ "startUrls": ["https://www.govdeals.com/search?kWord=forklift&stateName=Texas"], "maxLots": 50 }
Read out of the URL: keyword (kWord), category (category, expanded to every child
code), state (stateName or state), seller (accountId) and sale type
(auctionTypeId, sellerType). Single lot pages (/asset/<assetId>/<accountId>) are resolved
straight off the asset endpoint. Lot URLs and search URLs can be mixed in one list, and
startUrls overrides the searchType picker whenever it is not empty.
Deliberately not read from the URL: its own price (sPrice/ePrice) and sort (sf/so)
parameters. Those have run-level inputs — minBid/maxBid and sortBy — that apply to every
scope in the run, and honouring one pasted URL's sort while ignoring another's would be worse
than not supporting it.
Or pick a source — six of them, one row shape
searchType | What it does |
|---|---|
all | Walks the whole live GovDeals catalogue (26,193 open lots on 2026-08-12), closing-soonest first |
state | One server-side query per state — "TX", "Florida", "ON" all work |
category | Give a code (36) or a name ("Heavy Equipment and Construction"); resolved against GovDeals' live category menu and expanded to every child code |
keyword | GovDeals' own site search, one walk per term |
seller | One or more GovDeals account ids (the second number in a lot URL); all ids ride in one query |
lotUrls | Paste specific lots — resolved straight off the asset endpoint, live bid included. Ideal as a watchlist |
{ "searchType": "state", "states": ["TX", "OK"], "maxLots": 500, "sortBy": "newest" }
{ "searchType": "category", "categories": ["Heavy Equipment and Construction"],"sellerType": "1", "minBid": 5000, "maxLots": 0 }
Stackable server-side filters: sellerType (government vs commercial reseller), saleType
(online auction / buy now / make offer / sealed bid), timeWindow (closing today / new
listings) and sortBy. minBid / maxBid are applied client-side to keep the dataset clean —
rows they remove are never charged to you.
Monitor mode — the recurring play
{ "searchType": "keyword", "keywords": ["excavator"], "monitorMode": true,"slackWebhookUrl": "https://hooks.slack.com/services/…", "maxLots": 0 }
Set monitorMode: true and the Actor remembers, in a named key-value store, every lot id it
has already returned for that exact scope — so a scheduled run outputs, and bills, only
genuinely new listings. Closed lots age out of the memory a day after they close. Verified:
run 1 → 12 new of 12, run 2 → 0 new of 12, and a different keyword gets its own independent
seen-set. Pair it with an Apify Schedule (hourly for bids, daily for new inventory).
Optional webhookUrl, slackWebhookUrl (posts a card with the photo, bid, location and closing
time) and emailRecipients fire on each new lot; alertOnBidChange + watchlistLotIds alert
when a bid moves on lots you are tracking.
🧾 Sample rows
Two real rows from runs on 2026-08-12. Long strings are cut at … and a few fields are left out
for length — no value has been altered. They are deliberately different shapes so the conditional
fill above is visible rather than described.
Fields people misread:
high_bidder_id: nullmeans nobody has bid; it is never0.total_bids: 0withhas_bid: falsesays the same thing from the other side.buyer_premium_pctis a percentage (12.5= 12.5%), not a multiplier and not a 0–1 fraction.quantity: 1withquantity_unit: "Lot"can mean three machines. Read the unit, not the number.reserve_metis inverted from GovDeals' ownisReserveNotMetflag so thattruealways means the good thing.nullmeans GovDeals published no reserve state.time_left_secondsis computed atscraped_atand does not keep counting. Schedule onend_date, which is absolute UTC.
⚙️ Input
| Field | Type | Default | What it does |
|---|---|---|---|
| 1. What to scrape | |||
startUrls | string[] | [] | Paste any govdeals.com search or lot URL. Overrides searchType when non-empty. Reads keyword, category, state, seller and sale type out of the URL |
searchType | select | all | Used only when startUrls is empty: all · state · category · keyword · seller · lotUrls |
states | string[] | ["TX"] | "TX", "California", "ON". One server-side query per state, so each entry costs a request per page |
categories | string[] | ["Heavy Equipment and Construction"] | Codes or names; expanded to every child code. Unmatched names are logged and skipped |
keywords | string[] | ["forklift"] | One walk per term — two terms take roughly twice as long |
sellerAccountIds | string[] | [] | Numeric account ids; all ride in one query, so ten sellers cost the same as one |
lotUrls | string[] | [] | Full lot URLs. One request per lot, no search walk — the cheapest way to re-price a watchlist |
| 2. Filters | |||
sellerType | select | any | Government vs commercial reseller. Server-side, so it costs nothing |
saleType | select | any | Online Auction · Buy Now · Make Offer · Sealed Bid. Server-side |
timeWindow | select | any | Closing today · New listings. Server-side |
minBid | integer | — | Client-side. Rows it removes are never charged. Lots with no bid yet are always kept |
maxBid | integer | — | Client-side. Rows it removes are never charged |
includeClosedLots | boolean | false | ON builds sold comps; expect null quantity/condition/gallery on closed rows |
sortBy | select | closing | Decides which lots a capped run gets. closing · newest · bidHigh · bidLow · bestMatch |
| 3. Depth & cost | |||
fetchDetails | boolean | true | Two extra requests per lot for VIN, meter, terms, gallery and the live bid box. Same price either way |
maxLots | integer | 50 | Your hard cost cap. 0 = no cap (26,193 lots ≈ $79). Duplicates and filtered rows never count against it |
| 4. Monitoring & alerts | |||
monitorMode | boolean | false | Only lots not seen before for this exact scope. The one mode where zero rows is legitimate |
alertOnNewLot | boolean | true | Fires only when monitorMode is on and a destination is set |
alertOnBidChange | boolean | false | Needs watchlistLotIds and fetchDetails: true |
watchlistLotIds | string[] | [] | Ids in this Actor's "<assetId>-<accountId>" form, e.g. "13230-21430" |
webhookUrl | string | — | JSON POST per alert — Zapier, Make, n8n, Sheets. A failed POST warns, never fails the run |
slackWebhookUrl | string | — | Slack card with photo, bid, location, seller, closing time |
emailRecipients | string[] | [] | Digest via the apify/send-mail Actor |
| 5. Advanced | |||
proxyConfiguration | proxy | Apify datacenter | The site is not walled for this route — residential costs more and measurably buys nothing |
requestConcurrency | integer | 4 | Clamped to 5. GovDeals' robots.txt asks for a 5-second crawl delay |
diagnose | boolean | false | Dumps raw upstream JSON to the key-value store and exits without billing you |
💸 Pricing
$0.003 per lot returned — $3 per 1,000. Pay-per-event on the lot-scraped event, with no
monthly platform fee from this Actor and no charge for starting a run.
You are charged for lots delivered to your dataset, and never for:
- a lot that closed before the run reached it (dropped before enrichment),
- a lot removed by your
minBid/maxBidfilter, - the same lot twice — duplicates are dropped before billing,
- any of the Actor's own retries, proxy traffic, category-menu lookups or bootstrap calls,
- a
diagnose: truerun, which writes no dataset rows at all.
Charging and delivery are atomic (Actor.pushData(items, 'lot-scraped')), so if you hit a
spend cap you get whole rows you paid for — never billed-but-missing rows.
| Run | Lots | Cost |
|---|---|---|
| Default smoke test | 50 | $0.15 |
| One state, enriched | 500 | $1.50 |
| Daily new-listings monitor | ~200 new/day | $0.60/day |
| Whole live catalogue (2026-08-12) | 26,193 | ~$79 |
maxLots is your hard cost cap (default 50; set 0 for the full catalogue).
🔍 Honest limits
- Seller contact is a 13.5% field, not a lead database.
seller_contact_name/email/phoneare the agency's published surplus contact where the seller chose to publish one. On a vehicle-heavy commercial-reseller scope it is close to zero (the sample vehicle row above has all three null). This is a GovDeals publishing choice, not a scraping failure. If you need contactable rows, filtersellerType: "1"— government sellers publish a contact far more often — and useagency_url(43.5%) as the enrichment handle. makeis 45.5% andvin_serialis 15.5% on an unfiltered run. Scope to vehicles and they go to 100%: a 30-lotsearchType: "category","Automobiles/Cars"run on 2026-08-19 measured make 30/30, model 30/30, model_year 30/30, vin_serial 30/30, meter_reading 29/30. There is no setting that makes a jewelry lot have a VIN.buy_now_priceis 2–6% on the default scope and 30/30 on asaleType: "4"(Buy Now) run. Most GovDeals lots are timed auctions; only Buy-Now / strike-price lots carry one.lot_numberis a Make-Offer field. Measured per format on 2026-08-19: Online Auction 0/50, Buy Now 0/50, Make Offer 47/50 — so an unfiltered run shows ~0% andsaleType: "5"shows 27/30. GovDeals also writes-1on some records when there is none, and this Actor returnsnullrather than passing the sentinel through as data.agency_url(43.5%) is the agency's OWN website (webAddr), published by only some sellers — 2/30 on a Texas run, 19/30 on a Buy-Now run. GovDeals separately carries a storefront slug for ~60% of lots, which this Actor does not currently emit;agency+agency_idare on 100% of rows and are the reliable join keys.- A small number of lots have no detail record at all. Open lots enriched 200/200, 60/60,
50/50 and 25/25 across four measured runs, but in an earlier 20-lot sample GovDeals returned
its zeroed placeholder (
assetId: 0) for 2 of 20 — 18/20 = 90%. That response repeats identically off-proxy and from a browser, so it is GovDeals, not a block. Those rows keep every listing field and are flaggeddetail_fetched: false— never a silent half-row. - No ZIP + radius search. GovDeals' proximity parameters return nothing for anonymous
callers, so this Actor does not pretend to support them. Use
states, or filterlatitude/longitudeyourself; both are returned on 98.5% of rows. - No sold-price archive. GovDeals stops publishing a lot's detail record once it closes, so
includeClosedLots: truegives you the listing fields and a final bid, not a full sold record. RunmonitorModeon a schedule if you want to accumulate comps over time. - One state per query. GovDeals' state filter accepts a single value, so ten states cost ten walks. The cheap way to slice a region is to run unfiltered into a dataset and filter it yourself.
- Cross-run dedupe needs
monitorMode. Within a run, duplicates are impossible (measured 0.00% over 1,000 contiguous rows). Across runs, only monitor mode remembers. - No login, no API key, no CAPTCHA-solving service, no browser automation. If GovDeals ever puts this route behind an account, this Actor will fail loudly rather than work around it.
🔌 How it works
GovDeals is an Angular single-page app — there is no server-rendered lot HTML to parse.
Everything the browser shows comes from three public JSON endpoints on Liquidity Services'
"maestro" service: a search endpoint (list + x-total-count), an asset endpoint (quantity,
condition, gallery, terms) and a live bid-box endpoint (bid, increment, buyer's premium, views).
The Actor calls the same three, anonymously, with no login and no cookie, reading the site's
own published web key out of its JS bundle on every run so a key rotation heals itself instead of
breaking your schedule.
Transport, measured through the Apify proxy — not assumed:
| Check | Result |
|---|---|
| Search, 20 contiguous pages × 50 rows, plain Apify datacenter proxy | 20/20 = 100% |
| Uniqueness on those 1,000 rows | 1,000 unique, 0 duplicates — 0.00% |
| Full-Actor contiguous walk, 1,000 lots over 10 pages | 1,000 unique, 0 duplicates — 0.00% |
| Live bid box, 20 real lots | 20/20 = 100% |
| Asset detail, 200 open lots (default scope) | 200/200 = 100% |
| Public-config bootstrap (homepage + JS bundle) | 5/5 = 100% |
No residential proxy, no session pinning, no browser — so runs are fast and cheap. A default 50-lot enriched run completes in ~30 seconds and ~103 HTTP calls (re-measured 2026-08-12: 38s, 103 calls); 1,000 listing-only lots take 29 seconds and 12 calls.
Re-listed lots are handled correctly. GovDeals re-lists unsold lots under an incremented
auction round, and the bid-box endpoint will happily answer for the wrong round if you ask
for the wrong one — returning the original round's price and closing date. (Asset 3/32136:
round 1 → $7,100, closes 14 Jul; round 2 → $10,200, closes 12 Aug, which is what the site
shows.) This Actor always asks for the lot's current round and discards any answer that comes
back for a different one. A 50-lot sample spanning auction rounds 1–13 cross-checked against a
fresh live search: 0 mismatches on closing date, and the only two bid deltas were lots
that took new bids in the seconds between the two reads.
Closed lots, and why the filter matters
GovDeals leaves a lot in its open search index for a while after bidding closes, and its
asset endpoint stops publishing that lot's detail record once it does. With the default
closing-soonest sort, a run that lands on the hour would otherwise hand you a page of dead
lots you cannot bid on — measured on a real run at 23:00:18Z where all 50 closing-soonest
rows had closed at 23:00:00Z and only 12 still had a detail record.
So this Actor drops already-closed lots before enrichment: you are not billed for them,
and it keeps paginating to fill your maxLots with live ones. Verified across 1,000 lots:
0 rows closed at their own scrape time.
Uniqueness
Measured on contiguous page walks, because sparse offsets prove nothing: 20 contiguous search
pages × 50 rows gave 1,000 unique of 1,000 — a 0.00% duplicate rate, and a full-Actor walk of
1,000 lots over 10 pages gave the same. Deduplication is keyed on lot_id
(<assetId>-<accountId>) and happens before enrichment and before billing, so the dataset —
and your bill — never holds the same lot twice. displayRows is held constant for a whole scope
on purpose: GovDeals offsets by page × displayRows, so shrinking it on the last page re-reads
rows you already have (measured: a 20-row final page came back 20/20 duplicates).
When a run fails
- Zero rows throws. A green-but-empty run is the classic broken-scraper failure, so the Actor errors with the scope, the GovDeals match count, how many lots were closed, how many were filtered out and the HTTP call counts.
- A category name that matches nothing is logged and skipped; if none of them resolve, the run stops rather than silently scraping the whole catalogue.
- A non-govdeals.com URL in
startUrlsis warned about and skipped, not guessed at. - A bid box answering for the wrong auction round is discarded rather than used.
- Detail fill below 60% raises a warning naming the likely cause, so null columns are never mistaken for a scraper fault.
diagnose: truedumps the raw upstream JSON and exits without writing rows or billing you.- The one legitimate zero:
monitorModereporting no new lots since the last run.
👥 Who buys this
- Used-equipment and heavy-plant dealers —
make+model+meter_reading+end_dateon a category-scoped monitor is a daily buy-list;buyer_premium_pctis what makes the landed-cost maths correct. - Fleet and municipal buyers —
sellerType: "1"plusstatesgives every unit a public agency is disposing of within driving distance, withlatitude/longitudefor routing. - Resale arbitrage sellers (eBay / Facebook Marketplace / equipment brokers) —
watcher_countandview_countagainstend_datesurface the lots nobody is watching that close today. - Auction aggregators and comparison sites — one row shape across GovDeals plus the sibling Actors below, with ISO dates and numeric money, is a feed you can merge without a mapping layer.
- Scrap and metals traders — condition codes
S(Scrap) andNR(Needs Repair) withquantity_unit: "Lot"are the bulk-disposal listings. - Procurement and asset-disposal analysts —
agency+category+current_bidover time is a defensible recovery-rate benchmark for what public bodies actually get for surplus.
🔗 Sibling Actors — same row shape
| Actor | What it is | Why you would use it instead |
|---|---|---|
| GSA Auctions Scraper | US federal surplus | GovDeals is states, counties, cities and schools; GSA is the federal government. Different sellers, no overlap |
| PublicSurplus Scraper | The other big municipal surplus marketplace | Many agencies list on one or the other, not both — run both for real coverage |
| Municibid Scraper | Municipal surplus, smaller-town skew | Reaches townships and school districts that never list on GovDeals |
| K-BID Auction Scraper | Upper-Midwest online auctions | Regional depth GovDeals does not carry |
| Equip-Bid Auction Scraper | Midwest equipment & estate auctions | Same |
GovDeals is where you go for government surplus with a buyer's premium and demand counts; GSA is where you go for federal disposals. If you only run one, run the one whose sellers are within driving distance of your yard — surplus is a pickup-only business.
❓ FAQ
Does this need a GovDeals account or login?
No. Every endpoint it touches is the anonymous public one the site itself calls for a logged-out visitor. No account, no API key, no cookie, no CAPTCHA solving.
Can I paste a URL from the GovDeals website?
Yes — that is the startUrls field, and it is the fastest way in. Search pages, category, state
and seller searches, and single lot pages all work, and you can mix them in one list.
Can I get the whole catalogue in one run?
Yes — searchType: "all", maxLots: 0. That was 26,193 lots on 2026-08-12 and about 262 search
pages. Leave fetchDetails: true on only if you want the full record, since it adds two calls
per lot. Raise the run timeout for big sweeps.
How much does 1,000 lots cost?
$3.00, at $0.003 per lot returned. The whole live catalogue is about $79.
Do I get lots that have already ended?
No, not by default — they are dropped before you are billed for them. Set
includeClosedLots: true if you want them.
Do I get charged for rows that get filtered out?
No. Closed lots, minBid/maxBid rejects and duplicates are all removed before billing, and a
diagnose: true run bills nothing at all.
Two runs — will I get duplicates?
Within a run, no: 1,000 unique of 1,000 on a contiguous walk. Across runs, yes, unless you turn
on monitorMode, which remembers what it has already returned for that exact scope.
Does it need a proxy?
It uses the default Apify datacenter proxy and that is enough — 20/20 on contiguous search pages and 20/20 on live bid boxes. Residential costs more and measurably buys nothing here.
Why does current_bid differ from the site by a few dollars?
Because it is a live auction. Rows are a snapshot at fetch time; lots closing within minutes move between your run and your read.
How fresh is the bid?
With fetchDetails: true the bid comes from the per-lot live bid box fetched during the run,
which is the same number the site's bid widget shows. With fetchDetails: false it comes from
the search row.
Does time_left_seconds keep counting?
No — it is computed at scraped_at. Use end_date (absolute UTC) for anything you schedule on.
Can I filter by ZIP + radius?
Not reliably — GovDeals' proximity parameters return nothing for anonymous callers, so this
Actor does not pretend to support them. Use states, or filter latitude/longitude
yourself; both are returned on 98.5% of rows.
What is buyer_premium_pct?
The seller's buyer premium as a percentage, straight from GovDeals' bid box (commonly
9.5–15%). It is what turns a headline bid into a landed cost, and most surplus scrapers do not
return it. Requires fetchDetails: true.
Are view_count / visitor_count / watcher_count useful?
They are GovDeals' own demand signals for a lot — how many times it has been opened, by how many distinct visitors, and how many people are watching it. A high watcher count on a lot closing soon means competition; a zero-watcher lot closing today is where the arbitrage is.
Why are some fields empty?
Because GovDeals never published them for that lot. make/model/VIN/meter exist on
vehicles and plant only; seller contact exists where the agency chose to publish one. The fill
tables above give the measured rate for every field, and every run logs its own.
Why do some titles have a missing character?
GovDeals stores a handful of dashes as an un-decodable byte in its own database. Rather than invent a character, this Actor strips it and collapses the whitespace.
Sealed-bid and Buy-Now lots?
Included. sale_type tells you the format, and saleType in the input filters to one.
Canadian lots?
Yes — GovDeals Canada lots share the catalogue. Province codes ("ON", "AB") work in
states, and currency distinguishes CAD from USD rows.
Can I schedule it?
Yes. Use monitorMode: true with an Apify Schedule — hourly for bid movement on a watchlist,
daily for new inventory — and wire slackWebhookUrl or webhookUrl into your own tools.
Can I export to CSV or Excel?
Yes — the dataset exports to CSV, Excel, JSON, XML or an API feed like any Apify dataset, and the Actor ships a table view with the columns that matter for bidding.
Something looks wrong — how do I debug it?
Set diagnose: true. The Actor fetches two pages plus one lot, dumps the raw GovDeals JSON to
the key-value store (DEBUG_SEARCH_ROWS, DEBUG_DETAIL, DEBUG_BIDBOX), reports whether
pagination overlapped, and exits without writing dataset rows or charging you for them.
Will a run ever succeed with zero rows?
No. A green-but-empty run is the classic broken-scraper failure, so this Actor throws with the scope, the GovDeals match count and the HTTP call counts in the message. Monitor mode is the one exception: "no new lots since last run" is a legitimate zero.
⚖️ Legal & fair use
GovDeals lists surplus property sold by public agencies (cities, counties, states, school districts, universities) and their commercial remarketers; the listings are public and intended to be seen by the widest possible audience of bidders.
What is read: the site's own public, logged-out JSON endpoints and the public JS bundle at
the site root, at a concurrency of 5 or less. What is not touched: the paths GovDeals'
robots.txt disallows (/account/, /assets/), any
logged-in page, and any anti-bot challenge — this Actor does not log in, does not solve
CAPTCHAs and collects no data behind any authentication.
Personal data: rows carry no bidder identities — high bidders appear only as GovDeals' opaque numeric id, never a name or username. The only contact details returned are the selling agency's own published surplus contact (13.5% of rows), which is business contact information published by a public body for the purpose of being contacted about that lot. If you use it for outreach, you are responsible for GDPR/CCPA, CAN-SPAM and any applicable state rules.
You are responsible for complying with GovDeals' Terms of Service and with applicable law in your jurisdiction. Verify every lot on GovDeals before bidding — auction data changes by the second.
GovDeals® is a trademark of Liquidity Services, Inc. This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by GovDeals or Liquidity Services.
💬 Feedback
Found a missing field, or want a filter that is not here? Open an issue on the Issues tab —
include the run id and, if a run looked wrong, the output of diagnose: true.