# Poshmark Sold Listings Comps (`resourcefully_dialect/resale-comps`) Actor

Poshmark sold-listing price comps by keyword or UPC: median, percentiles and sell-through. This release supports Poshmark only; eBay, Mercari and Depop are not supported yet (planned).

- **URL**: https://apify.com/resourcefully_dialect/resale-comps.md
- **Developed by:** [Biosick Johnny](https://apify.com/resourcefully_dialect) (community)
- **Categories:** E-commerce
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Poshmark Sold Listings Comps

> **This release supports only Poshmark sold listings.** eBay, Mercari and Depop are **not supported yet** (planned for a later release). Their adapters remain in the input as experimental, off-by-default options and may fail or be blocked; do not rely on them.

### Pricing

Pay per event: **$0.005 per run** (actor start) + **$0.002 per result item**.

An Apify Actor for public sold-listing comparisons by keyword or UPC. It emits normalized items, statistics for each query/platform, and an aggregate `all` row. Uses Apify SDK v3, Crawlee, and Node 20.

### Platform status

| Platform | Default | Collection | Limitations |
| --- | --- | --- | --- |
| eBay | Off, experimental (not supported yet) | Public sold/completed HTML, both `li.s-item` and `.s-card`, next-page links | Requires a sold date on each card. Blocks rotate anonymous sessions with bounded retries. Residential proxy by default. |
| Poshmark | On, supported | Public SSR JSON (`__INITIAL_STATE__`, `__NEXT_DATA__`), `sold_out` records, opaque `max_id` cursors | Dates/conditions/shipping can be missing. Datacenter proxy supported with residential fallback. |
| Mercari | Off, experimental (not supported yet) | Playwright public page, same-domain JSON responses and SSR, residential proxy | Cloudflare may prevent collection. Only explicit sold states qualify; API shape can change. Eight scrolls maximum. |
| Depop | Off, experimental (not supported yet) | Playwright public page and JSON responses, residential proxy | `sold=true` is **not verified as a working public sold-search filter**. Only explicit sold flags qualify. Zero results reports unavailable. Prices can be the last asking price. |
| Grailed | Unsupported | Stub throws `unsupported`; actor reports an unsupported summary | Sold data requires login; login is outside this Actor's scope. |

Core means implemented and covered by offline fixtures, not live production certification.

Depop verification: a free anonymous read of [the public search page with `sold=true`](https://www.depop.com/search/?q=nike\&sold=true) on 2026-11-02 returned ordinary listing cards without establishing sold-filter semantics. Depop's [selling help](https://depophelp.zendesk.com/hc/en-gb/articles/360009418014-Selling-on-Depop) describes the account's own Sold tab, not a documented public sold-search parameter. The adapter does not infer a sale from the URL parameter or a missing/removed listing. Enable it only for best-effort discovery of explicitly sold records exposed by the anonymous page.

### Input

```json
{
  "queries": ["Sony WH-1000XM5", "0194253403933"],
  "platforms": ["poshmark"],
  "maxItemsPerPlatform": 100,
  "soldWithinDays": 90,
  "condition": "any",
  "minPrice": 10,
  "maxPrice": 500,
  "includeSellThrough": true,
  "outputMode": "both",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"]
  }
}
```

- `queries` is required and must contain nonempty strings. UPCs go into the normal keyword parameter; retain them as strings to preserve leading zeroes. Duplicate queries and platforms are collapsed.
- `platforms` defaults to `["poshmark"]`, the only supported platform in this release. eBay, Mercari and Depop are experimental and require explicit selection.
- `maxItemsPerPlatform` is per query/platform, defaults to 100, and accepts 1–1000. Filtering/deduplication happen before the cap. HTML pagination has a 50-page safety limit.
- `soldWithinDays` is an optional positive integer, using a rolling UTC window. Undated and future-dated records are excluded when this filter is set.
- `condition` is `any` (default), `new`, or `used`. Unknown conditions are excluded by a specific condition filter. Refurbished/like-new map to used.
- `minPrice`/`maxPrice` are inclusive item-price bounds excluding shipping, in the listing's original currency. They do not imply USD conversion.
- `includeSellThrough` defaults to true. Public active counts are fetched for eBay/Poshmark. Poshmark condition/price filters cannot be reliably mirrored in active counts, so those combinations return null counts. Unsupported, approximate, blocked, or missing counts stay null.
- `outputMode` accepts `items`, `summary`, or `both` (default); key-value `OUTPUT` always contains summaries.
- `proxyConfiguration` defaults to Apify residential. eBay follows your proxy selection. Poshmark can use datacenter, then retry with residential on sold-search or active-count failure. Experimental platforms use residential for Apify-managed proxies. Explicit `useApifyProxy:false` and custom proxy URLs override proxy selection. No automatic fallback to a direct connection occurs on proxy setup failure.

### Output

The dataset is a heterogeneous array of `type:"item"` and/or `type:"summary"` records. See [SAMPLE_OUTPUT.json](SAMPLE_OUTPUT.json), generated entirely from the hand-made fixtures, and `.actor/dataset_schema.json`.

Item fields: `query`, `platform`, `id`, canonical `url`, `title`, `price`, `currency`, `shipping`, `totalPrice`, `soldAt`, `condition`, `brand`, `size`, `imageUrl`, `scrapedAt`.

- Money is numeric. `"$1,234.56"` becomes `1234.56`; a range such as `"$20.00 to $30.00"` becomes null with `priceWarning`, rather than guessing a transaction amount. Poshmark uses the displayed sold-listing price, not `original_price_amount`. Public prices may differ from negotiated final sale prices on all platforms.
- Unknown prices/shipping remain null; explicitly free shipping is zero. `totalPrice` is available only when price and shipping are both known; taxes/fees are excluded.
- Non-USD currency is preserved and flagged by `currencyWarning`. This version has no exchange-rate source and performs no conversions. Single-currency summaries use original units; mixed-currency summaries withhold all price statistics, list `currencies`, and include a warning. U.S. marketplace amounts without a currency code/symbol default to USD.
- Dates are ISO UTC or null. Yearless eBay dates use the most recent matching calendar date. Missing values stay null. Items deduplicate by platform/id within a query.
- Only listing fields are retained. No seller identity, contact details, raw JSON, account data, or account cookies are exported.

Each summary contains `query`, `platform`, `count`, `currency`, `median`, `p10`, `p25`, `p75`, `p90`, `min`, `max`, `mean`, `soldCount`, `activeCount`, `sellThroughRate`, and `dateRange:{from,to}`. `status`, `warnings`, and `currencies` explain completeness and currency limitations.

Price statistics use **item price**, exclude shipping and null prices, and use linear interpolation at index `(n-1)*q` on sorted prices. Empty price samples yield null statistics; singleton samples yield that amount for every quantile. `count` and `soldCount` include all accepted sampled sold records, including records with unknown prices. They are not claims about the marketplace's total historical sales.

`sellThroughRate = soldCount / (soldCount + activeCount)` when both counts are known and the denominator is positive; otherwise null. This is a **sampled estimate**, sensitive to the item cap, date/condition/price filters, and public search visibility. It is not a true inventory-cohort sell-through metric. The `all` active count/rate is withheld unless every selected platform has a known active count. Active counts describe current public inventory; a date filter applies only to sold records.

Statuses distinguish an explicit empty result (`ok` with zero count) from `failed`, `unavailable`, `unsupported`, `partial` (including capped/browser samples), or `budget-stopped`. Failures on one platform do not prevent others from running. Missing sold records without an explicit empty-result message are treated as a possible parser/block failure, not a verified zero-sales result.

The default key-value store's `OUTPUT` record contains:

```json
{ "summaries": [], "warnings": [], "status": "ok", "generatedAt": "2026-11-02T12:00:00.000Z" }
```

### Pay per event

`.actor/pay_per_event.json` describes `actor-start` (once per run), `query` (once per unique query/platform with at least one accepted result), and optional `result-item`. **Prices are intentionally unset (TBD)**. This file is an unpriced event manifest, not a deployed pricing configuration. Configure event pricing separately before monetizing; this project does not publish pricing or deploy anything.

Events go through `Actor.charge({eventName,count:1})`. Runtime prices and remaining event capacity come from the SDK charging manager. The actor checks budget before scraping each query/platform and before each event, accounting for prior charged event counts. `ACTOR_MAX_TOTAL_CHARGE_USD=0` stops immediately without charge calls; positive limits stop work when exhausted or the next event is unaffordable. No local price constants are used. A successful final charge's results are delivered, then subsequent scraping stops. Summary status rows explain skipped work.

Per-item charging is off unless `RESALE_CHARGE_RESULT_ITEMS=true`. If enabled, configure the `result-item` event in platform pricing; each accepted normalized item is charged even in summary-only mode. Records that cannot be charged are omitted from both emitted items and statistics. Non-PPE runs allow the SDK's no-op charge response. See [Apify's PPE guide](https://docs.apify.com/sdk/js/docs/3.3/guides/pay-per-event) for platform setup.

### Local development and packaging

```sh
npm ci
npm test
npm run sample
npm run check
```

Tests use `node:test`, hand-made HTML/JSON fixtures and mocked SDK/crawler interfaces. A real SDK entrypoint test runs with a zero budget, isolated temporary storage, no inherited Apify credentials, and networking disabled. Tests make no network calls and do not charge, use proxies, or start real browsers. `npm run sample` regenerates the sample deterministically; `npm run check` validates schemas, metadata paths, the lockfile, and reproducibility. Apify and Crawlee are pinned to compatible core versions; the entrypoint test guards against dependency-resolution drift.

Running `npm start` performs **live** public searches and may incur platform/proxy costs. Supply input through the Apify SDK's normal input mechanism (`storage/key_value_stores/default/INPUT.json` locally) and arrange proxy access yourself before running it. For experimental adapters locally, install the pinned browser with `npx playwright install chromium`. The Dockerfile uses the Node 20 Apify Playwright image and installs the Chromium revision required by the pinned Playwright dependency. A container build requires registry/browser downloads; it is separate from offline tests.

### Public data and responsible use

Public data only. No login, account cookies, credential collection, or personal seller data. Anonymous crawler/browser sessions are isolated; no saved user browser profile is used. Respect each site's Terms of Service, robots/access policies, applicable law, and reasonable rate limits. Block/challenge responses have bounded retries; the actor does not solve CAPTCHAs or access account-only pages. You are responsible for permission to collect and use the data. Public sold listings and displayed asking prices are imperfect evidence of actual transaction values; use the completeness/currency warnings when evaluating comparisons.

# Actor input Schema

## `queries` (type: `array`):

Nonempty search strings. UPCs must be strings to retain leading zeroes. Defaults to 'lululemon align leggings' when empty.

## `platforms` (type: `array`):

This release supports Poshmark only. eBay, Mercari and Depop are EXPERIMENTAL, not supported yet, and off by default (planned for a later release); results may fail or be blocked. Grailed always reports unsupported.

## `maxItemsPerPlatform` (type: `integer`):

Cap on accepted listings after filtering and deduplication.

## `soldWithinDays` (type: `integer`):

Rolling UTC window (default 90). Results are requested newest-first and pagination stops at the window end. sellThroughRate is only reported when all sales in this window were collected (not capped by maxItemsPerPlatform).

## `condition` (type: `string`):

Unknown conditions are excluded when a condition filter is set.

## `minPrice` (type: `number`):

Inclusive, excludes shipping. Applied in each listing's original currency; no exchange conversion.

## `maxPrice` (type: `number`):

Inclusive, excludes shipping. Applied in each listing's original currency; no exchange conversion.

## `includeSellThrough` (type: `boolean`):

Fetch public Poshmark active counts for a sampled sell-through estimate.

## `outputMode` (type: `string`):

OUTPUT in the key-value store always contains summaries.

## `proxyConfiguration` (type: `object`):

Residential by default. Poshmark falls back to residential if datacenter fails. Custom/direct proxies remain explicit overrides.

## Actor input object example

```json
{
  "queries": [
    "lululemon align leggings"
  ],
  "platforms": [
    "poshmark"
  ],
  "maxItemsPerPlatform": 10,
  "soldWithinDays": 90,
  "condition": "any",
  "includeSellThrough": true,
  "outputMode": "both",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "queries": [
        "lululemon align leggings"
    ],
    "platforms": [
        "poshmark"
    ],
    "maxItemsPerPlatform": 10,
    "soldWithinDays": 90
};

// Run the Actor and wait for it to finish
const run = await client.actor("resourcefully_dialect/resale-comps").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "queries": ["lululemon align leggings"],
    "platforms": ["poshmark"],
    "maxItemsPerPlatform": 10,
    "soldWithinDays": 90,
}

# Run the Actor and wait for it to finish
run = client.actor("resourcefully_dialect/resale-comps").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "queries": [
    "lululemon align leggings"
  ],
  "platforms": [
    "poshmark"
  ],
  "maxItemsPerPlatform": 10,
  "soldWithinDays": 90
}' |
apify call resourcefully_dialect/resale-comps --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,resourcefully_dialect/resale-comps"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/O9njIW9bXubKWi5L6/builds/RszEFIGloZvQfxMHs/openapi.json
