All notable changes to this actor are documented here.
[1.1] - 2026-08-29
Fixed — critical: actor returned 0 results on every run
Root cause:warm_up_ebay() in src/product.py discarded the cookies
collected from the eBay homepage warm-up request whenever that request came
back with an HTTP status of 400 or higher:
# before
if r.status_code >=400:
return{}
try:
returndict(r.cookies)
Live testing (2026-08-29, direct — no proxy) showed eBay's Akamai bot
manager serves the homepage (https://www.ebay.com/) as an HTTP 403 on
every single request to this fetch profile, regardless of TLS
impersonation — but that 403 response still sets the bm_s / bm_so
challenge cookies, and those specific cookies are exactly what the
following search/item request needs to get a real HTTP 200:
Cold search request (no cookies at all): 403 on 3/3 trials, 0 product
links extracted.
Same search request replayed with the cookies harvested from the 403
warm-up response: 200 on every trial, 60 product links extracted with the
existing li.s-card > a.s-card__link selector.
Because the homepage warm-up is always 403, warm_up_ebay()always
returned {} in production. session_cookies was therefore always falsy,
nav_headers was always None, and every search/product fetch for every
run went out cold — which always got 403'd — which always yielded 0
product URLs and 0 dataset rows. This reproduced on every run because it
had nothing to do with proxies, selectors, or eBay markup changes; it was a
straight logic bug that threw away the one thing the warm-up request was
for.
Fix (src/product.py, warm_up_ebay()): stop discarding cookies based
on status code. Only a hard transport failure (timeout, connection error,
TLS handshake failure) should return no cookies; any response that made it
back — 403 included — has its cookies harvested, because the cookies are
the payload, not the status code.
Fixed — data quality: titles/descriptions/categories corrupted when they contained a quote, ampersand, etc.
Root cause: eBay's own application/ld+jsonProduct block embeds
literal HTML entities inside the JSON string values (e.g. the name field
for a laptop listing contained 14" FHD instead of 14" FHD).
json.loads() only interprets JSON string escapes, not HTML entities, so
any title/description/breadcrumb name sourced from JSON-LD came out with
raw ", &, etc. still in it — live-confirmed on a real listing
(itm/206429424062, ASUS ExpertBook, title contained 14" FHD).
Fix (src/extract_product_data.py): html.unescape() is now applied to
every string pulled out of a JSON-LD block before it is used —
_title()'s product["name"], _subtitle()'s product["description"],
and _breadcrumb_list_names()'s itemListElement[].name. (DOM-sourced text
via BeautifulSoup's get_text() was never affected — bs4 already decodes
HTML entities when parsing markup; only the raw JSON-LD string path needed
the explicit unescape.)
Verified
Both fixes were verified against real, live https://www.ebay.com pages
(not fixtures/mocks): a real search results page
(https://www.ebay.com/sch/i.html?_nkw=laptop) discovered 60 product URLs
via the existing primary selector, and 3 real item pages were fetched and
parsed end-to-end, each yielding a full, non-empty product record with a
correctly-decoded title, real price, seller, item location and sold count.
Changed
Bumped actor version 1.0 → 1.1 in .actor/actor.json.
Verified but unchanged (checked while investigating this issue)
.actor/actor.json's relative paths (../Dockerfile, ../README.md,
./input_schema.json, ./output_schema.json, ./dataset_schema.json)
all correctly resolve relative to the .actor/ directory and every
target file exists — not part of the bug.
The search-page discovery selector (li.s-card > a.s-card__link) and the
item-page extraction logic in extract_all_data() both work correctly
against eBay's current live markup; they were never the problem.