AlterLabAlterLab
Tutorials

How to Scrape eBay: Complete Guide for 2026

Learn how to scrape eBay listings, prices, and seller data in 2026. Bypass Akamai Bot Manager, handle JS rendering, and extract structured data at scale.

Yash Dubey
Yash Dubey

March 23, 2026

8 min read
12 views

eBay hosts over 1.7 billion live listings across every product category, with prices and availability shifting by the hour. The sold listing data alone — actual transaction prices, not asking prices — is one of the most useful secondary-market benchmarks available on the public web. This guide covers everything you need to extract that data reliably in 2026: what protections you're up against, how to get structured data out of both search and item pages, and how to scale without burning through proxies.


Why Scrape eBay?

Three use cases dominate production eBay pipelines:

Price monitoring and repricing — Resellers and retailers track eBay prices to benchmark against their own inventory and set dynamic pricing rules. A GPU listed at $650 new on a retailer's site might clear at $390 on eBay's secondary market; that spread is actionable data for sourcing, pricing, and promotion decisions.

Market research on completed listings — Appending &LH_Complete=1&LH_Sold=1 to any eBay search URL filters to sold transactions. For commodities like trading cards, vintage electronics, or rare apparel, this is the closest real-world proxy to market value — what buyers actually paid, not what sellers hoped for.

Catalog and inventory enrichment — Product data teams pull titles, condition grades, image URLs, seller ratings, and category breadcrumbs to enrich internal databases or build price comparison engines, without maintaining their own taxonomy from scratch.

1.7B+Live eBay Listings
99.2%Scraping Success Rate
1.4sAvg Response Time
190+Proxy Countries Available

Anti-Bot Challenges on eBay

eBay's protection stack is more layered than most e-commerce sites. Understanding what you're up against saves hours of debugging failed or empty responses.

Akamai Bot Manager is the primary layer. It performs TLS fingerprinting — inspecting cipher suites and extension order during the TLS handshake — to identify non-browser clients. Python's requests library produces a fingerprint Akamai's models have catalogued and block by default. This is why a raw requests.get() against an eBay search URL often returns a CAPTCHA page or a redirect rather than listings.

JavaScript-dependent rendering affects both search and item pages. Search results load listing data through client-side JavaScript calls; a static HTTP request frequently returns empty .s-item container divs. Item pages may deliver base HTML but fetch pricing and availability data via XHR that only fires inside a real browser context.

Behavioral scoring evaluates request timing, browsing sequence, and session patterns. Scrapers that hit sequential paginated URLs at uniform intervals without session continuity get scored as bots and face increasing friction — soft blocks, CAPTCHA injections, or silently degraded responses that look like valid HTML but contain no useful data.

IP reputation closes the loop. Datacenter IPs are blocked at the edge for most eBay endpoints. Residential proxies work but get burned fast at volume without smart rotation.

The AlterLab Anti-Bot Bypass API handles all of this transparently — TLS fingerprinting is matched to a real browser profile, JavaScript executes in a managed headless session, and requests route through a rotating residential pool. You send a URL and get back fully-rendered HTML.


Quick Start with AlterLab API

Install the SDK and BeautifulSoup, then grab your API key from the dashboard. The getting started guide covers account creation and your first request in under five minutes.

Bash
pip install alterlab beautifulsoup4

A minimal eBay search scrape with JavaScript rendering enabled:

Python
import alterlab
from bs4 import BeautifulSoup

client = alterlab.Client("YOUR_API_KEY")

response = client.scrape(
    "https://www.ebay.com/sch/i.html?_nkw=mechanical+keyboard&_sop=12",
    render_js=True,
)

soup = BeautifulSoup(response.text, "html.parser")
listings = soup.select(".s-item")

for item in listings[1:]:  # eBay injects a ghost template item at index 0 — always skip it
    title = item.select_one(".s-item__title")
    price = item.select_one(".s-item__price")
    link = item.select_one(".s-item__link")
    condition = item.select_one(".s-item__subtitle")

    print({
        "title": title.text.strip() if title else None,
        "price": price.text.strip() if price else None,
        "url": link["href"] if link else None,
        "condition": condition.text.strip() if condition else None,
    })

The same request via cURL — useful for inspecting raw response structure before writing a parser:

Bash
curl -X POST https://api.alterlab.io/v1/scrape \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://www.ebay.com/sch/i.html?_nkw=mechanical+keyboard",
    "render_js": true
  }'
Try it yourself

Try scraping an eBay search results page live with AlterLab


Extracting Structured Data

Search Results: CSS Selectors

eBay's search result pages use a consistent DOM structure. These selectors are stable as of Q1 2026:

FieldCSS Selector
Listing container.s-item
Title.s-item__title
Price.s-item__price
Condition.s-item__subtitle
Shipping cost.s-item__shipping
Item URL.s-item__link[href]
Thumbnail image.s-item__image-img[src]

For sold listing history, append &LH_Complete=1&LH_Sold=1 to your search URL. The DOM structure is identical to active listings.

Item Detail Pages: Prefer JSON-LD

eBay embeds application/ld+json structured data on item pages. Parsing this is more reliable than CSS selectors because it survives UI redesigns and localisation changes:

Python
import json
import alterlab
from bs4 import BeautifulSoup

client = alterlab.Client("YOUR_API_KEY")

def scrape_ebay_item(item_id: str) -> dict:
    url = f"https://www.ebay.com/itm/{item_id}"
    response = client.scrape(url, render_js=True)
    soup = BeautifulSoup(response.text, "html.parser")

    # Prefer JSON-LD — encodes price, condition, availability, and seller in one block
    json_ld = soup.find("script", {"type": "application/ld+json"})
    if json_ld:
        data = json.loads(json_ld.string)
        offers = data.get("offers", {})
        return {
            "name": data.get("name"),
            "price": offers.get("price"),
            "currency": offers.get("priceCurrency"),
            "availability": offers.get("availability"),
            "condition": offers.get("itemCondition"),
            "seller": offers.get("seller", {}).get("name"),
        }

    # Fallback: CSS selectors when JSON-LD is absent (happens on some auction pages)
    title_el = soup.select_one("h1.x-item-title__mainTitle span")
    price_el = soup.select_one(".x-price-primary .ux-textspans")
    return {
        "name": title_el.text.strip() if title_el else None,
        "price": price_el.text.strip() if price_el else None,
    }

if __name__ == "__main__":
    print(scrape_ebay_item("315012345678"))

JSON-LD is present on most Buy It Now item pages but can be absent on some auction-format listings. Always implement the CSS selector fallback.


Common Pitfalls

The ghost first listing — eBay's search DOM always includes a promotional or template item at listings[0] that doesn't represent a real result. Slice from index 1 (soup.select(".s-item")[1:]) without exception.

Missing render_js=True — This is the most common cause of blank results on eBay. The .s-item containers render empty without JavaScript execution. If your output is an empty list, render_js is almost always the culprit before anything else.

Pagination model varies by category — Traditional eBay search paginates via _pgn=2, _pgn=3. But some category landing pages have migrated to infinite scroll. Before building a paginator, verify that a "Next page" element (a.pagination__next) exists in your rendered HTML.

Description iframes — Item descriptions frequently live inside an <iframe id="desc_ifr"> pointing to ebaydesc.com. If you need full item descriptions, extract the src attribute and make a second request for that URL. The iframe content will not be present in the parent page response.

Uniform timing patterns — Requests at perfectly even intervals are a bot signal. If you're managing request scheduling yourself (rather than using the batch API), introduce a small randomised delay between requests.


Scaling Up

For paginated bulk collection, the batch API handles concurrency and retries at the request level:

Python
import alterlab
from bs4 import BeautifulSoup

client = alterlab.Client("YOUR_API_KEY")

# Five pages of search results for a single keyword
urls = [
    f"https://www.ebay.com/sch/i.html?_nkw=rtx+4080&_pgn={page}&_sop=12"
    for page in range(1, 6)
]

# Batch up to 50 URLs per call; concurrency controls parallel headless sessions
results = client.batch_scrape(urls, render_js=True, concurrency=5)

all_listings = []
for result in results:
    if result.status_code != 200:
        continue
    soup = BeautifulSoup(result.text, "html.parser")
    for item in soup.select(".s-item")[1:]:
        title = item.select_one(".s-item__title")
        price = item.select_one(".s-item__price")
        link = item.select_one(".s-item__link")
        if title and price:
            all_listings.append({
                "title": title.text.strip(),
                "price": price.text.strip(),
                "url": link["href"] if link else None,
            })

print(f"Collected {len(all_listings)} listings across {len(urls)} pages")

Cost planning — Credit consumption depends on request volume and render mode. Headless browser sessions cost more than plain fetches, so it pays to disable render_js on any pages where you've confirmed the target data exists in static HTML. Check AlterLab pricing to model costs for your pipeline before scheduling a production run.


Key Takeaways

  • eBay uses Akamai Bot Manager. Standard HTTP clients get blocked at the TLS layer before a single HTML byte is returned.
  • Always pass render_js=True for search and item pages unless you've explicitly verified the content is in the static HTML response.
  • Prefer application/ld+json on item pages over CSS selectors — it's more stable across redesigns and surfaces price, condition, availability, and seller in a single parse.
  • Always skip listings[0] on search pages — it's a ghost/template item injected by eBay's frontend.
  • For actual transaction data rather than asking prices, append &LH_Complete=1&LH_Sold=1 to any search URL.
  • Keep batch concurrency between 5 and 10 for eBay specifically. More parallel sessions past that threshold doesn't increase throughput and burns through proxy pool capacity.

Building a multi-platform price intelligence pipeline? These guides cover the other major marketplaces:

  • How to Scrape Amazon — Amazon's AWS WAF and dynamic ASIN pages require different anti-bot handling than eBay.
  • How to Scrape Walmart — Walmart.com runs Cloudflare and has a stricter per-IP rate limit profile.
  • How to Scrape AliExpress — Heavy JavaScript rendering, geolocation redirects, and currency localisation add significant complexity.
Share

Was this article helpful?

Frequently Asked Questions

Scraping publicly accessible eBay pages is generally permissible under US case law following hiQ v. LinkedIn, which held that accessing public data does not violate the CFAA. However, eBay's User Agreement restricts automated access, so consult your legal team before using scraped data commercially. Stick to publicly visible listings and avoid authenticated endpoints or personal user data.
eBay runs Akamai Bot Manager, which combines TLS fingerprinting, JavaScript execution checks, and IP reputation scoring — making standard HTTP libraries like `requests` ineffective out of the box. AlterLab's Anti-Bot Bypass API handles all three layers transparently: it spoofs a browser-grade TLS fingerprint, executes JavaScript in a headless browser, and routes requests through a rotating residential proxy pool so your scraper receives fully-rendered HTML without writing any bypass logic yourself.
Cost depends on request volume and whether you need JavaScript rendering — headless browser sessions consume more credits than simple fetches. AlterLab's pricing page breaks down credit costs per request type so you can model your pipeline before committing. Most eBay search and item pages require `render_js=True`, so budget accordingly and disable it on any pages where you've confirmed the target data exists in static HTML.