Build1 distinct publisher3 min readUpdated
A dev.to writeup argues the expensive failure in eBay search scraping is not blocking but promoted listings sharing the same container as real ones, silently billed per row.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
A writeup published on dev.to makes a narrow but useful argument about eBay search scraping: the defect worth engineering against is not blocking, it is sponsored cards sitting in the same result grid and the same markup as organic listings, so any code that selects every card on the page emits them [s1c1]. On a pay-per-result Actor that is not a cosmetic problem, because every emitted row is billed, and a sponsored card that slips through is a row the customer pays for and did not ask for [s1c2].
The failure has no signal. Unlike a crash, it never announces itself; the dataset just quietly contains ads [s1c2]. And the cards are built to resist casual separation: according to the author, promoted and organic results share the same `li.s-card` container, the same title and price structure, and the same image treatment [s1c3].
Two tests survive that. The first is eBay's own placeholder title on promoted slots, the exact string "Shop on eBay" [s1c4]. The second is the one carrying the weight: a genuine eBay item has a numeric id of nine or more digits in its `/itm/` URL, and ad slots, interstitials and malformed cards do not [s1c5]. That distinction matters because it tests a required field rather than a decoration. eBay can move the "Sponsored" badge, restyle it, or render it inside a shadow root, and an item id is still either a real item id or it is not [s1c6].
Where the test runs is as important as what it tests. The check happens before the row object is constructed, so a sponsored or malformed card never becomes a ResultRow and is never billed [s1c7]. The reasoning is unglamorous and correct: a row that exists can be pushed by accident, and a row that was never built cannot [s1c8].
The rest of the piece is the same discipline applied to cheaper mistakes. eBay's older search DOM used `li.s-item`, which a great deal of published scraping code still targets, and that selector now matches nothing on the live site; matching nothing looks exactly like being blocked, which sends you debugging your proxy instead of your parser [s1c9]. Requesting the search URL as the very first call in a fresh session is a recognisable pattern of no cookies, no referer and no prior navigation, since real browsers do not arrive at a results page that way, so the session makes one warm-up request to a normal eBay page, keeps the cookie jar, and only then issues the search [s1c10]. The session uses curl_cffi with browser impersonation so TLS and HTTP/2 fingerprints match the claimed User-Agent; the author calls a Chrome UA over Python's default TLS stack the most common reason a working scraper starts failing [s1c11].
Then the tell. eBay appends "Opens in a new window or tab" to result link text, so a naive `.text()` puts that suffix inside the product title in every row, and because the text really is in the DOM it survives any test whose fixture was copied from the same broken output [s1c12]. The listing model has a similar honesty: eBay mixes auctions, fixed-price and best-offer listings in one result set, so format is inferred from card content and "unknown" is a permitted value rather than a guess [s1c13].
What to watch: whether your own per-result datasets can prove the filter fired. Row counts alone will not tell you, and neither will a green run.
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
Sponsored cards sit in the same result grid and the same markup as organic eBay listings; selecting every card on the page emits them, and the author argues this, not blocking, is the defect worth engineering against.
On a pay-per-result Actor every emitted row is billed; a sponsored card that slips through is a row the customer pays for and did not want, and unlike a crash it never announces itself because the dataset just quietly contains ads.
Sponsored and organic cards are deliberately hard to distinguish: same li.s-card container, same title and price structure, same image treatment.
One sponsored signal is eBay's own placeholder title on promoted slots, the exact string 'Shop on eBay'.
A genuine eBay item has a numeric id of nine or more digits in its /itm/ URL; ad slots, interstitials and malformed cards do not, so an item id failing that shape check marks a card as not a real listing.
The sponsored/malformed check runs before the row is constructed: build_row returns None for such cards, so the card never becomes a ResultRow and is never billed.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
First-party code detail, no external corroboration
The technical core is unusually specific for a blog post — named selectors, an exact placeholder string, a regex, parse-order code and a typed format enum — which makes the implementation claims internally checkable. But everything comes from a single vendor-authored item with no dated page capture, no test fixtures, no accuracy measurement for the sponsored heuristic and no second publisher to confirm the claimed live-site DOM or the asserted failure-frequency generalisations.
Vendor product exists; no usage evidence
The only adoption signal is the vendor's own disclosure that a per-result-priced eBay Product Listings Scraper is shipped with sponsored filtering and format classification. There are no user counts, run volumes, customer names, revenue figures or third-party deployments, so adoption beyond the publisher's own fleet cannot be assessed.
Sound technique, over-generalised framing
The specific engineering advice is modest and plausible, and the headline framing that sponsored rows silently bill customers follows directly from per-result pricing. Overstatement comes from the surrounding generalisations — that this is 'the defect worth engineering against' rather than blocking, that identifier-shape testing will survive redesigns, and that UA/TLS mismatch is the most common cause of scraper failure — none of which are measured, and all of which flatter the author's paid product.
Vendor marketing its own paid scraper
The author is Devil Scrapes, whose closing blurb sells the exact product the article's advice describes, priced per result. The piece explicitly frames trap-publishing as its marketing strategy, and each named defect maps to a feature of the paid Actor, so the commercial interest in the framing is direct and undisclosed by any counter-source.
Credible implementation account, weak verification
Confidence is limited by structure rather than plausibility: one publisher, one item, strong commercial incentive, and claims about a third party's live DOM that no supplied source can confirm. The implementation-level claims about the vendor's own code deserve moderate trust; the site-behaviour and failure-frequency claims do not.
build
Google Trends returns 200 OK with an empty body when it blocks you1 distinct publisher
build
Five Socrata registries, two working filters: 'active only' quietly no-ops in three states1 distinct publisher
build
Six MariaDB versions, one real difference: the only reason to leave 10.6 is the July 2026 clock1 distinct publisher
build
Force the tool call, then hand Lightsail a long-lived key1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 21, 2026