Have a question?

eBay scraper API Scraping-bot.io extracting product data from eBay listings

How to scrape an eBay product page

5 min read
eBay Scraper API: How to Scrape eBay Product Pages
Web Scraping 10 min read  ·  Published: 07/05/2026

eBay Scraper API: How to Scrape eBay Product Pages

An eBay scraper API lets you extract product titles, prices, stock levels, delivery costs, EAN codes, and descriptions from any eBay listing — automatically and at scale. Whether you are monitoring competitor prices, building a price comparison tool, or studying market fluctuations, this guide shows you exactly how to use Scraping-bot.io to collect eBay product data with Python and Node.js examples.

1. Why scrape eBay product pages?

eBay hosts hundreds of millions of active listings across thousands of categories. Collecting that data manually is simply not feasible — even a few hundred product pages would take hours to process by hand. Fortunately, an eBay scraper API automates the entire process, giving you structured, up-to-date data in seconds.

In practice, here are the most common use cases teams build with eBay scraping:

Use caseWhat you collectHow you use it
Price monitoringCurrent price, best offer, buy-it-now priceTrack competitor pricing and get alerted on changes
Price comparisonPrice + shipping across multiple sellersFeed a comparison widget or marketplace aggregator
Market researchSold prices, listing age, category trendsIdentify demand patterns and seasonal fluctuations
Inventory managementStock levels, seller ratings, conditionAutomate restocking decisions based on competitor stock
Product catalogue enrichmentEAN, brand, category, images, descriptionsPopulate your own database with structured product data

2. What data can you extract from an eBay product page?

A typical eBay product page contains far more structured data than what is visible at first glance. With Scraping-bot.io's eBay scraper API, you can reliably extract all of the following fields:

FieldExample value
Product titleApple iPhone 15 Pro 256GB Natural Titanium — Unlocked
Current price€899.99
Original price€1,099.00
Delivery costFree delivery
Stock / quantity available12 available
ConditionNew / Used / Refurbished
Seller name & ratingtech_store_eu (99.8% positive)
EAN / Item number194253716433
Product categoryMobile Phones & Communication > Smart Phones
Product descriptionFull HTML description block
ImagesArray of high-resolution image URLs
Item specificsBrand, Model, Storage, Colour, Network...

3. Setting up the eBay scraper API

Prerequisites

  • A Scraping-bot.io account — your username and API key are in your dashboard
  • Python 3.8+ or Node.js 18+
  • The eBay product URL(s) you want to scrape
💡 Free plan: Scraping-bot.io offers 100 free credits per month — no payment information required. Sign up at scraping-bot.io and make your first eBay scraper API call in minutes.

Why eBay requires a headless browser

eBay loads much of its product data — prices, stock levels, item specifics — via JavaScript after the initial page load. Consequently, a simple HTTP request to the raw HTML will often return an incomplete page with missing fields. In addition, eBay actively detects and blocks datacenter IPs, which means standard requests frequently fail on the first attempt. For these reasons, always use waitForNetworkIdle: true in your API options, and switch to premiumProxy: true whenever you encounter a CAPTCHA or a 403 response.

Basic API call in Python

import requests
import base64

USERNAME = "your_username"
API_KEY  = "your_api_key"
creds    = base64.b64encode(f"{USERNAME}:{API_KEY}".encode()).decode()

def scrape_ebay(url, premium_proxy=False):
    """
    Scrape an eBay product page using Scraping-bot.io.
    Returns the full rendered HTML as a string.
    """
    response = requests.post(
        "https://api.scraping-bot.io/scrape/raw-html",
        headers={
            "Authorization": f"Basic {creds}",
            "Content-Type":  "application/json"
        },
        json={
            "url": url,
            "options": {
                "premiumProxy":       premium_proxy,
                "waitForNetworkIdle": True
            }
        }
    )
    response.raise_for_status()
    data = response.json()

    if data.get("captchaFound"):
        raise RuntimeError("CAPTCHA detected — retry with premiumProxy=True")
    if data["statusCode"] != 200:
        raise RuntimeError(f"Unexpected status: {data['statusCode']}")

    return data["html"]

html = scrape_ebay("https://www.ebay.com/itm/123456789012")
print(f"Page size: {len(html)} characters")

Basic API call in Node.js

const fetch = require("node-fetch");

const USERNAME = "your_username";
const API_KEY  = "your_api_key";
const creds    = Buffer.from(`${USERNAME}:${API_KEY}`).toString("base64");

async function scrapeEbay(url, premiumProxy = false) {
  const res = await fetch("https://api.scraping-bot.io/scrape/raw-html", {
    method: "POST",
    headers: {
      "Authorization": `Basic ${creds}`,
      "Content-Type":  "application/json"
    },
    body: JSON.stringify({
      url,
      options: { premiumProxy, waitForNetworkIdle: true }
    })
  });

  const data = await res.json();

  if (data.captchaFound) throw new Error("CAPTCHA — retry with premiumProxy");
  if (data.statusCode !== 200) throw new Error(`Status: ${data.statusCode}`);

  return data.html;
}

const html = await scrapeEbay("https://www.ebay.com/itm/123456789012");
console.log(`Page size: ${html.length} characters`);

4. Parsing the eBay product page

Once you have the rendered HTML, the next step is to extract the fields you need. In most cases, the most robust approach is to use CSS selectors, which are more resilient to minor HTML changes than XPath or positional indexing.

Full product parser in Python

from bs4 import BeautifulSoup
import json, re

def parse_ebay_product(html):
    soup = BeautifulSoup(html, "html.parser")
    result = {}

    # Product title
    title = soup.select_one("h1.x-item-title__mainTitle span")
    result["title"] = title.text.strip() if title else None

    # Current price
    price = soup.select_one(".x-price-primary span.ux-textspans")
    result["price"] = price.text.strip() if price else None

    # Original price (if discounted)
    original = soup.select_one(".x-price-was span.ux-textspans")
    result["original_price"] = original.text.strip() if original else None

    # Delivery cost
    delivery = soup.select_one(".ux-labels-values--shipping .ux-textspans")
    result["delivery"] = delivery.text.strip() if delivery else None

    # Stock / quantity
    qty = soup.select_one(".d-quantity__availability span")
    result["stock"] = qty.text.strip() if qty else None

    # Condition
    condition = soup.select_one(".x-item-condition-text .ux-textspans")
    result["condition"] = condition.text.strip() if condition else None

    # Seller name and rating
    seller = soup.select_one(".x-sellercard-atf__info__about-seller a")
    result["seller"] = seller.text.strip() if seller else None

    rating = soup.select_one(".x-sellercard-atf__info__about-seller .ux-textspans--SECONDARY")
    result["seller_rating"] = rating.text.strip() if rating else None

    # Images
    imgs = soup.select("div.ux-image-carousel-item img")
    result["images"] = [img.get("src") or img.get("data-src")
                        for img in imgs if img.get("src") or img.get("data-src")]

    # Item specifics (brand, model, storage, etc.)
    specifics = {}
    rows = soup.select(".ux-layout-section--item-specifics .ux-labels-values")
    for row in rows:
        label = row.select_one(".ux-labels-values__labels")
        value = row.select_one(".ux-labels-values__values")
        if label and value:
            specifics[label.text.strip()] = value.text.strip()
    result["specifics"] = specifics

    return result

# Full pipeline
html    = scrape_ebay("https://www.ebay.com/itm/123456789012")
product = parse_ebay_product(html)
print(json.dumps(product, indent=2, ensure_ascii=False))

A successful extraction returns a clean structured record like this:

{
  "title":          "Apple iPhone 15 Pro 256GB Natural Titanium — Unlocked",
  "price":          "€899.99",
  "original_price": "€1,099.00",
  "delivery":       "Free delivery",
  "stock":          "12 available",
  "condition":      "New",
  "seller":         "tech_store_eu",
  "seller_rating":  "99.8% positive feedback",
  "images":         ["https://i.ebayimg.com/images/g/...jpg", "..."],
  "specifics": {
    "Brand":   "Apple",
    "Model":   "iPhone 15 Pro",
    "Storage": "256 GB",
    "Colour":  "Natural Titanium",
    "Network": "Unlocked"
  }
}

5. Scraping multiple eBay listings at scale

When you need to monitor dozens or hundreds of eBay listings, batching your requests is essential. As a result, the following pattern processes a list of URLs with polite delays and automatic retry on CAPTCHA, rather than firing all requests at once:

import time, random, json

def scrape_ebay_safe(url, max_retries=3):
    """Scrape with automatic escalation to premium proxy on block."""
    for attempt in range(1, max_retries + 1):
        try:
            use_premium = attempt > 1   # upgrade on retry
            html = scrape_ebay(url, premium_proxy=use_premium)
            return html
        except RuntimeError as e:
            print(f"Attempt {attempt} failed for {url}: {e}")
            if attempt < max_retries:
                time.sleep(2 ** attempt)   # exponential backoff
    return None

def scrape_product_list(urls):
    results = []
    for i, url in enumerate(urls, 1):
        print(f"Scraping {i}/{len(urls)}: {url}")
        html = scrape_ebay_safe(url)

        if html:
            product = parse_ebay_product(html)
            product["source_url"] = url
            results.append(product)
        else:
            print(f"  ⚠️  Failed after all retries: {url}")

        # Polite delay between requests
        delay = random.uniform(1.5, 4.0)
        time.sleep(delay)

    return results

urls = [
    "https://www.ebay.com/itm/123456789012",
    "https://www.ebay.com/itm/234567890123",
    "https://www.ebay.com/itm/345678901234",
]

products = scrape_product_list(urls)

# Save to JSON
with open("ebay_products.json", "w", encoding="utf-8") as f:
    json.dump(products, f, indent=2, ensure_ascii=False)

print(f"Saved {len(products)} products")
💡 Tip: For price monitoring pipelines, store each scrape result with a timestamp and compare against the previous value. This way, you can detect price drops and trigger alerts automatically — without storing the full HTML each time.

6. Common errors and how to fix them

ErrorCauseFix
captchaFound: trueeBay blocked the datacenter IPSet premiumProxy: true — residential IPs bypass eBay's bot detection
statusCode: 403IP banned or geo-blockedUse premiumProxy: true with a country option matching the eBay domain
Empty price / title fieldsJavaScript not fully renderedEnsure waitForNetworkIdle: true is set
CSS selector returns NoneeBay updated its HTML structureInspect the live page and update the selector — eBay redesigns periodically
429 Too Many RequestsToo many requests in a short windowIncrease delay between requests; reduce concurrency
Missing item specificsSpecifics loaded in a lazy iframeAdd a short time.sleep(2) after the initial scrape call

7. Plans and pricing

Scraping-bot.io offers plans to suit every scale of eBay scraping project — from individual developers testing a new idea to enterprise teams running millions of requests per month:

PlanCredits / monthConcurrent requestsJS RenderingPremium proxies
Free1001
StarterFrom 1,0005
Freelancer100,00010
Startup250,00025
Business1,000,00050✅ + geo-targeting
EnterpriseCustomCustom✅ + priority support

Start with the Free plan — 100 credits per month, no payment required — and test your eBay scraper API pipeline directly from the live test tool in your dashboard. Once you are ready to scale, upgrading takes less than a minute. For large-scale or custom requirements, however, the best option is to contact us directly — we will build a plan around your specific needs.

Looking for something more specific?

Start using ScrapingBot

Ready to Unlock Web Data?
Data is only useful once it’s accessible. Let us do the heavy lifting so you can focus on insights.