Poshmark Sold Comps & Resale Price Scraper
Pricing
Pay per usage
Poshmark Sold Comps & Resale Price Scraper
Scrape SOLD Poshmark listings into resale price comps: sold price, days-to-sell, condition, and size, plus a summary per search (median, p25, p75 sold price, comp confidence, and a suggested list price). Built for Poshmark's 403 blocking.
Pricing
Pay per usage
Rating
0.0
(0)
Developer
KULQIZ
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Poshmark Sold Comps and Resale Price Scraper
Search any brand, item, or keyword on Poshmark and get its SOLD comps: real sold price, days-to-sell, condition, size, and seller. Each run also returns a summary per search term: median, p25, and p75 sold price, average days-to-sell, a comp-confidence score, and a suggested list price. It does the pricing math for you.
Made for resellers, flippers, and sourcing sellers who price from real data. If you have asked "what does this actually sell for on Poshmark, and how fast?", this answers it.
Pay-per-result: you pay for the listing records you get, and maxItems caps the spend.
Not affiliated with or endorsed by Poshmark.
Why this over the free Poshmark scrapers
Most Poshmark scrapers on the Store are free hobby projects. They hand you a raw listing dump and leave the analysis to you, and they tend to stall on Poshmark's 403 blocking. This one is built for the pricing decision:
- Every run writes a per-search comps summary: median sold price, p25/p75, days-to-sell, price by condition, size and brand, a recency-weighted comp-confidence score, and a suggested list price.
- It is built for Poshmark's 403 reality: proxy rotation, session retirement on a block,
challenge-page detection with backoff, and a per-run
data_qualityrecord so you see the real block rate and completeness of your own run. - It is cheap: it reads Poshmark's search response directly and pages through results, with no headless browser and no per-listing page fetch. Measured around $0.20 per 1,000 results on datacenter proxy (150 comps for about 3 cents).
- Fields Poshmark does not show come back
null, never guessed. Records are schema-validated before they are written.
Every number below comes from a reproducible run in /benchmarks. There are no figures here
we did not measure.
Benchmarks
Measured 2026-06-17. Re-run with the scripts in /benchmarks.
| What we measured | Result |
|---|---|
| Live sold listings parsed (coach tabby, lululemon align, nike dunk) | 17 |
| Passed schema validation | 17 / 17 (100%) |
| Core field completeness on live data (price, sold date, days-to-sell, brand, size, condition) | 100% |
| Sold comps returned by one search fetch | 44 / 39 / 16 |
| Block rate, 24 paced requests from a plain datacenter IP, no proxy | 0 / 24 (0%) |
The block-rate number is a floor, not a production figure. It was one IP, a short session, at
modest paced volume. Poshmark's anti-bot escalates under sustained load, which is why every run
writes a data_quality record and why residential proxy is available for heavy runs. We have
not published a sustained-load block rate. Your run's data_quality is the real number.
We did not measure competitor block rates, accuracy, or price. We cannot run another Actor in a controlled way, so there are no competitor numbers here.
What you get
One dataset record per listing:
{"itemId": "69bac03e74cb47e2344a7847","url": "https://poshmark.com/listing/Coach-Tabby-26-Black-Leather-...","title": "Coach Tabby 26 Black Leather","brand": "Coach","department": "Women","category": "Bags","size": "OS","condition": "Like New","color": "Black","originalRetailPrice": 395.0,"listingPrice": 240.0,"soldPrice": 240.0,"currency": "USD","isSold": true,"soldDateObserved": "2026-03-22","daysToSell": 4,"likesCount": 47,"commentsCount": 6,"sellerUsername": "luxuryhandba","imageUrls": ["https://di2ponv0v5otw.cloudfront.net/posts/..."],"scrapedAt": "2026-06-17T03:00:00Z","sourceQuery": "coach tabby"}
One Key-Value Store record per search term (AGGREGATE-<slug>). Example shape (for measured
numbers see Benchmarks and /benchmarks):
{"query": "coach tabby","count": 44, "soldCount": 41,"medianSoldPrice": 196.0, "p25SoldPrice": 160.0, "p75SoldPrice": 230.0,"outliersRemoved": 2,"avgDaysToSell": 12.2, "medianDaysToSell": 4,"sellThroughRate": null,"windowDays": 140, "pctSoldWithin30Days": 0.67, "pctSoldWithin90Days": 0.83,"compConfidence": "medium","priceByCondition": { "Like New": 220.0, "New With Tags": 245.0, "Used": 150.0 },"priceBySize": { "OS": 196.0 },"pricingRecommendation": {"listAt": 196.0, "fastSaleListAt": 160.0, "topDollarListAt": 230.0,"expectedDaysToSell": 4, "confidence": "medium"}}
compConfidence is transparent: high needs at least 30 sold comps and at least 60% sold
within 60 days, medium needs at least 10 sold comps, otherwise low. Price stats are
outlier-trimmed (IQR fence) once the sample is large enough, so one mispriced bundle does not
skew your median. sellThroughRate is null in the default sold-only mode, because a
sold-only sample is 100% sold. Set soldOnly to false to sample sold and active and get a
real rate.
A real example (live sample, June 2026)
From a small live sample, reproducible in /benchmarks. Time-to-sell varies a lot by category:
| Query | Median sold | p25 to p75 | Median days-to-sell |
|---|---|---|---|
| coach tabby | $195.50 | $160 to $230 | 2 |
| lululemon align | $45.00 | $40 to $50 | 19 |
| nike dunk | $59.50 | $39 to $89 | 34 |
A Coach Tabby in good shape near $195 tends to move within days. A Nike Dunk at these prices
sits much longer. Small live sample; compConfidence rises as you raise maxItems, since each
search page exposes 16 to 44 comps.
Input
| Field | Type | Default | Description |
|---|---|---|---|
searchQueries | array of string | required | Search terms. Each is scraped on its own and gets its own aggregate. |
brand | string | (none) | Keep only listings whose brand matches (case-insensitive). |
department | enum | (none) | Women, Men, Kids, Home, Electronics, Pets, or Other. |
category | string | (none) | Keep only listings whose category contains this text (e.g. Bags). |
sizes | array of string | (none) | Keep only these sizes (e.g. ["M","L","OS","34"]). |
conditions | array of string | (none) | Keep only matching conditions (e.g. ["New With Tags"]). |
soldOnly | boolean | true | Only collect SOLD listings. Set false for sell-through. |
maxItems | integer | 200 | Hard cap on total records across all queries. Caps cost. |
includeSellerStats | boolean | false | Experimental. Fetches each seller's closet; often null until verified, so off by default. |
computeAggregates | boolean | true | Write one summary record per query to the Key-Value Store. |
maxConcurrency | integer | 3 | Parallel pages. Keep low to stay polite and avoid blocks. |
proxyConfiguration | proxy | datacenter | Datacenter by default (cheapest, works at modest volume). Switch to RESIDENTIAL for heavy or blocked runs. |
Example input:
{"searchQueries": ["coach tabby", "lululemon align"],"soldOnly": true,"maxItems": 100,"computeAggregates": true,"proxyConfiguration": { "useApifyProxy": true }}
Export the dataset as JSON, CSV, Excel, or HTML from the run's Dataset tab. Read the per-query
summary from the Key-Value Store tab (AGGREGATE-<query-slug>), and the per-run reliability
record at DATA_QUALITY.
Reliability and the 403 question
Poshmark blocks scraping with HTTP 403. This is the real risk for any Poshmark Actor, so here is the honest version instead of a slogan:
- Datacenter proxy by default (cheapest, and it works for Poshmark at modest volume). Switch to residential for heavy or blocked runs.
- The session pool retires IPs on 403, 429, or 503 and rotates to a fresh session.
- A block or challenge page (including PerimeterX-style pages) triggers jittered backoff, a session rotation, then a retry. If a page stays blocked it is skipped and the run finishes with partial data instead of crashing.
- Every run writes a
DATA_QUALITYrecord: block rate, items returned versus requested, and per-field completeness. You measure reliability on your own run. - Measured on the platform: 150 comps across 4 paginated requests, 0 blocks, about 96% core
completeness, around $0.21 per 1,000 results on datacenter (compConfidence reaches "high" at this
sample). Your
DATA_QUALITYrecord is the number for your run.
If yields drop, lower maxConcurrency, switch to residential proxy, and reduce maxItems.
How to run
In the Apify Console, open the Actor, paste the input, and click Start.
By API:
curl -X POST "https://api.apify.com/v2/acts/<username>~poshmark-sold-comps/run-sync-get-dataset-items?token=<APIFY_TOKEN>" \-H "Content-Type: application/json" \-d '{ "searchQueries": ["coach tabby"], "maxItems": 50 }'
Local development:
pip install -r requirements.txtapify runpython -m src.main --validatepython -m pytest -qpython -m benchmarks.fetch_live "coach tabby" --per-query 7python -m benchmarks.measure_live
FAQ
Will Poshmark's 403 blocking break my run? It is the main risk for any Poshmark scraper.
This one rotates sessions on a block, backs off, and finishes with partial data plus a
DATA_QUALITY record rather than dying. Datacenter proxy is the default and cheapest; switch
to residential for heavy or blocked runs.
Are these real sold prices? Yes, filtered to availability=sold_out. Poshmark exposes no
separate final or offer amount, so soldPrice equals the listing price at the time of sale.
Why is sellThroughRate null? The default mode collects sold listings only, which is 100%
sold by definition. Set soldOnly to false to sample sold and active and get a true rate.
Can I get eBay or Mercari comps? The engine sits behind a thin marketplace adapter, so another marketplace can be added without rewriting the crawler. Ask if you need one.
Responsible use, legal, and ToS
- Public data only. It reads publicly visible listing pages. No login, credentials, tokens, or paywall bypass.
- No personal data beyond public seller handles and stats. No buyer data.
- Respect Poshmark's Terms of Service and your local laws. Scraping public pages is widely considered permissible, but Poshmark's ToS restricts automated access, and you are responsible for how you use this Actor and the data. Use modest volumes and cache results.
- Intended for analytics and pricing research by resellers. Not affiliated with or endorsed by Poshmark.
Limitations and known-brittle spots
- Sold price equals the listing price at the time of sale. Poshmark exposes no separate final amount.
- Seller rating, sales, and followers are best-effort from the public closet page and may be
nulluntil verified for your region and layout. - Brand is seller-entered and sometimes inconsistent (for example "Coach" versus "Coach
Tabby"), so
priceByBrandis most useful across multi-brand queries. - Condition labels are normalized from Poshmark's internal codes. Uncommon codes fall back to New or Used, or the raw code, rather than guessing a grade.
- Comps per query are bounded by how many sold results Poshmark has for the term; the Actor pages
through them up to
maxItems. Broad terms return thousands, narrow terms fewer. - Poshmark can change its DOM or payload at any time. Extraction is layered (embedded app-state
first, then JSON-LD, then meta tags, then the DOM) and is verified against live HTML in
/benchmarks. SeeNOTES.mdfor the exact brittle points.
Found this useful
If it saved you time, a short review on the Store helps other people find it. Found a bug or a missing field? Open an issue and it gets fixed.
See CHANGELOG.md. Current version: 0.4.0.