ThredUp Scraper - Resale Fashion, Prices & 35+ Fields avatar

ThredUp Scraper - Resale Fashion, Prices & 35+ Fields

Pricing

from $1.50 / 1,000 listing results

Go to Apify Store
ThredUp Scraper - Resale Fashion, Prices & 35+ Fields

ThredUp Scraper - Resale Fashion, Prices & 35+ Fields

Scrape ThredUp resale listings by keyword, department, brand, size, condition, price or URL. Extract 35+ fields including brand, price, original price, MSRP, size, colours, materials, condition, category and all photos. Supports filters and sorting.

Pricing

from $1.50 / 1,000 listing results

Rating

0.0

(0)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

0

Bookmarked

6

Total users

3

Monthly active users

18 days ago

Last modified

Security

Limited permissions

Share

ThredUp Scraper: Secondhand Fashion Prices, Sizes & Photos

ThredUp Scraper turns ThredUp, one of the largest online resale and thrift marketplaces for secondhand clothing, shoes and accessories, into clean structured data. Give it keywords plus a department and filters, or paste ThredUp category and item URLs, and get brand, price, original price, MSRP, size, colors, materials, condition, category and every photo for each listing. Search and URL modes, three sort orders, and brand, category, material, condition and price filters are all built in, and you can export to JSON, CSV or Excel, or pull results straight into your app through the API.

Why This Scraper?

  • Rich fields. Returns 35+ named fields per item with no extra request: brand, price, original price, MSRP, size, condition, colors, materials, category, department and every photo URL.
  • Two ways in. Two ways to collect: keyword search with department, brand, material, style, condition and price filters, or paste ThredUp category and item URLs directly.
  • Sort orders. Four sort orders for search results: relevance, newest first, price low to high, price high to low.
  • Full details. Optional full item details on demand: description plus garment measurements (chest, waist, inseam, length).
  • Built for schedules. Built for schedules: incremental mode returns only new, updated and reappeared items on recurring runs, and a refused run fails loudly instead of quietly returning an empty dataset.
  • Resume. Resume one interrupted crawl with resumeFromRunId so you never pay twice for items already collected.
  • Cost control. Set Max items and Max pages to control run size and cost precisely.

Use Cases

  • Resale price tracking: monitor a brand or category's price and MSRP spread over time to spot underpriced or newly listed items.
  • Competitive analysis for resale sellers: compare your own listings' pricing and condition grading against similar items already on ThredUp.
  • Market research and trend analysis: build a dataset of brand, material, condition and price mix across departments to track secondhand demand.
  • Personal shopping bots and alerts: watch a saved search for new drops in your size, brand or price range.
  • Academic and sustainability research: study circular-fashion pricing, condition grading and turnover patterns.

Data You Get

Sample shape: values are illustrative placeholders, not from a live listing.

FieldExample
id200000000
itemNumber1501420014
url"https://www.thredup.com/product/1501420014"
title"Sample Item Title"
description"Short item description appears here."
brand"Sample Brand"
price24.99
originalPrice39.99
msrp89
condition"excellent"
sizeDisplay"Size M"
department"women" (also "" for all departments and kids for girls plus boys; ThredUp has no men's department)
category"dresses"
colorNames["Black"]
materials["Cotton"]
newWithTagsfalse
clearancefalse
imageUrl"https://tup-images.thredup.com/assets/000000000/xlarge.jpg"
imageUrls["https://tup-images.thredup.com/assets/000000000/xlarge.jpg"]
fullDescription"Full description appears when Fetch full details is on."
measurements{ "chest": "00", "length": "00" } (when Fetch full details is on)
changeType"NEW" (also "UPDATED", "UNCHANGED", "REAPPEARED", "EXPIRED", incremental mode only)
changedFields["price"] (incremental mode only, non-empty only for UPDATED)
firstSeenAt"2024-01-01T00:00:00Z" (incremental mode only)
lastSeenAt"2024-01-02T00:00:00Z" (incremental mode only)

Each row also carries the alias fields size (same as sizeDisplay), color (colors joined with commas), discount (whole-percent markdown from original price, null when not discounted), isNewWithTags (same as newWithTags) and scrapedAt (ISO timestamp, ignored by incremental change detection).

Other output rows carry the same shape with extra catalog fields such as sku, brandId, categoryTags, departmentTags, styleTags, availability, state, favoriteCount, warehouseId, supplierId and the raw photoIds list; these fill in from the same listing or item-page fetch with no separate toggle.

How to Use

  1. Pick a mode: search (keywords plus filters) or url (paste ThredUp category and item links).
  2. In search mode, set a department and any filters (brand, category, material, style, condition, price, sort). In url mode, paste the category or item links directly; filters and sort are read from each URL.
  3. Turn on Fetch full details if you also want the description and garment measurements.
  4. Set Max items (and optionally Max pages) to control run size and cost, then click Start.

Search a department with filters:

{
"mode": "search",
"searchQueries": ["nike dress"],
"department": "women",
"brands": ["Nike"],
"priceMin": 15,
"priceMax": 60,
"sortBy": "price_low_high",
"maxItems": 50,
"proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "US" }
}

Browse a whole category with full details:

{
"mode": "search",
"department": "juniors",
"categories": ["jeans"],
"fetchDetails": true,
"maxItems": 40
}

Paste ThredUp URLs (category pages and item pages):

{
"mode": "url",
"urls": [
"https://www.thredup.com/women",
"https://www.thredup.com/women/dresses",
"https://www.thredup.com/women/dresses?page=2",
"https://www.thredup.com/women?sort=newest_first",
"https://www.thredup.com/women?search_text=nike",
"https://www.thredup.com/product/1501420014"
]
}

Supported URL shapes (url mode). Leaving urls untouched now scrapes one example of each of the first twelve shapes (a behaviour change from scraping nothing).

ShapeExampleRead as
Departmenthttps://www.thredup.com/womendepartment filter
Categoryhttps://www.thredup.com/women/dressesdepartment + category
Paginatedhttps://www.thredup.com/women/dresses?page=2starts at that page; a start page above 1 is a partial scope
Sortedhttps://www.thredup.com/women?sort=newest_firstnewest_first, price_low_high, price_high_low; unknown values log a warning and use relevance
Keywordhttps://www.thredup.com/women?search_text=nikekeyword search
Brandhttps://www.thredup.com/women/free-peoplebrand filter; the exact brand name is read from the page
Kids brandhttps://www.thredup.com/kids/girls/levisgirls or boys department plus brand (levis is read as Levi's)
Brand and categoryhttps://www.thredup.com/petite/coldwater-creek/sweatersbrand plus category (a fourth path part is read as a style)
Category sub-typehttps://www.thredup.com/women/dresses/casual-dressesdepartment + category + the sub-type filter the site itself applies (casual dresses are the site's casual occasion filter); read from the page, falls back to a style filter with a warning if the page cannot be read
Designerhttps://www.thredup.com/designer/dressesdesigner department + category
Brand-named pagehttps://www.thredup.com/women/availableThredUp lists this page as the brand named Available (the page title and breadcrumb say so); it is not an availability filter
Plain hosthttps://thredup.com/petitesame as the www form, department root (ThredUp redirects to www)
Keyword (search tags)https://www.thredup.com/women?search_tags=nike+dress&sort=newest_firstkeyword search, sorted
Department landinghttps://www.thredup.com/handbagsdepartment filter
Filteredhttps://www.thredup.com/women?department_tags=womenfilter params passed through (not in the example list)
Legacyhttps://www.thredup.com/products/women?search_tags=nike+dresssame as the keyword form
Itemhttps://www.thredup.com/product/1501420014that single item

Track new drops in a brand on a schedule:

{
"mode": "search",
"searchQueries": ["Madewell"],
"department": "women",
"incrementalMode": true,
"maxItems": 100
}

Run it from your code

Python:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/thredup-scraper").call(run_input={
"mode": "search",
"searchQueries": ["nike dress"],
"department": "women",
"maxItems": 50,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["title"], item["brand"], item["price"])

JavaScript:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/thredup-scraper').call({
mode: 'search',
searchQueries: ['nike dress'],
department: 'women',
maxItems: 50,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.

How deep a search goes

There is no page limit by default: Max pages is empty (0), and the walk keeps going until ThredUp itself stops serving pages or Max items is reached.

ThredUp's own search backend, however, only serves the first block of about 10,000 matches for any one query or filter set, at 48 items per page. A broad search such as all of women reports a fixed total and stops after roughly 209 pages no matter how many items actually exist behind it. That is a limit of the site, not of this actor, and raising Max pages cannot get past it. To collect more from a large department, split the search into narrower slices (by brand, category, price band or condition) and run them separately; a slice whose real total is below that ceiling is served in full.

This also decides when EXPIRED rows can fire: they only make sense after a run has proven it saw the whole tracked result set, so a search that hit the site's own ceiling is skipped for EXPIRED detection rather than risk billing tombstones for items that are actually still listed.

Resume and recurring updates

There are two different things here, pick the one that matches what you are doing:

NeedUse
A crawl stopped and should continueresumeFromRunId
Run the same search every day and receive only changesincrementalMode
Keep separate daily campaigns for similar searchesdistinct stateKey values
Run a normal full snapshotleave both off

Resume (resumeFromRunId) continues one specific interrupted or previous crawl: paste a run ID or dataset ID and this run skips items already collected there, returning only the remaining new items. Incremental mode (incrementalMode) is for a schedule (for example, daily): the actor remembers the previous run of the same search by itself, so you never paste a run ID. The first run returns everything as NEW; later runs return only NEW, UPDATED and REAPPEARED items by default, and unchanged items are not billed unless you turn on Emit unchanged. State is isolated per search or URL and filter setup automatically; set stateKey to name or deliberately share a monitoring campaign, but only share it across configurations you actually want merged into one combined baseline (see the FAQ).

What is compared. UPDATED is decided by comparing every output field with the previous run (price, condition, size, color, availability, photos and the rest). A field that is empty on one side is not counted as a change, numbers are compared by value (25 equals 25.0), and scrapedAt is excluded because it changes on every scrape. Use ignoreFieldsForChanges to exclude more.

A few limits worth knowing before you rely on incremental mode for billing-sensitive scheduling:

  • EXPIRED detection runs once the normal walk finishes, and the EXPIRED rows it returns count toward Max items: only the remaining share is returned, and any tracked items beyond it stay tracked and are reported on a later run. Turn Emit expired off if you do not want these extra rows.
  • Combining resumeFromRunId with a first-time incrementalMode bootstrap (a search that has no prior incremental state yet) makes every resumed item read as UPDATED, with every field listed in changedFields, on the very next ordinary scheduled run. This is a one-time effect per resumed item, not a repeating one.
  • Each page of results is split against the remaining Max items share before anything is returned, and every item on that page, including the ones past the share, is counted as already handled for the rest of the run. Two things follow. With several search queries or URLs in one call, an item that an earlier query fetched but did not return is skipped by every later query too, so a tight Max items can return fewer items than were actually available. In incremental mode, unchanged items are dropped only after that split, so when the top of a page is unchanged, NEW items further down the same page can be skipped for this run (they show up as NEW on a later run). Give Max items generous headroom (or set it to 0) when running several queries together or when using incremental mode.
  • The first run after this update skips EXPIRED detection for searches saved earlier (URL searches in particular now apply the category, brand and style in the link, so they cover a narrower set than before). That run only refreshes the tracked set to the current search, and EXPIRED detection works normally from the next run.
  • In url mode, if a pasted item link fails to load, that item is not returned, and the failure does not stop EXPIRED detection: if the item was tracked before and Emit expired is on, the run can wrongly mark it EXPIRED even though it may still be listed. Re-check any EXPIRED result on a pasted item link directly before acting on it.

Send results into your apps (MCP connectors)

Optionally pipe results into the apps you already use, through Model Context Protocol (MCP) connectors. Authorize a connector under Apify, Settings, API and Integrations, then select it in the mcpConnectors input. For Notion, also set notionParentPageUrl. Each item is written as a condensed, human-readable summary (title plus key fields), not the full JSON; the complete record always stays in the Apify dataset. Leave the field empty to skip; it never changes the dataset output.

Input Parameters

ParameterTypeDefaultDescription
modestringsearchsearch (keywords plus filters) or url (paste ThredUp URLs).
searchQueriesarray(none)Keywords to search ThredUp for (search mode). Leave empty to browse a whole department or category by filters.
searchQuerystring(none)Single-keyword alias for searchQueries; merged into searchQueries when both are given (duplicates dropped).
departmentstringwomenTop-level department: women, juniors, girls, boys, maternity, plus, petite, designer, handbags, shoes, accessories.
categoriesarray(none)Category tags to narrow to (e.g. dresses, jeans, sneakers).
brandsarray(none)Brand names to filter by (e.g. Nike, Madewell, Lululemon). Lowercase or hyphenated names such as free-people are read as the display name (Free People). A brand that matches nothing ends the run with a warning that names it.
materialsarray(none)Material tags (e.g. Cotton, Leather, Silk).
stylesarray(none)Style tags (e.g. Casual, Y2K).
conditionarray(none)ThredUp condition tags to filter by (advanced; leave empty for any condition). The output condition field always shows the item's quality grade.
clearanceOnlybooleanfalseOnly return items ThredUp itself flags as clearance. Applies to search mode; in url mode the pasted link decides what is returned.
priceMininteger(none)Only return items priced at or above this amount, in USD.
priceMaxinteger(none)Only return items priced at or below this amount, in USD.
sortBystringrelevancerelevance, price_low_high, or price_high_low.
urlsarray5 example URLs (one per shape)ThredUp category (PLP) or item URLs to paste (url mode). Multiple URLs supported; filters, sort, search and start page are read from each one. If you switch to url mode and leave this untouched, the five example URLs below are scraped.
fetchDetailsbooleanfalseVisit each item page to add the full description and measurements. Adds a small per-item charge; leave off for fast listings, which already include brand, price, size, condition, colors, materials and all photos.
maxItemsinteger20Stop after collecting this many items across all queries or URLs. Set 0 for unlimited.
maxPagesinteger0Optional cap on result pages per query. Leave empty (or 0) for no page limit: the run then stops only when Max items is reached or ThredUp stops serving further pages. See "How deep a search goes" above for ThredUp's own depth limit.
proxyobjectResidential USProxy configuration. Defaults to Residential US because ThredUp only serves US connections and challenges many other connections. See Plan Requirement.
resumeFromRunIdstring(none)Continue one specific previous run or dataset: items already collected there are skipped and not re-billed. For recurring monitoring of the same search, use Incremental mode instead.
incrementalModebooleanfalseDaily or recurring monitoring of this same search. The first run returns everything as NEW; later runs return only NEW, UPDATED and REAPPEARED by default. See "Resume and recurring updates" above.
stateKeystring(none)Optional name for a monitoring campaign, so its incremental state stays stable or is deliberately shared. Auto-derived from your search, URL and filter settings when left empty.
emitUnchangedbooleanfalseIncremental mode only. Also return items unchanged since the last run, marked UNCHANGED. Adds and bills extra rows you already have.
emitExpiredbooleanfalseIncremental mode only. Also return items from a previous run no longer found, marked EXPIRED, once a run has fully scanned the search (not capped, not a resume, and narrow enough that ThredUp serves the whole result set). Adds and bills extra synthetic rows; together with the other rows they never exceed Max items, and any left over stay tracked for the next run.
ignoreFieldsForChangesarray[]Incremental mode only. Field names (or dotted nested paths such as photos.url) to exclude from UPDATED detection and changedFields; they still appear in the output. scrapedAt is always excluded.
mcpConnectorsarray(none)Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API and Integrations, then select it here. Leave empty to skip; never changes the dataset output.
notionParentPageUrlstring(none)Notion connector only: URL (or id) of the page under which item pages are created. Ignored by other connectors.
maxNotifyListingsinteger50Cap on items written to each connector per run. Does not affect the dataset.

Output Example

Sample shape: values are illustrative placeholders, not from a live listing.

{
"id": 200000000,
"itemNumber": "1501420014",
"sku": "SKU0000000",
"url": "https://www.thredup.com/product/1501420014",
"title": "Sample Item Title",
"description": "Short item description appears here.",
"brand": "Sample Brand",
"brandId": 1000,
"price": 24.99,
"originalPrice": 39.99,
"msrp": 89,
"clearance": false,
"newWithTags": false,
"condition": "excellent",
"sizeDisplay": "Size M",
"department": "women",
"category": "dresses",
"categoryTags": ["dresses"],
"colorNames": ["Black"],
"materials": ["Cotton"],
"availability": "available",
"favoriteCount": 0,
"warehouseId": 0,
"photoIds": ["000000000"],
"imageUrls": ["https://tup-images.thredup.com/assets/000000000/xlarge.jpg"],
"imageUrl": "https://tup-images.thredup.com/assets/000000000/xlarge.jpg",
"fullDescription": null,
"measurements": null
}

Plan Requirement

ThredUp sits behind a Cloudflare challenge that refuses many plain requests, so this actor needs a real proxy connection and is not guaranteed to work on a datacenter connection. The default is Apify Residential proxy with country US, and ThredUp only serves US connections. Residential proxy access is included from the Starter plan and above; keep it selected under Proxy & connection (the choice and its cost are yours).

If the selected connection is unavailable to your account (Apify free plans do not include any Apify proxy), the run logs "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable." and tries a backup connection. Pages served that way are logged as "Served over the backup connection". Upgrading at apify.com/pricing or supplying your own proxy URLs gives the most reliable results.

When nothing can be loaded the run fails with one of these messages instead of returning an empty dataset:

  • "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required." (also check your own proxy credentials if you entered any).
  • "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested ThredUp pages."
  • "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later."

FAQ

What views does the dataset have?

The dataset has three views: Overview (the main listing columns), Detail (description, measurements, materials, colors and photos) and Changes (changeType, changedFields, firstSeenAt, lastSeenAt for incremental runs).

How is billing counted?

Billing is per listing returned (one event per dataset item), plus one event per item enriched when Fetch full details is on, plus one actor-start event. Current rates are on the Pricing tab. Suppressed, unchanged (unless Emit unchanged is on) and failed items are not charged.

How much does it cost?

You pay per item returned, with the optional full-details fetch billed only when you switch it on. The Pricing tab shows the current rates. Use Max items to cap the cost of a run.

This actor collects only publicly available resale listings. You are responsible for how you use the data: follow ThredUp's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Prices and listing details are generally facts, but photos may be subject to third-party rights.

Can I get only new or changed items on a schedule?

Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only NEW, UPDATED and REAPPEARED items by default, and unchanged items are not billed unless you also turn on Emit unchanged.

What's the difference between Resume and Incremental mode?

Resume (resumeFromRunId) continues one specific interrupted or previous run: paste its run or dataset ID and this run skips items already collected there. Incremental mode is for running the same search again and again, for example daily: the actor remembers the previous run of that search by itself, so you never paste an ID.

Why did my run fail instead of returning an empty dataset?

If a connection to ThredUp cannot be established at all, the run stops with a clear message so "no listings matched" is never confused with "the connection never worked". See Plan Requirement for the exact messages. This is usually transient; try again, and confirm Apify Residential proxy with country US is set.

Why did a scheduled run bill more items than my Max items setting?

Max items also bounds EXPIRED detection: EXPIRED rows are returned only up to the remaining Max items share after the normal walk, and the rest stay tracked for a later run. Turn off Emit expired if you do not want these extra rows.

Why was an item marked UPDATED even though nothing about it seems to have changed?

This can happen once per item after you bootstrap Incremental mode from Resume, meaning resumeFromRunId combined with incrementalMode on a search that has no prior incremental state yet. Items pulled in through Resume are recorded without a comparable snapshot, so the very next ordinary scheduled run has nothing reliable to diff them against and reports them as UPDATED with every field listed in changedFields. It is a one-time effect for each resumed item, not a repeating one.

Can two searches share the same monitoring state?

Yes, by setting the same State key on both, which is useful when you deliberately want one combined "still listed" baseline for two related searches. Doing this across searches that track very different items is not recommended: once one of them scans to completion with Emit expired on, it will tombstone every tracked item it does not itself encounter, including ones that only the other search would find and that may still be live.

Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

πŸ”— Want more classifieds data?

Pair this actor with these related scrapers from the same team:

🏷️ Vinted.fr
Scrape Vinted.fr, France's C2C second-hand fashion marketplace: search with...
🏷️ Curtsy Fashion Resale Scraper
Scrape Curtsy, the US secondhand fashion marketplace: listings with brand, size, list vs...
🏷️ Poshmark Scraper
Scrape Poshmark fashion-resale listings: keyword search, brand and category browse, sold...
πŸ‘— Mango Scraper
Scrape Mango (shop.mango.com) fashion products: current price plus strike-through Rebajas...
🏷️ Daangn Scraper
Scrape Daangn (λ‹Ήκ·Ό) marketplace listings by keyword and region. Returns 35+ fields per...
πŸ‘— KREAM Scraper
Scrape KREAM (kream.co.kr) sneaker and fashion resale data by keyword search, filters...

πŸ‘‰ Browse all abotapi scrapers

πŸ’¬ Support

  • 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
  • πŸ› οΈ Need another site, extra fields or a private build? Email contact@abotapi.com.
  • ⭐ Enjoying it? A quick review on the actor page helps other users find it.