Troostwijk Auction Scraper - Industrial Lots, Bids, Close Times
Pricing
$1.70 / 1,000 per lot row returneds
Troostwijk Auction Scraper - Industrial Lots, Bids, Close Times
From $1.70 per 1,000 lots, no start fee. Every live lot on Troostwijk Auctions (TBAuctions: Troostwijk, Surplex, Vavato) with its real per-lot closing time, current bid, bid count, city and country. Optional lot pages add specs, brand, year, photos and the inspection/collection address.
Pricing
$1.70 / 1,000 per lot row returneds
Rating
0.0
(0)
Developer
Scrapers Delight
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share
Troostwijk Auction Scraper — industrial lots, bids and real closing times
Every live lot on troostwijkauctions.com, the TBAuctions storefront that also carries Surplex and Vavato inventory: bankruptcy estates, plant clearances, CNC machinery, forklifts, trucks, farm equipment, lab and IT hardware across 21 European countries.
One row = one live lot. $0.0017 per row — $1.70 per 1,000 lots. No start fee. Nothing you filter out is charged.
Measured on the live board on 2026-09-18, by walking all of it: 506 open auctions carrying 77,868 lots, across 21 countries (Belgium 879 collection locations, Netherlands 807, Germany 148, France 103, Austria 28, Poland 21, and 15 more).
The thing that makes this different
Every lot in this dataset carries a real per-lot closing time. That sounds unremarkable until you look at where the site actually publishes it:
| Surface | endDate present |
|---|---|
The auction's lot list (/en/a/<slug>) | 568 of 568 = 100.0% |
The lot's own detail page (/en/l/<slug>) | 0 of 48 = 0.0% |
Those are literal counts over real captured bytes, both in fixtures/. A scraper that starts from lot
pages — which is the obvious way to build this, and what the store's most detailed Troostwijk rival
does with its 37-field dump — cannot produce a closing time at all. Substituting the auction's
closing time does not fix it either, because closes are staggered within a single auction: in one
sampled 48-lot page, all 48 lots had 48 different close times.
So this actor builds every row from the auction lot list and treats the lot page purely as
optional enrichment layered on top. mergeDetail() is explicitly forbidden from touching the close
time. That is the whole architectural decision, and it is why there is deliberately no "paste a lot
URL" mode — a lot URL alone cannot tell you when the lot closes.
The same asymmetry hits the price: the lot detail payload publishes currentBidAmount on only
15 of 48 lots (31.3%), while the lot list carries it on 100%.
What you get
Always (one request per 48 lots)
| Field | Type | What it is | Fill |
|---|---|---|---|
lotUrl | string | Direct link to the lot page. | 100.0% |
displayId | string | The lot number the site shows, e.g. A7-50158-6. Unique, and the auction id is its prefix. | 100.0% |
title | string | Lot title as listed. | 100.0% |
closesAt | ISO 8601 UTC | When this lot closes. Per lot, not per auction. | 100.0% |
minutesToClose | integer | Minutes from scrapedAt to closesAt. Negative if it closed while the run was in flight. | 100.0% |
opensAt | ISO 8601 UTC | When bidding opened on this lot. | 100.0% |
currentBid | number | Current bid in currency units, not cents. Before the first bid this is the opening price. | 100.0% |
currency | string | EUR on all 568 captured rows. | 100.0% |
bidsCount | integer | Number of bids. 0 is a real answer, not a missing value. | 100.0% |
hasBids | boolean | bidsCount > 0, for filtering without a null check. | 100.0% |
followersCount | integer | How many users are watching the lot. The demand signal that moves before the bids do. | 100.0% |
biddingStatus | string | BIDDING_OPEN, BIDDING_NOT_YET_OPENED, BIDDING_CLOSED. | 100.0% |
saleTerm | string | GUARANTEED_SALE (250 of 568), OPEN_RESERVE_PRICE_NOT_ACHIEVED (235), OPEN_ALLOCATION_SET (83). | 100.0% |
city | string | City the lot is physically in. | 100.0% |
countryCode | string | ISO-2, lower case. 21 countries on the live board. | 100.0% |
auctionDisplayId | string | Parent auction number, e.g. A7-50158. | 100.0% |
auctionName | string | Parent auction title. | 100.0% |
auctionUrl | string | Link to the parent auction. | 100.0% |
auctionLotCount | integer | Lots the board reports for that auction. See the note below on why this is not the lot-list total. | 100.0% |
auctionFirstLotClosesAt | ISO 8601 UTC | The auction's minEndDate - the earliest close among its lots, and a true lower bound. | 100.0% |
imageUrl | string | Primary thumbnail. | 100.0% |
platform | string | TWK Troostwijk (448 of 568), SPX Surplex (103), VAVATO Vavato (17). | 100.0% |
Plus lotId, itemId, urlSlug, auctionId, auctionOpensAt, closesAtUnix and scrapedAt as | |||
| identifiers. |
With Fetch each lot page on (one extra request per lot, ~65 KB)
Same rows, more columns — at no extra charge. There is one billing event and it is per row, so enrichment costs you nothing beyond run time.
| Field | Type | What it is | Fill |
|---|---|---|---|
description | string | Seller description, HTML as published. | 77.1% |
remarks | string | Auctioneer notes on condition, wear and what is included. | 45.8% |
specs | array | Structured specifications as {name, value, unit}. 97 distinct spec names across 48 lots; median 6 per lot, max 29. | 89.6% |
brand | string | Pulled out of specs - the Brand attribute. | 72.9% |
modelType | string | The Type or Model attribute. | 54.2% |
yearOfBuild | string | The Year of build / Construction date attribute. | 56.3% |
serialNumber | string | The Serial number / Chassis number attribute. | 14.6% |
category | string | Top-level category, e.g. Metalworking, Transport & Logistics. | 100.0% |
subCategory | string | Second level, e.g. Lathes. | 100.0% |
categoryPath | string | The breadcrumb joined with >. | 100.0% |
startingBid | number | The lot's opening amount (initialAmount), in currency units. | 100.0% |
reserveStatus | string | NO_MINIMUM_BID_AMOUNT (25 of 48) or MINIMUM_BID_AMOUNT_NOT_MET (23) - whether a reserve exists and whether it is met. | 100.0% |
quantity | integer | Units in the lot. | 100.0% |
condition | string | Returned verbatim. Not a condition grade - see honest limits. | 100.0% |
marginScheme | boolean | true = sold under the VAT margin scheme, so no reclaimable VAT on the invoice. true on 1 of 48. | 100.0% |
hasWarranty | boolean | false on all 36 lots that publish it. | 75.0% |
imageUrls | array | Every photo, in the site's own order. Median 10 per lot, max 119. | 100.0% |
imageCount | integer | Length of imageUrls. | 100.0% |
documentUrls | array | Attached PDFs - spec sheets, inspection reports. | 8.3% |
carbonFootprintKg | number | The site's own carbon figure for the lot (its "LCA value"), kg CO2e. | 35.4% |
companyType | string | COMPANY (40 of 48), LIQUIDATOR (4), COMPANY_LIGHT (4). | 100.0% |
companyId | string | Numeric consignor-account id, stable across that consignor's lots. Not a company name. | 100.0% |
auctioneerId | string | The selling TBAuctions entity: TBA, SPX_DE, KDA, DECHOW, TWK_FR, TWK_VAVATO, TWK_REST_OF_WORLD. | 100.0% |
regionRestriction | string | NONE (44 of 48) or EU (4) - where the lot may be bid from. | 100.0% |
visiblePlatforms | array | Which sibling storefronts also show this lot. | 75.0% |
otherLanguageUrls | array | The same lot page in up to 11 languages, as {locale, url}. | 100.0% |
collectionStreet | string | Street of the collection facility. | 91.7% |
collectionPostalCode | string | Postcode of the collection facility. | 91.7% |
collectionCity | string | City of the collection facility. | 100.0% |
collectionCountryCode | string | Country of the collection facility. | 100.0% |
collectionFacilityName | string | Facility name, e.g. Amann Werkzeugmaschinen GmbH. | 91.7% |
collectionWindows | array | Collection slots as {from, to, byAppointment, isDeliverySchedule, forkliftAvailable, forkliftCapacityKg, contactNote, emails}. | 100.0% |
viewingStreet | string | Street of the viewing/inspection location, when it differs from collection. | 50.0% |
viewingPostalCode | string | Postcode of the viewing location. | 50.0% |
viewingCity | string | City of the viewing location. | 100.0% |
viewingFacilityName | string | Name of the viewing location. | 50.0% |
viewingWindows | array | Inspection slots, same shape as collectionWindows. | 100.0% |
forkliftAvailable | boolean | Whether any collection slot lists a forklift on site. | 100.0% |
forkliftCapacityKg | integer | Largest forklift capacity offered, in kg. Null when the site records none. | 25.0% |
collectionContactEmails | array | Email addresses extracted from the collection/viewing block. Logistics contacts, not the consignor. | 50.0% |
collectionNotes | string | The prose left in that block once the addresses are extracted - collection deadlines, a contact name, a phone number. | 33.3% |
| Every percentage in both tables above is a literal count over real captured rows — 568 lot-list | |||
| rows across 14 auctions for the core fields, 48 lot pages across those same 14 auctions for the | |||
detail fields. null, "", "-", "N/A" and an empty array are all counted as not filled. | |||
offline_validate.mjs re-derives every one of them from fixtures/ and fails if a number here | |||
drifts from the code. Run node offline_validate.mjs yourself. |
A real row
This is not an illustrative example. It is an actual row this code produced from the captured bytes,
reproduced field-for-field by the validator. The long arrays (specs, imageUrls,
otherLanguageUrls, collectionWindows, description) are shown separately below only so the block
stays readable.
{"lotUrl": "https://www.troostwijkauctions.com/en/l/mori-seiki-ntx1000-cnc-lathe-A7-50158-6","displayId": "A7-50158-6","title": "Mori Seiki - NTX1000 - CNC lathe","lotId": "717bab5d-74f9-410e-a0bf-3fda05bf0023","itemId": "72b38e6d-0fa2-4a4a-aa37-2b66e7d04393","urlSlug": "mori-seiki-ntx1000-cnc-lathe-A7-50158-6","closesAt": "2026-10-13T08:06:00.000Z","closesAtUnix": 1791878760,"minutesToClose": 35406,"opensAt": "2026-09-17T08:00:00.000Z","currentBid": 60000,"currency": "EUR","bidsCount": 0,"hasBids": false,"followersCount": 3,"biddingStatus": "BIDDING_OPEN","saleTerm": "OPEN_RESERVE_PRICE_NOT_ACHIEVED","city": "Nürnberg","countryCode": "de","auctionDisplayId": "A7-50158","auctionName": "CNC machinery from Deckel Maho, Hüller Hille, Emag, Mazak and other manufacturers","auctionUrl": "https://www.troostwijkauctions.com/en/a/cnc-machinery-from-deckel-maho-huller-hille-emag-mazak-and-other-manufacturers-A7-50158","auctionId": "eb232a3b-51b6-4165-99da-1584c11c609f","auctionLotCount": 16,"auctionOpensAt": "2026-09-17T08:00:00.000Z","auctionFirstLotClosesAt": "2026-10-13T08:00:00.000Z","imageUrl": "https://media.tbauctions.com/image-media/43f2f32b-229a-48f3-bd90-19af9a4e51bf/file","platform": "SPX","scrapedAt": "2026-09-18T18:00:00.000Z","detailFetched": true,"remarks": "Used machine - functional, without warranty","brand": "Mori Seiki","modelType": "NTX1000","yearOfBuild": "2012","serialNumber": "NTX10120608","category": "Metalworking","subCategory": "Lathes","categoryPath": "Metalworking > Lathes","startingBid": 60000,"reserveStatus": "MINIMUM_BID_AMOUNT_NOT_MET","quantity": 1,"condition": "NOT_CHECKED","marginScheme": false,"hasWarranty": null,"imageCount": 11,"carbonFootprintKg": 39413.88,"companyType": "COMPANY","companyId": "16239","auctioneerId": "SPX_DE","regionRestriction": "NONE","collectionStreet": "Elisenstraße 4","collectionPostalCode": "90441","collectionCity": "Nürnberg","collectionCountryCode": "de","collectionFacilityName": "Amann Werkzeugmaschinen GmbH","viewingStreet": null,"viewingPostalCode": null,"viewingCity": "Nürnberg","viewingFacilityName": null,"forkliftAvailable": false,"forkliftCapacityKg": null,"collectionContactEmails": ["j.berchner@amann-wzm.de","project.de@surplex.com"],"collectionNotes": "Julian Berchner, +49 171 5303 918,"}
Its specs array, verbatim:
| Attribute | Value | Unit |
|---|---|---|
| Brand | Mori Seiki | |
| Type | NTX1000 | |
| Year of build | 2012 | |
| Serial number | NTX10120608 | |
| Turning Ø | 370 | mm |
| Turning length | 424 | mm |
| Spindle boring Ø | 65 | mm |
| Speed from n= | 0 | rpm |
| Speed till n= | 12000 | rpm |
| Number of tool changer stations | 10 | |
| Coolant pump | true | |
| Chip conveyor | true | |
| CNC control, brand, type | MSX-711-IV | |
| Engine power (kW) | 45 | kW |
| Machine weight | 10500 | kg |
| Dimension l | 2710 | mm |
| Dimension w | 2430 | mm |
| Dimension h | 2400 | mm |
| Terms of delivery | Loaded on truck |
And its collectionWindows, which is where the collection contact comes from:
[{"from": null, "to": null, "byAppointment": true,"isDeliverySchedule": false, "forkliftAvailable": false, "forkliftCapacityKg": null,"contactNote": "Julian Berchner, +49 171 5303 918,","emails": ["j.berchner@amann-wzm.de"]},{"from": "2026-10-23T06:00:00.000Z", "to": "2026-10-23T14:00:00.000Z", "byAppointment": false,"isDeliverySchedule": false, "forkliftAvailable": false, "forkliftCapacityKg": null,"contactNote": null, "emails": []}]
A CNC lathe at EUR 60,000, closing on a known date, sitting at a named facility with a street address, an inspection slot, and a contact for arranging collection. That is the row.
Inputs
What to scrape
| Input | What it does |
|---|---|
auctionUrls | Auction URLs, slugs or display ids (A1-49582). Empty = the whole live board. An auction that has already closed is reported, not billed. |
Filter the live board
| Input | What it does |
|---|---|
countryCodes | Lot's own country, ISO-2 lower case. 21 available. |
platforms | TWK / SPX / VAVATO — the storefront serves all three. |
keywords | Case-insensitive match on the lot title. Title is the only text the list surface publishes. |
closingWithinHours | Only lots closing inside the window. The cheapest filter here — see below. |
onlyWithBids | Only lots that already have a bid (37.5% of live lots do). |
minCurrentBid / maxCurrentBid | Current bid band, in EUR. Measured range across 568 rows: EUR 1 to EUR 60,000, median EUR 700. |
includeNotYetOpen | Include the 14 pre-announced auctions. Off by default — they usually publish no lots yet. |
Order and depth
| Input | What it does |
|---|---|
auctionOrder | closingSoonest (default), newest, mostLots, siteOrder. A capped run should hand you the lots about to close, not an arbitrary slice of 77,868. |
maxLots | Hard cap on rows delivered — and therefore charged. Default 500, 0 = no cap. |
includeLotDetails | Fetch each lot page for the detail columns. Off by default. |
Advanced
| Input | What it does |
|---|---|
concurrency | 1–10, default 5. Measured: 5 gave 90/90 clean requests; 10 and 16 each dropped one or two to connection errors. |
useFastJsonRoute | Use the site's cheaper JSON route where possible, with automatic HTML fallback. |
proxyConfiguration | Apify proxy on by default. The site is clean through it — see below. |
closingWithinHours is nearly free, and that is not an accident
You are charged per row delivered but we pay per row examined, so a filter applied after fetching would decouple your cost from ours. Two things keep that honest here:
-
The lot list returns 48 rows per request. One 84 KB fetch yields 48 candidates, so a filter that rejects 47 of them still cost one request. Cost per examined row is ~1.75 KB.
-
The site publishes a pre-filter index and we use it. Every auction carries
minEndDate, the earliest close among its lots — and it is a genuine lower bound (verified on 14 auctions, 14/14:minEndDate <= min(lot endDate)). So an auction whose first lot closes after your window is skipped whole, all 5,784 of its lots if need be, without a single request. In the validator's own run against the captured board, a 24-hour window pruned 499 of 506 auctions and ~69,794 lots that were never fetched.
The expensive surface — lot detail, one request and ~500 KB per row — is fetched only for rows that already passed every filter and fit inside your cap. A run never pays detail cost for a row it will not deliver, and the validator asserts that ordering in the source.
How it reads the site, and the one thing that rots
The site is a Next.js app; every page server-renders its data into a __NEXT_DATA__ script block.
There is no DOM scraping and no browser — this actor runs on plain Node at 512 MB.
| Step | Request | Measured |
|---|---|---|
| 1. The board | GET /en/auctions?page=N | 11 pages, 506 auctions, closes exactly on the site's own totalSize |
| 2. Lots in an auction | GET /en/a/<slug>?page=N | 48 rows per page, terminates on hasNext:false, asserted against lots.totalSize |
| 3. Optional detail | GET /en/l/<slug> | ~65 KB on the wire, ~530 KB decoded |
The site serves the same rows a second way, as JSON at /_next/data/<buildId>/…, for 56 KB instead
of 88 KB. That URL embeds a build id that changes on every site deploy. The validator confirms
the two routes return page 2 element-for-element identically, so this actor:
- uses the HTML route as primary — it needs no build id and cannot rot;
- scrapes the build id fresh each run and never hardcodes it (the validator greps
src/to prove the literal is absent); - treats the JSON route as a bandwidth optimisation that falls back to HTML automatically on any failure. A build-id rotation mid-run is a slowdown, not an outage.
lotCount is not the lot-list total. The board's lotCount and the lot list's own totalSize
disagree slightly — measured across 14 auctions, 1,934 against 1,932, with one auction reporting
lotCount 116 against totalSize 114. The walk asserts against totalSize, because that is the
number the lot list will actually hand you.
Reliability, measured rather than asserted
All on 2026-09-18, through the Apify proxy unless noted:
| Test | Result |
|---|---|
| 60 lot-detail pages, concurrency 5 | 60/60 HTTP 200 in 33 s (1.82 req/s), 65,030 B avg |
| 30 auction lot-list pages, concurrency 5 | 30/30 HTTP 200 in 20 s (1.50 req/s), 84,003 B avg |
| Same 60 at concurrency 10 | 58/60 — two connection failures |
| Same 60 at concurrency 16 | 59/60 — one connection failure |
Apify proxy, auto pool | 3/3 HTTP 200 |
| Apify proxy, RESIDENTIAL + country NL | 3/3 HTTP 200, full __NEXT_DATA__ |
| Full board walk, 11 pages | 11/11 HTTP 200 |
| 48 lot pages from a bare home IP | 47 first-try, 1 transient 403 that returned 200 on one retry |
That single 403 is the only anti-bot signal on this target, and its body was ordinary lot HTML with
the lot's own <title> — a rate-limit blip, not a challenge page. The actor therefore retries 403
on a fresh proxy session, which most builds do not. No browser, no cookie mint, no challenge solve is
needed anywhere.
What happens when something does go wrong
- A non-200, or a 200 carrying a block page, is a transport failure, never end-of-data. Retried on a fresh proxy session, up to 4 attempts.
- A body that arrives short is rejected before parsing. The
__NEXT_DATA__block sits near the end of a 890 KB document, so a body truncated mid-stream parses to "no data" rather than to an error — the validator feeds it a real 120,003-byte truncation to prove the rejection fires. - One unreadable auction does not abort the run; it is recorded in
RUN_SUMMARYand the walk continues. The run only fails if more than half the auctions are unreadable — that is a block, not bad luck. - Deliberate aborts use
Actor.fail(), never a barethrow(which the runtime's uncaught-exception net swallows, leaving a broken run reading SUCCEEDED). - Both walks are floor-asserted against the site's own published totals. A short read is refused rather than reported as a small site.
Two real runs, start to finish
Both on 2026-09-18 against the live site, through the Apify proxy at concurrency 5.
Run A — the default shape (maxLots: 40, detail off, closing-soonest):
board: 504 auctions (site reported 504), 11 pages, 11/11 HTTP 200482 of 504 auctions passed the auction-level filters40 lots delivered and charged, from 48 lot rows examined (1.2 examined per delivered)40/40 carry a real close time; every core field 100% filled24 seconds wall clock, container boot included
Run B — a deliberately selective filter (countryCodes: ["de"], closingWithinHours: 72,
detail on, maxLots: 12):
the closing-time pre-filter skipped 426 auctions outright, plus 47 on country and 8 empty~75,279 lots were never fetched at all6 auctions opened, 1,071 lot rows examined, 12 delivered and charged (89.3 examined per delivered)12/12 carry a real close time; 12 lot pages enriched, 0 unreadableone transient NGHTTP2_REFUSED_STREAM, retried on a fresh session and succeeded
Run B is the expensive case on purpose — a filter that matches about 1% of what it reads. Even
there the ratio is 89 examined rows per delivered row, against a break-even near 2,200: one
lot-list request costs roughly $0.000037 of compute and returns 48 rows, so $0.0017 of revenue buys
about 46 requests. The ratio is reported in RUN_SUMMARY as lotRowsExaminedPerDelivered on every
run, so you can see it rather than take this on trust.
Billing
One event, lot-scraped, $0.0017 per row delivered. No start fee. No separate detail event.
Not charged, and kept apart from each other in RUN_SUMMARY so you can tell them apart:
- lots rejected by your filters (never delivered),
- whole auctions skipped by the closing-time pre-filter (never even fetched),
- requested auctions that are no longer on the live board (reported by id),
- lot pages that could not be read after retries (reported with the URL).
Actor.pushData(items, event) bills per item and respects your charge cap, so delivered equals
charged. The cap is checked against rows produced, not rows flushed — a cap checked against
flushed batches lags by a whole chunk.
RUN_SUMMARY
Written to the key-value store on every run. It reconciles what the site said was there against what was actually read:
- live auctions the site reported vs. auctions collected vs. board pages read;
- auctions opened, auctions skipped and why, and how many lots sat behind the skipped ones;
- lot rows examined vs. delivered;
delivered == charged; everyOpenedAuctionWalkedToItsOwnTotal— a per-auction completeness flag;closeTimeIntegrity— the share of delivered rows carrying a real close time, which is the claim this actor is built around;fieldFillOnDeliveredRows— per-field fill counted on the rows this run actually produced, not quoted from this README;- the four kinds of nothing, listed above.
Honest limits
There is no consignor email, phone or company name, and there never will be. Consignors here are
bankruptcy estates and liquidators, and the platform keeps them anonymous on purpose. Over 48 real
lot pages: zero email or phone fields anywhere on the lot object. companyId is a numeric
account id (16239), not a name — it is shipped as an id and labelled as one.
What is published, and what this actor ships instead, is the physical facility: street, postcode,
city, country and facility name on 91.7% of lots, with inspection and collection windows, forklift
availability and — on 50.0% — an email for arranging collection, plus 33.3% carrying a contact
name, phone or collection deadline in collectionNotes. Those are logistics contacts at the handling
or consigning company, not the beneficial seller, and they are labelled that way. On 8.3% of lots
an auctioneer has typed an address into the free-text remarks; it is left there in context rather
than promoted into a contact column.
condition is not a condition grade. The site publishes the field on 100% of lots, but across 48
real lot pages every value was either NOT_CHECKED (46) or NOT_APPLICABLE (2). It is returned
verbatim because both store rivals advertise it and you will look for it — but do not build a
condition filter on it. packaging and appearance carry exactly the same two placeholders and are
cut rather than shipped as constant columns.
No all-in price estimate. The buyer's premium and VAT are revealed only inside the authenticated
bid-confirmation flow — the site's own words are "before you confirm, you'll see a full overview of
all amounts related to your bid, including buyer's premium and VAT". They are in no public byte this
actor reads, so no premium, VAT or all-in figure is offered. currentBid is the hammer figure only.
Budget roughly 25–45% on top; confirm against the auction's own terms.
No coordinates. lot.location publishes {city, countryCode} and nothing else — 0 of 48 lot
pages carried a latitude or longitude. The collection street and postcode this actor ships geocode
precisely; a fabricated point would not.
Closed and sold lots are out of scope. This actor reads the live board. The site's sitemaps carry ~205,000 mostly-closed lot URLs, and status query parameters do not reopen them on the auction page — a realized-price archive is a genuinely different product with a different enumeration, and pretending otherwise would ship a dataset whose close-time column is meaningless.
Fields cut for being empty, each with its count: videos (0 of 48), packaging and appearance
(placeholder-only), onboarding (false on 48 of 48), bsc (an undocumented float with no published
meaning), assignedExplicitly (internal auction-ops flag). documentUrls is only 8.3% full but is
kept, because when a spec sheet or inspection report is attached it is worth having.
Bids arrive late. The site soft-closes: a bid in the final 2 minutes extends the lot by 2 minutes.
A snapshot taken hours before close is inventory data, not a price. Schedule the run inside
closingWithinHours if you want prices that mean something.
The board moves under you. totalSize read 507, then 506, then 505 over about an hour of probing.
The walks tolerate 1% of drift and refuse anything worse.
Source and legal
Public listing data from www.troostwijkauctions.com. No login, no paywall, no personal data beyond
the business collection contacts described above. This is an independent tool, not affiliated with or
endorsed by TBAuctions, Troostwijk Auctions, Surplex or Vavato.
robots.txt at the time of building, quoted verbatim (also captured in fixtures/robots.txt):
User-agent: *Allow: /Disallow: */search*Disallow: *categoryLevel=*Disallow: *totalSize=*Disallow: *countries=*Disallow: *brands=*Disallow: *auctions=*Disallow: *amounts=*Disallow: */login?callbackUrl=*Disallow: */signup?callbackUrl=*
This actor reads three paths — /en/auctions?page=, /en/a/<slug> and /en/l/<slug> — and none of
them matches a Disallow rule. Every filter in this actor is applied on our side, against rows
the plain default listing already returned, precisely so that no request ever carries countries=,
brands=, amounts=, categoryLevel=, totalSize= or the search path. The validator generates
seven representative URLs from the real URL builders and tests each against all nine rules.
Verify it yourself
node offline_validate.mjs
No network, no Apify account, no npm install. It re-derives every number in this README from the
real bytes in fixtures/ — the close-time asymmetry, every fill percentage, the route equivalence,
the truncation rejection, the billing ordering, the schema/code agreement, and the sample row above.