Find Below-Market Vintage Cars on Bring a Trailer
Purpose
Scan Bring a Trailer's live auctions for a set of collectible car models and surface the ones whose current bid is below that model's historical BaT sale median, along with the comps that justify the call. For each model it returns the market band (p25 / median / p75 from past BaT sales), every currently-running auction that matches the model, and per-listing signals — current bid, percent-of-median, days left, and no-reserve flag — so the caller can judge whether a low number is a real bargain or just an auction that hasn't been bid up yet. Read-only: it never bids, watches, comments, or logs in.
When to Use
- "Find first-gen Broncos / Datsun 240Zs / Pagoda SLs currently going below their usual BaT price."
- A collector or dealer monitoring live auctions for buying opportunities across a watchlist of models.
- Building an alerting agent that flags undervalued lots ending soon.
- Any time you'd otherwise hand-scrape BaT auction cards — the inline JSON feed is faster, complete, and structurally reliable.
Workflow
BaT is a WordPress site that embeds everything you need as inline <script> JSON,
and both the live-auction page and each model page return that JSON to a plain
HTTP GET — no login, no cookies, no anti-bot stealth. The optimal method is
therefore fetch + parse, not scripted browsing. browse cloud fetch <url> returns
a JSON envelope {statusCode, headers, contentType, encoding, content}; the HTML is
in .content. --proxies is not required (bare GET returns 200 with the full
blob); add it only if Cloudflare ever challenges you.
-
Pull the live-auction feed (one fetch).
GET https://bringatrailer.com/auctions/→ in.content, find the markervar auctionsCurrentInitialData = {and brace-match the object..itemsis the complete live set (~960 lots). Each item has:title,current_bid(integer),current_bid_formatted,timestamp_end(epoch seconds),noreserve,year,url, and — critically —keyword_pages: an array of model-page IDs that is the precise join key back to a model. -
Resolve each target to its BaT model page. If you don't know the slug, hit the autocomplete endpoint:
GET https://bringatrailer.com/wp-json/bringatrailer/1.0/data/autocomplete?term=<urlenc-name>(the param isterm—q/query/keywordall return HTTP 400)..contentis JSON whoseitems[]give{title, url}, e.g.term=pagoda→mercedes-benz/w113,term=365→ four Ferrari 365 variants. -
Get the model's market price + join IDs.
GET https://bringatrailer.com/{make}/{model}/→ extractvar auctionsCompletedInitialData = {.- Market band: prefer
stats.sold[].amount— the model's entire BaT sale history in one array — and take the median (and p25/p75). Filter amounts>= $3000to drop parts/wheels/seats lots that share the model tag. - Join key:
base_filter.keyword_pages— the model's page ID(s).
- Market band: prefer
-
Match live auctions to each target. A live item belongs to a target iff its
keyword_pagesintersects the model page'sbase_filter.keyword_pages. This is far more reliable than title/keyword regex. -
Flag + rank. Mark matched live lots with
current_bid < medianas below-market candidates. Computepct_of_median = current_bid / median * 100anddays_left = (timestamp_end*1000 - now) / 86400000. Always surfacedays_leftandno_reserve— a bid at 20% of median with 6 days left is not a bargain, it just hasn't been bid up. The strongest signals are lots ending within ~1 day still well under median, and no-reserve lots (guaranteed to sell).
Resolving broad "family" models (generation drill-down)
Some names map to a family page with stats_enabled:false and an empty stats
object — you cannot get a market price from it directly. These include Camaro,
Bronco, Porsche 356, Porsche 911 GT3, and Mercedes Geländewagen. On the family page,
each generation is listed in a data-items="[...]" HTML attribute (HTML-entity
encoded — decode "→", –→-, &→&) as {id, title, url}.
Pick the generation matching the requested era and fetch that page:
- First-gen Camaro (1967-1969) →
chevrolet/camaro-1967-1969 - First-gen Bronco (1966-1977) →
ford/bronco-u13-u14-u15-1966-1977 - First-gen G-Wagen (W460) →
mercedes-benz/w460-gelandewagen - Porsche 356 → per-variant pages (
356a,356b,356c,356-speedster, …) - 911 GT3 → per-generation pages (
991-gt3,992-gt3,996-gt3,997-gt3)
Browser fallback
If plain GETs ever get Cloudflare-challenged: open a remote session
(browse open <url> --remote), let the page render, then read the same objects with
browse eval 'JSON.stringify(window.auctionsCurrentInitialData)' and
window.auctionsCompletedInitialData. Same fields, ~100x the cost — avoid unless forced.
Site-Specific Gotchas
browse cloud fetchreturns an envelope, not raw HTML. Parse the outer JSON and read the page from.content. Inside.contentthe inline blobs use normal (unescaped) quotes — brace-match from the first{aftervar NAME =.stats.soldis served intermittently (edge-cache variance). The same model URL may return a fullstats.soldarray on one fetch and omit it (emptystats) on the next — observed onmercedes-benz/w113(absent 3× then 569 sales on the 4th) andford/bronco-u13-…(1234 sales, then absent). Retry the fetch up to ~6× untilstats.soldis present. If it never appears, fall back to the 24items[]on completed page 1 (recent comps, lower confidence) and label the source.- Family pages have
stats_enabled:falseand emptystats. Don't try to price Camaro/Bronco/356/911-GT3/Geländewagen from the family page — drill into a generation page (see above).items_totalis populated but the money data is not. keyword_pagesjoin pulls in parts & projects. Chassis, seats, tube-frame race cars, and engine-swap projects share a model'skeyword_pages. The>= $3000floor removes most parts, but inspecttitlebefore calling something a "below-market car" (e.g. a "$101 Camaro-bodied tube-frame track car" or a "$39,500 chassis for Bronco" are not cars).- Trim levels have no dedicated page. "911 GT3 Touring" is a trim, not a model —
use the generation page (
porsche/992-gt3) and filtertitleby/touring/i. Same pattern for other sub-trims. - Ambiguous model names resolve to several pages.
term=365returns 365 GT 2+2, 365 GTC, 365 GTC/4, and 365 GT4/400/412 — pick the intended one or price each. - Thin markets → low-confidence median. Ferrari 365 GTC/4 (~11 sales) and INEOS
Grenadier (~24 sales) have too few comps for a stable median; report
nso the caller can weight it. current_bidmid-auction ≠ final price. BaT auctions get most of their action in the final minutes. A live bid far below median with days left is expected, not a deal. Gate "below-market" confidence ondays_left(and prefer no-reserve lots).timestamp_endis epoch seconds, andcurrent_bidon completed items is the final hammer price (sold_textdistinguishes "Sold for $X" from a no-sale/reserve- not-met listing wheresold_textis empty).- Autocomplete param is
term.?term=→ 200;?q=/?query=/?keyword=→ 400. - No anti-bot wall observed. Plain GET (no proxies, no stealth) returned HTTP 200
with the full inline feed across every page tested.
--proxiesis an optional safety net, not a requirement.
Expected Output
One object; results[] has one entry per target model. Live auctions with
below_market: true are the candidates; use days_left + no_reserve to rank
confidence.
{
"success": true,
"generated": "2026-07-07T23:00:00Z",
"live_total": 960,
"results": [
{
"target": "Datsun 240Z",
"model_page": "https://bringatrailer.com/datsun/240z/",
"market": { "n": 553, "source": "stats.sold(full history)", "p25": 17500, "median": 25000, "p75": 36500 },
"live_auctions": [
{
"title": "1972 Datsun 240Z 4-Speed",
"current_bid": 22500,
"ends": "2026-07-08",
"days_left": 0.8,
"no_reserve": false,
"pct_of_median": 90,
"below_market": true,
"url": "https://bringatrailer.com/listing/1972-datsun-240z-.../"
}
]
},
{
"target": "Mercedes-Benz W113 SL (Pagoda)",
"model_page": "https://bringatrailer.com/mercedes-benz/w113/",
"market": { "n": 555, "source": "stats.sold(full history)", "p25": 49900, "median": 66500, "p75": 88500 },
"live_auctions": [
{
"title": "30-Years-Family-Owned 1970 Mercedes-Benz 280SL 4-Speed",
"current_bid": 40000,
"ends": "2026-07-11",
"days_left": 3.8,
"no_reserve": true,
"pct_of_median": 60,
"below_market": true,
"url": "https://bringatrailer.com/listing/..."
}
]
},
{
"target": "First-gen Chevrolet Camaro (1967-1969)",
"model_page": "https://bringatrailer.com/chevrolet/camaro-1967-1969/",
"market": { "n": 876, "source": "stats.sold(full history)", "p25": 40000, "median": 55000, "p75": 77500 },
"live_auctions": [
{
"title": "1967 Chevrolet Camaro RS/SS Coupe L35 396/325 4-Speed",
"current_bid": 43150,
"ends": "2026-07-13",
"days_left": 5.8,
"no_reserve": false,
"pct_of_median": 78,
"below_market": true,
"url": "https://bringatrailer.com/listing/..."
}
]
}
],
"error_reasoning": null
}
Model with no live auctions returns an empty live_auctions: [] (e.g. INEOS
Grenadier and Ferrari 365 GTC/4 had zero live lots in the validation run). On total
failure: { "success": false, "results": [], "error_reasoning": "<what broke>" }.