DEV Community

Gio Rich
Gio Rich

Posted on

Getting eBay search results and prices as JSON with Python (no API key, no browser)

I built this actor; it's a paid tool on Apify with a free trial credit.

eBay is still the biggest open price list for second-hand and new-in-box products: phones, LEGO sets, watches, car parts, graphics cards. If you flip things, price your own stock, or want to know what a used iPhone 13 actually goes for in the UK versus Germany, its search pages are the data.

eBay's official APIs need a developer account, OAuth and an application review, and the one that answered "what did this sell for" (Finding API, findCompletedItems) was switched off in 2025. I'm an 18-year-old engineering student, and I built eBay Search Scraper to turn eBay searches into clean rows instead. This post covers calling it from Python and Node, what a row looks like, and a small project: a weekly median price for one product on three eBay sites.

How it works

eBay's search page (/sch/i.html?_nkw=...) is still server-rendered HTML: every listing is an <li class="s-card"> with the title, price, condition, shipping line, location, "N sold" and the seller's feedback right in the markup. With _ipg=240 one request returns 240 listings, so 10,000 listings are about 42 requests. The actor reads those pages over plain HTTP with Crawlee and parses them with cheerio; no browser.

Three things cost real debugging time and are worth knowing if you ever scrape eBay yourself:

  • Fingerprint and IP. eBay's edge answers a Chrome, Safari or Edge TLS fingerprint coming from a server with an instant 403 "Error Page", and it answers every datacenter IP that way, Apify's shared datacenter proxies included. A Firefox desktop fingerprint over HTTP/2 from a residential IP is served normally; HTTP/1.1 is 403 again. The actor therefore defaults to Apify's residential proxy. A home IP works for a few dozen requests, then gets a "Pardon Our Interruption" challenge.
  • Prices follow the visitor. eBay converts prices and shipping into the local currency of the IP it sees: the same earbuds came back as $39.95 from a US exit and as R$ 77.98 from a Brazilian one. The actor tries exits in the eBay site's own country first, falls back to any country if that pool is rejected, and always stores priceText and currency next to the numeric price, so nothing is silently mixed up.
  • Sold listings are behind a login now. LH_Sold=1&LH_Complete=1 redirects guests to eBay's sign-in page in most regions (I tried from home and through residential proxies; sign-in every time). The actor still accepts listingType: "sold" and retries with a warmed-up session, but treat it as experimental; failed searches are not charged. Active listings are the product.

Placeholder cards ("Shop on eBay", item 123456) and the "results matching fewer words" tail are dropped, sponsored listings are flagged from the page's own JSON model, and rows are deduplicated by item id within a search.

Python example

pip install apify-client
Enter fullscreen mode Exit fullscreen mode
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")

run = client.actor("rel8ble/ebay-search-scraper").call(run_input={
    "queries": ["iphone 13 128gb"],
    "site": "ebay.co.uk",
    "conditions": ["used"],
    "buyingFormat": "buy-it-now",
    "priceMin": "100",
    "priceMax": "400",
    "sort": "price-asc",
    "maxItemsPerSearch": 240,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(f'{item["price"]} {item["currency"]} | {item["condition"]} | {item["shippingText"]} | {item["title"][:60]} | {item["itemUrl"]}')
Enter fullscreen mode Exit fullscreen mode

call() waits for the run to finish (one page takes a few seconds; large runs a few minutes). The dataset is also available as CSV or Excel from the Apify console, or over the REST API with ?format=csv.

Node example

npm install apify-client
Enter fullscreen mode Exit fullscreen mode
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });

const run = await client.actor('rel8ble/ebay-search-scraper').call({
    startUrls: ['https://www.ebay.de/sch/i.html?_nkw=lego&LH_Auction=1&_sop=1'],
    maxItemsPerSearch: 240,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const it of items) console.log(it.price, it.currency, it.bidCount, 'bids', it.timeLeft, '|', it.title);
Enter fullscreen mode Exit fullscreen mode

Pasting an eBay URL works on any country site; the filters in the URL are used as they are.

What a row looks like

A real row from a run on 2026-10-20 (wireless earbuds, ebay.com):

{
    "itemId": "126409498277",
    "title": "NEW Skullcandy Riff Wireless Bluetooth On-Ear Headphones with Mic Black",
    "condition": "Brand New",
    "price": 39.95,
    "priceText": "$39.95",
    "currency": "USD",
    "originalPrice": null,
    "discountPercent": null,
    "buyingFormat": "Buy It Now",
    "acceptsBestOffer": false,
    "bidCount": null,
    "timeLeft": null,
    "shippingText": "+$55.39 delivery",
    "shippingCost": 55.39,
    "freeShipping": false,
    "itemLocation": "United States",
    "soldCount": 340,
    "watchers": null,
    "sellerName": "alphadailydeals",
    "sellerFeedbackPercent": 99.8,
    "sellerFeedbackCount": 59300,
    "isSponsored": true,
    "categoryId": "112529",
    "imageUrl": "https://i.ebayimg.com/images/g/5p4AAOSwzAZjYuC9/s-l500.jpg",
    "itemUrl": "https://www.ebay.com/itm/126409498277",
    "site": "ebay.com",
    "listingType": "active",
    "sourceSearch": "\"wireless earbuds\", on ebay.com",
    "page": 1,
    "rank": 1
}
Enter fullscreen mode Exit fullscreen mode

A marked-down listing in the same run came with "price": 79.99, "originalPrice": 99.99, "discountPercent": 20. Auction rows carry bidCount and timeLeft ("1 Gebot" and "33 Min" become 1 and "33 Min" on ebay.de), and European sites add sellerType: "private" or "business".

Small project: a weekly median price on three sites

Say you buy used iPhone 13s and want to know where they are cheapest this week. Run the same search on ebay.com, ebay.co.uk and ebay.de, keep only rows in the site's own currency, and take the median of price plus shipping.

import statistics
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
SITES = {"ebay.com": "USD", "ebay.co.uk": "GBP", "ebay.de": "EUR"}

for site, cur in SITES.items():
    run = client.actor("rel8ble/ebay-search-scraper").call(run_input={
        "queries": ["iphone 13 128gb"],
        "site": site,
        "conditions": ["used"],
        "buyingFormat": "buy-it-now",
        "includeSponsored": False,
        "maxItemsPerSearch": 240,
    })
    totals = []
    for r in client.dataset(run["defaultDatasetId"]).iterate_items():
        if r["currency"] != cur or r["price"] is None or r.get("priceTo"):
            continue  # skip rows eBay showed in another currency and price ranges
        totals.append(r["price"] + (r["shippingCost"] or 0))
    if totals:
        print(f"{site}: {len(totals)} listings, median {statistics.median(totals):.2f} {cur}, cheapest {min(totals):.2f}")
Enter fullscreen mode Exit fullscreen mode

Run it on a schedule (Apify can run the actor on cron and call a webhook) and append the three numbers to a sheet. Three pages of 240 listings cost about $0.43.

Things to know

  • Price: $0.60 per 1,000 listings, no start fee. Apify's $5 monthly free credit covers roughly 8,000 listings.
  • Residential proxy is required and is the default. On pay-per-event pricing the compute and proxy bandwidth are paid by the developer, not added to your bill.
  • Check the currency. currency and priceText tell you what eBay actually printed; the run summary lists the currencies seen and the proxy exit country.
  • About 10,000 results per search is eBay's ceiling. Split big searches by price band, category or condition.
  • Search pages only: no descriptions, item specifics or buyer data. Seller username and feedback score are the only personal data, and they are public on every result page.

Questions or broken fields: the Issues tab on the actor page comes straight to me.

eBay Search Scraper on Apify

Need a custom scraper or a ready dataset? Hire me on Fiverr: https://www.fiverr.com/rel8ble


This article was drafted with AI and published by me.

Top comments (0)