Search eBay's consumer site for listings matching a keyword query (with category, condition, price, location, format, and sort filters) and return them as structured JSON. Supports the Sold + Completed cross-section for comp pricing. Read-only.
Works with
AI-first code editor with Composer
Before installing skills in Cursor, ensure your development environment meets these requirements:
node --versionsearch-productsExecute the skills CLI command in your project's root directory to begin installation:
Fetches search-products from ebay.com/search-products-i9m1v2 and configures it for Cursor.
The CLI shows a list of agents. Use arrow keys and space to select Cursor:
Confirm successful installation by checking the skill directory location:
Restart Cursor to activate search-products. Access via /search-products in your agent's command palette.
We perform automated surface-level scans (Gen AI Scanner, Socket, Snyk) during installation. These checks detect common vulnerabilities but do not guarantee complete security. Always review skill source code and verify the publisher's reputation before production use.
Skills execute code in your environment. Always review source, verify the publisher, and test in isolation before production.
Submit your Claude Code skill and start earning
Automate repetitive workflows and reduce manual effort
Example
Generate reports, summarize documents, draft communications
Save 3-5 hours per week on routine tasks
Learn new skills, understand complex topics, get expert guidance
Example
Explain concepts, provide examples, suggest learning resources
Accelerate learning and skill development by 2x
Enhance output quality through reviews, suggestions, and refinements
Example
Review drafts, suggest improvements, catch errors
Improve work quality by 30-40% with less effort
0
total installs
0
this week
0
upvotes
Run in your terminal
0
installs
0
this week
—
stars
| name | search-products |
| title | eBay Search Products |
| description | >- Search eBay's consumer site for listings matching a keyword query (with category, condition, price, location, format, and sort filters) and return them as structured JSON. Supports the Sold + Completed cross-section for comp pricing. Read-only. |
| website | ebay.com |
| category | marketplace |
| tags | - marketplace - listings - search - ebay - akamai - comp-pricing |
| source | 'browserbase: agent-runtime 2026-05-16' |
| updated | '2026-05-16' |
| recommended_method | browser |
| alternative_methods | - method: api rationale: >- eBay's official Browse API (api.ebay.com/buy/browse/v1/item_summary/search) is the cleanest data path but is gated behind the eBay Developer Program's production-app approval — most agents will not have a Bearer token. Lead with the API only when approved credentials are available. - method: browser rationale: >- Consumer SRP at /sch/i.html is the always-available surface. The new .s-card DOM renders mostly server-side with stable selectors. Mandatory: --verified --proxies, plus a retry-on-403 loop (~40-60% of fresh sessions get Akamai-blocked on first navigation). |
| verified | true |
| proxies | true |
Search eBay's consumer site for listings matching a keyword query (with optional category, condition, price, location, format, and sort filters) and return the matching results as structured JSON — title, item ID, condition, listing format, price, shipping, location, seller, watchers, sold/sold-date (in Sold mode), and canonical /itm/{itemId} URL per listing, plus the page-wide result count and active-filter chips. Also supports the Sold/Completed cross-section for comp pricing. Read-only — never click Buy It Now, Place Bid, Make Offer, Add to Watchlist, Add to Cart, or Sign In.
<query>" — keyword search across the whole site or scoped to a category.<query> recently sell for on eBay?" (Sold + Completed mode).<query> end in the next hour?" (LH_Auction=1&_sop=1).<query> posted in the last day?" (_sop=10&LH_TopRatedPlus=1)./itm/{itemId} directly (browser fallback below).eBay's Browse API (api.ebay.com/buy/browse/v1/item_summary/search) is the cleanest data path but is gated behind the eBay Developer Program's production-approval workflow — most agents will not have a Bearer token. The pragmatic, always-available surface is the consumer SRP at https://www.ebay.com/sch/i.html, which renders mostly as server-rendered HTML in the new .s-card layout. Lead with browser scraping. Mention the Browse API only if your environment has approved credentials.
eBay is fronted by Akamai. A bare HTTP fetch is unusable — browse cloud fetch (no proxy) is redirected to /splashui/challenge (Akamai JS interstitial), and browse cloud fetch --proxies returns 403 Access Denied from errors.edgesuite.net. You need a real Chrome session with --verified --proxies. Even with both flags, 40–60% of fresh Browserbase sessions land on the static Access Denied page on first navigation — plan for retries on 403 (see Gotchas).
Always assemble URL parameters explicitly rather than relying on the page UI. The parameter surface (verified during this skill's development against the live site):
| Parameter | Meaning |
|---|---|
_nkw=<query> | Keyword query (URL-encoded; + for spaces). The only required field. |
_sacat=<id> | Category leaf id (e.g. 183454 = CCG Individual Cards, 9355 = Cell Phones & Smartphones). Maps to eBay's nested taxonomy. |
LH_ItemCondition=<code> | 1000=New, 1500=New other, 1750=New with defects, 2000=Manufacturer refurbished, 2010=Certified refurbished, 2020=Excellent refurb, 2030=Very Good refurb, 2500=Seller refurbished, 3000=Used, 4000=Very Good (books/media), 5000=Good, 6000=Acceptable, 7000=For parts or not working. Comma-separate for unions (e.g. 1000|1500). |
LH_BIN=1 | Buy It Now only |
LH_Auction=1 | Auction only |
LH_BO=1 | Best Offer enabled |
LH_FS=1 | Free shipping |
LH_Sold=1&LH_Complete=1 | Sold + Completed — comp-pricing mode. Always pair both. |
LH_PrefLoc=<n> | 1=US Only, 2=North America, 3=Worldwide, 4=Europe, 5=Asia. |
LH_TopRatedPlus=1 | Top Rated Plus sellers only |
LH_TitleDesc=1 | Search title + description (slower, broader recall) |
_udlo=<n> / _udhi=<n> | Price min / max (storefront currency, integer dollars) |
_stpos=<ZIP>&_dmd=<mi> | Within X miles of ZIP/postal code. Without _stpos, eBay infers shipping ZIP from the proxy IP — your displayed delivery costs will depend on it. |
_sasl=<seller> | Specific seller username (paired with &_saslop=1 for "include only this seller"). |
_ipg=<n> | Items per page: 60, 120, or 240. |
_pgn=<n> | Page number (1-indexed). |
_sop=<n> | Sort: 12 Best Match (default), 1 Ending soonest, 10 Newly listed, 2 Price lowest, 3 Price highest, 15 Price+Shipping lowest, 16 Price+Shipping highest, 7 Distance: nearest. |
Example URLs (all verified to render listings during skill development):
https://www.ebay.com/sch/i.html?_nkw=vintage+Levi+501+size+32&_ipg=60https://www.ebay.com/sch/i.html?_nkw=iphone+12&LH_Sold=1&LH_Complete=1&_ipg=60https://www.ebay.com/sch/i.html?_nkw=Charizard&_sacat=183454&LH_ItemCondition=3000&_sop=15&_ipg=60If the input is already a full SRP URL, use it as-is; if augmenting, parse and merge query params.
# Mandatory flags. Without Verified+proxies you hit /splashui/challenge instantly.
SID=$(browse cloud sessions create --keep-alive --verified --proxies | jq -r .id)
for attempt in 1 2 3 4; do
browse cloud browse --connect "$SID" open "$URL"
browse cloud browse --connect "$SID" wait load
browse cloud browse --connect "$SID" wait timeout 2500
TITLE=$(browse cloud browse --connect "$SID" get title)
if [[ "$TITLE" != *"Access Denied"* && "$TITLE" != *"Pardon Our Interruption"* ]]; then
break
fi
# Release this session, spin a fresh one — eBay's Akamai blocks ~40-60% of
# Browserbase residential IPs on first connect. The cheapest path forward
# is a new session, not solving the challenge.
browse cloud sessions update "$SID" --status REQUEST_RELEASE
SID=$(browse cloud sessions create --keep-alive --verified --proxies | jq -r .id)
done
A clean SRP page-title looks like Vintage Levi 501 Size 32 for sale | eBay. A blocked one is exactly Access Denied (Akamai static error from errors.edgesuite.net) or Pardon Our Interruption... (the /splashui/challenge JS interstitial).
.s-card DOMeBay migrated the SRP to a new .s-card markup. The legacy .s-item__* selectors do not match anymore on the live site (hasSItem: false, hasSCard: true confirmed across multiple iterations). Use page.evaluate (or your harness's equivalent) with this selector set:
| Target | Selector |
|---|---|
| Card container | li.s-card (or .s-card) |
| Internal listing-tracking ID (NOT the public itemId) | li.s-card[data-listingid] |
| Canonical URL | a.s-card__link[href*="/itm/"] |
| Public itemId | regex on href: /\/itm\/(?:[^\/]+\/)?(\d{8,})/ |
| Title | .s-card__title |
| Subtitle (condition + key item-specifics) | .s-card__subtitle |
| Price (formatted) | .s-card__price |
| Image | .s-card__image img |
| Attribute rows (one row per fact) | .s-card__attribute-row, .s-card__footer--row |
| Page-wide result count | h1.srp-controls__count-heading (e.g. 776 results for vintage Levi 501 size 32, 18,000+ results for iphone 12) |
| Applied-filter chips | .srp-applied-filter, .srp-applied-filters__item |
| Category breadcrumbs (left rail) | ul.x-categories__list li (top entry is All; the highlighted leaf is the current scope) |
| Popular filters (above results) | .x-refine__main__list a |
Per card, attribute rows are a flat list of short strings like:
[
"$92.92$109.32", // price node (sale + strikethrough concatenated, no separator)
"or Best Offer", // buy-format row
"+$27.11 delivery", // shipping row
"Located in Canada", // location row
"12 watchers", // watcher count
"5% off with coupon. Max $5 off", // coupon row
"buybackboss 99.6% positive (34.3K)", // seller row (Sold-mode only — username + feedback% + score)
"View similar active items", // eBay-injected nav (skip)
"Sell one like this", // eBay-injected nav (skip)
"S4ponsored..." // sponsored marker (last row, obfuscated — see Gotchas)
]
Classify each row by regex:
| Field | Regex |
|---|---|
priceText | /^\$[\d,]/ (strip the strikethrough overlap; see Gotchas) |
buyFormat | /buy it now|or best offer|best offer accepted|auction/i |
bidCount | /^(\d+)\s+bids?\b/i |
shipping | /^\+\$|^free delivery|^free shipping|delivery$|shipping$/i |
location | /^located in /i (capture rest as country/region) |
returnsAccepted | /^free returns$/i (boolean) |
watchers | /^(\d+)\s+watchers?\b/i |
soldCount | /^(\d+)\s+sold\b/i |
coupon | /coupon|% off/i |
seller | /^(\S+)\s+(\d+(?:\.\d+)?)%\s+positive\s+\(([^)]+)\)/i — captures username + feedback% + score (e.g. 34.3K → 34300) |
sponsored | last row, after stripping U+2063 separators; test /sponsored/i (see Gotchas) |
| Skip | /^view similar active items$|^sell one like this$/i |
{
"itemId": "227321194210", // from /itm/{id} in URL
"url": "https://www.ebay.com/itm/227321194210",
"title": "Levi's 501 Men's Jeans Vintage 90s Size 32x33...",
"condition": "Pre-Owned", // first segment of .s-card__subtitle before " · "
"itemSpecifics": ["Size 32"], // remaining " · "-separated segments
"price": { "text": "$80.00", "value": 80.00, "currency": "USD" },
"wasPrice": null, // strikethrough portion if the price node is $X$Y
"listingFormat": "BuyItNow", // BuyItNow | Auction | BuyItNow+BestOffer | BestOfferAccepted | Auction+BuyItNow
"bidCount": null, // integer; null for fixed-price
"timeLeft": null, // ISO 8601 datetime if extractable from per-item detail; null for fixed-price
"shipping": { "text": "+$8.29 delivery", "value": 8.29, "free": false },
"location": { "text": "Located in United States", "country": "United States" },
"totalWithShipping": 88.29, // priced + shipping when both numeric
"imageUrl": "https://i.ebayimg.com/images/g/.../s-l500.webp",
"seller": null, // {"username":"buybackboss","feedbackPct":99.6,"feedbackScore":34300}; surfaces on most Sold-mode + some active cards
"topRatedPlus": false, // not surfaced in card markup — confirm via `LH_TopRatedPlus=1` query if needed
"returnsAccepted": false, // true when "Free returns" row present
"authenticityGuarantee": false, // appears as a row in supported categories (sneakers, watches, handbags, trading cards >$250)
"watchers": null, // from "X watchers" row
"soldCount": null, // from "X sold" row
"categoryBreadcrumbs": [], // page-wide; read from .x-categories__list once per page
"sponsored": false // see U+2063 gotcha
}
{
"resultCountText": "776 results for vintage Levi 501 size 32", // or "18,000+ results..." with comma+plus suffix
"resultCount": 776, // parse leading integer; preserve "approx" flag if "+" suffix
"appliedFilters": [{ "label": "Size: Regular 32", "removable": true }],
"breadcrumbs": ["All", "Clothing, Shoes & Accessories", "Men", "Men's Clothing", "Jeans"],
"shippingToZip": "37918", // from .b-header__row text; reflects proxy IP. Override with &_stpos=<ZIP>.
"sortOrder": "Best Match", // current value of the sort dropdown
"pageNumber": 1,
"itemsPerPage": 60
}
URL: &LH_Sold=1&LH_Complete=1. Always pair both flags — LH_Sold=1 alone is silently rewritten by eBay to the active-listings view.
In Sold mode, the same .s-card selectors apply. Distinguishing fields (verified empirically with _nkw=iphone+12&LH_Sold=1&LH_Complete=1):
41 bids.or Best Offer on active listings).buybackboss 99.6% positive (34.3K) — username + feedback% + score (34.3K → 34300, 1.4M → 1400000)..s-card__price — the listing already closed at this number. Treat priceText as soldPrice, surface soldDate only if you can extract it from a per-card caption row (one of the .s-card__caption--signal / .s-card__footer-caption slots — not consistently populated; see Honest Gap below).For input shape "list of itemIds", skip the SRP and hit each item's detail page:
https://www.ebay.com/itm/{itemId}
The same --verified --proxies session is required (same Akamai). On the detail page, extract:
h1.x-item-title__mainTitle span.ux-textspans.x-price-primary .ux-textspans (parse currency + amount).x-bid-count .ux-textspans, .ux-timer__time .ux-textspans.x-item-condition-text .ux-textspans.x-sellercard-atf__info__about-seller a (username), .x-sellercard-atf__data-item-block (feedback).ux-labels-values--shipping.ux-layout-section-evo__item--table-view dl (definition list).ux-image-carousel-item img<link rel="canonical">Buy It Now, Place bid, Make offer, Add to Watchlist, Add to cart, or Sign in. Stop at the rendered SRP / item-detail. Output is structured JSON only.browse cloud fetch is redirected to https://www.ebay.com/splashui/challenge?ap=1&appName=orch&ru=... (the JS interstitial that bare HTTP cannot pass). browse cloud fetch --proxies --allow-redirects returns a hard 403 Access Denied from errors.edgesuite.net. You need browse cloud browse with --verified --proxies against a real Chrome. Headed/headless does not matter; the JS-fingerprint check does.Access Denied HTML (title literally Access Denied, Reference #<id>.<id>.<id>.<id>, link to errors.edgesuite.net) or shows Pardon Our Interruption... (splash UI). Treat 403 as "rotate to a new session and retry", up to 3–4 attempts. Empirically 2–3 rotations reach a clean IP. Do not try to solve the splash UI; cycling sessions is cheaper and faster.https://api.ebay.com/buy/browse/v1/item_summary/search requires a production-app OAuth Bearer issued through eBay's Developer Program, which gates production access behind a partner-approval review (typically multiple weeks). Sandbox tokens point at a different host and return only test inventory. Without approved credentials, browser scraping is the only path. If the agent has credentials, lead with the API.m.ebay.com) and the RSS feed (&_rss=1) are also Akamai-gated — both returned 403 with proxies during testing. Don't bother bouncing through them..s-card markup, not legacy .s-item__*. Verified across 4+ iterations: hasSCard: true, hasSItem: false. Selectors like .s-item__title, .s-item__price, .s-item__seller-info-text do not match on the live site. The new class set is .s-card__title, .s-card__subtitle, .s-card__price, .s-card__attribute-row, .s-card__footer--row..s-card is always a "Shop on eBay" placeholder card with title="Shop on eBay", fake price="$20.00", fake href="https://ebay.com/itm/123456?..." and &hash=item123546. Filter it out by itemId === "123456", title === "Shop on eBay", or the absence of data-listingid on the <li>.S4ponso Y red... — visually reads "Sponsored" but a naive text.includes("Sponsored") returns false. Normalize before testing: text.replace(/[--]/g, '').replace(/\s+/g, '') then .toLowerCase().includes("sponsored"). The marker always appears in the last attribute row, after seller info."$92.92$109.32". Parse with /^(\$[\d,.]+)(\$[\d,.]+)?$/ and treat capture 2 as wasPrice."$0.99 to $3.00" — capture as priceMin / priceMax."Pre-Owned · Size 32", "Pre-Owned · Apple iPhone 12 · 128 GB · Unlocked", "Brand New". Split on · and take [0] as the condition; the rest is per-listing item-specifics (size, model, capacity, etc.)."18,000+ results for iphone 12" (note the +). Parse the leading integer; surface a countIsApproximate: true flag when + is present.37918 (Tennessee) — eBay's "Shipping to <ZIP>" header. The displayed delivery costs (+$7.38 delivery) are calculated against this ZIP. Override with &_stpos=<ZIP>; useful when the caller needs delivery cost from a specific origin.LH_Sold=1 alone is silently rewritten to the active-listings view. Always pair with LH_Complete=1. The "2 filters applied" pill on the controls bar is your confirmation; the result count text does NOT contain the word "sold"."2d 4h" (display-only). To get the precise ISO end-time, navigate to the item-detail page and read .ux-timer__time plus the <meta itemprop="endDate"> if present.buybackboss 99.6% positive (34.3K)), and inconsistently on active cards. When LH_TopRatedPlus=1 filter is applied, the seller-info row reliably surfaces with a "Top Rated Plus" badge before the username.data-listingid ≠ itemId. The data-listingid="2500219655424533" attribute on <li> is an internal listing-tracking ID (used for impression analytics), NOT the public itemId you'd use in /itm/{id}. Always parse the itemId from the /itm/(\d{8,}) portion of the anchor href._sacat=15709, watches _sacat=14324, handbags _sacat=169291, trading cards >$250). Detect by the literal phrase "Authenticity Guarantee" in the rows._pgn is reliable up to ~10,000 results (_ipg=240 × _pgn=42). Beyond that, eBay caps and silently re-renders page 1. For exhaustive enumeration, narrow the query (price band or date filter) instead._ipg only accepts 60, 120, 240 — any other integer is silently coerced to 60.aria-current="page" or a selected class on its <li>. Don't naïvely emit the whole list as the active breadcrumbs.Cookie consent modal sometimes appears on the first page-load and obstructs the lower half of the SRP. eBay does not block scraping if it's not dismissed, but if your DOM extraction misses listings on iter 1 and hasSCard: false, dismiss it via .gdpr-banner__close and re-snapshot..s-card__attribute-row or .s-card__footer--row rows for any of the iphone-12 sold cards I inspected. The per-item detail page (/itm/{id}) exposes a Sold on <date> element under .x-item-sold-history; that's the reliable fallback when callers require sold dates. A future agent should add .s-card__caption, .s-card__caption--signal, and .s-card__signal to the row-extraction set and re-test.Active-listings shape (recommended_method = browser):
{
"success": true,
"mode": "active",
"query": "vintage Levi 501 size 32",
"sourceUrl": "https://www.ebay.com/sch/i.html?_nkw=vintage+Levi+501+size+32&_ipg=60",
"resultCount": 776,
"resultCountIsApproximate": false,
"appliedFilters": [{"label": "Size: Regular 32", "removable": true}],
"breadcrumbs": ["All", "Clothing, Shoes & Accessories", "Men", "Men's Clothing", "Jeans"],
"shippingToZip": "37918",
"pageNumber": 1,
"itemsPerPage": 60,
"listings": [
{
"itemId": "227321194210",
"url": "https://www.ebay.com/itm/227321194210",
"title": "Levi's 501 Men's Jeans Vintage 90s Size 32x33 Straight Denim USA Button Fly 1992",
"condition": "Pre-Owned",
"itemSpecifics": ["Size 32"],
"price": {"text": "$80.00", "value": 80.00, "currency": "USD"},
"wasPrice": null,
"listingFormat": "BuyItNow",
"bidCount": null,
"timeLeft": null,
"shipping": {"text": "+$8.29 delivery", "value": 8.29, "free": false},
"location": {"text": "Located in United States", "country": "United States"},
"totalWithShipping": 88.29,
"imageUrl": "https://i.ebayimg.com/images/g/TGIAAeSwI3Zp8YG1/s-l500.webp",
"seller": null,
"returnsAccepted": false,
"watchers": null,
"sponsored": false
},
{
"itemId": "177819394878",
"url": "https://www.ebay.com/itm/177819394878",
"title": "Vintage Levis 501 Button Fly Blue Dark Denim Jeans USA",
"condition": "Pre-Owned",
"itemSpecifics": [],
"price": {"text": "$92.92", "value": 92.92, "currency": "USD"},
"wasPrice": {"text": "$109.32", "value": 109.32},
"listingFormat": "BuyItNow+BestOffer",
"shipping": {"text": "+$27.11 delivery", "value": 27.11, "free": false},
"location": {"text": "Located in Canada", "countr
Prerequisites
Time Estimate
15-45 minutes depending on use case complexity
Steps
Common Pitfalls
✓ Do
✗ Don't
💡 Pro Tips
✓ Use when
Use when skill capabilities match your task, clear ROI on time saved, and you can validate outputs. Best for repetitive tasks, learning, and quality improvement.
✗ Avoid when
Avoid when task requires deep expertise you can't validate, involves sensitive decisions, or when learning process is more valuable than speed of completion.
kostja94/marketing-skills
aaaaqwq/claude-code-skills
agentbay-ai/agentbay-skills
glebis/claude-skills
shopmeskills/mcp
sundial-org/awesome-openclaw-skills
search-products has been reliable in day-to-day use. Documentation quality is above average for community skills.
I recommend search-products for anyone iterating fast on agent tooling; clear intent and a small, reviewable surface area.
Solid pick for teams standardizing on skills: search-products is focused, and the summary matches what you get after install.
search-products is among the better-maintained entries we tried; worth keeping pinned for repeat workflows.
search-products reduced setup friction for our internal harness; good balance of opinion and flexibility.
search-products fits our agent workflows well — practical, well scoped, and easy to wire into existing repos.
Solid pick for teams standardizing on skills: search-products is focused, and the summary matches what you get after install.
Useful defaults in search-products — fewer surprises than typical one-off scripts, and it plays nicely with `npx skills` flows.
search-products has been reliable in day-to-day use. Documentation quality is above average for community skills.
We added search-products from the explainx registry; install was straightforward and the SKILL.md answered most questions upfront.
showing 1-10 of 73