How to Scrape eBay Listings in 2026
How to Scrape eBay Listings in 2026
eBay is the easiest major e-commerce target to scrape. Unlike Amazon, the anti-bot is mild. Unlike Twitter/LinkedIn, the official API is genuinely useful. The CSS structure of search-result pages has been stable for years.
The honest verdict first
| Use case | Recommendation |
|---|---|
| Active listings by keyword | eBay Browse API (free, 5k req/day) |
| Sold listings (price history) | DIY scraping (Browse API doesn't expose sold listings cleanly) |
| Bulk competitor pricing | Browse API + parallelization |
| Real-time auction monitoring | DIY (poll the listing every 30s) |
| Build a price-tracking product | Combine API + scraping for sold-listings completeness |
The official Browse API
developer.ebay.com — sign up, get a Production app key, you're done. Free tier: 5,000 calls/day.
import requests
EBAY_KEY = "YOUR_KEY"
def search_listings(keyword: str, n: int = 50) -> list[dict]:
r = requests.get("https://api.ebay.com/buy/browse/v1/item_summary/search",
params={"q": keyword, "limit": n},
headers={"Authorization": f"Bearer {EBAY_KEY}",
"X-EBAY-C-MARKETPLACE-ID": "EBAY_US"})
return r.json().get("itemSummaries", [])
for item in search_listings("rare books", 25):
print(item["title"], "—", item["price"]["value"], item["price"]["currency"])
Returns title, price, image URL, item ID, condition, seller, location, watch count, free-shipping flag — everything you'd extract from the HTML.
The DIY scraping path (for sold listings)
Sold-listings price history is the one thing the API doesn't expose well. For competitive pricing analysis:
import time
from random import uniform
from curl_cffi import requests as cf
from bs4 import BeautifulSoup
def ebay_sold_listings(query: str, n: int = 60) -> list[dict]:
url = "https://www.ebay.com/sch/i.html"
params = {
"_nkw": query,
"LH_Sold": "1", # filter to sold listings
"LH_Complete": "1", # completed listings only
"rt": "nc",
"_ipg": "60", # 60 per page
}
r = cf.get(url, params=params, impersonate="chrome131", timeout=20)
if r.status_code != 200:
return []
soup = BeautifulSoup(r.text, "html.parser")
results = []
for li in soup.select("li.s-item")[:n]:
title_el = li.select_one(".s-item__title")
price_el = li.select_one(".s-item__price")
sold_el = li.select_one(".s-item__title--tag, .s-item__caption")
link_el = li.select_one("a.s-item__link")
if not (title_el and price_el and link_el):
continue
results.append({
"title": title_el.get_text(strip=True).replace("New Listing", "").strip(),
"price": price_el.get_text(strip=True),
"sold_date": sold_el.get_text(strip=True) if sold_el else None,
"url": link_el["href"].split("?")[0],
})
return results
# Usage
sold = ebay_sold_listings("first edition Hemingway", n=60)
for s in sold:
print(s["title"], "—", s["price"], "—", s["sold_date"])
time.sleep(uniform(2.0, 4.0))
eBay tolerates this kind of slow polling (1 request per 2-4 seconds) without challenging. At higher volume (>10 req/sec), expect rate-limiting.
Combining API + scraping for full coverage
The clean pattern for competitor-pricing or condition-trend analysis:
- Use the Browse API for live active listings — no scraping, no anti-bot, no maintenance.
- Scrape sold-listings pages for the historical data — slow, polite, refreshed monthly.
- Merge into a single dataset — keyed by item-title-fuzzy-match.
This gives you the price-now (API) and price-history (scraped) views of the same product type, with maximum API quota usage and minimum scraping risk.
Auction-end monitoring
For bidding-window or auction-ending alerts, the Browse API has an endingTimeFrom filter:
import datetime as dt
def ending_soon(query: str, hours: int = 6, n: int = 50) -> list[dict]:
end = (dt.datetime.utcnow() + dt.timedelta(hours=hours)).isoformat() + "Z"
r = requests.get("https://api.ebay.com/buy/browse/v1/item_summary/search",
params={"q": query, "filter": f"endingTimeTo:[..{end}]", "limit": n},
headers={"Authorization": f"Bearer {EBAY_KEY}"})
return r.json().get("itemSummaries", [])
Combine with cron (every 30 min) and a Slack webhook for "auctions ending today matching my watch list" alerts.
Real-world demo
See portfolio_demos/competitor_watch/ in the repo for the recurring-monitor pattern. Same template works for eBay watch-list monitoring.
Legal & ToS
eBay's ToS allows API access (that's what the API is for). Direct HTML scraping sits in a grey zone — they tolerate it for reasonable-volume personal use; they pursue larger commercial scrapers with C&D letters.
Practical rules:
- Use the API for anything you'll deploy or commercialize
- Scrape sold-listings only at slow speeds (<1 req/sec)
- Don't republish the data as a competing search product
- Don't scrape user data (seller profiles, buyer-feedback histories) — that's the high-risk territory
What to read next
- Self-Healing AI Web Extractors — useful when eBay's CSS structures eventually drift
- How to Scrape Amazon — the harder e-commerce target
- Web Scraping Tools Comparison
For an eBay-data brief, send to info@luba.media. Quick fixed-price gigs ($150-500), most ship in 1-3 days using the API + selective scraping.
Hire me to build this for your site
I quote fixed-price and ship in 7-10 days. Send a brief to info@luba.media.
Send a brief