DEV Community

Marc Andrew
Marc Andrew

Posted on Originally published at compsniper.com

How to Get Poshmark Sold Listings with Python Without Maintaining a Scraper

If you are building a resale tool, the useful question is rarely what sellers are asking. You need to
know what comparable items have already sold for.

Poshmark does not provide a broadly documented developer API for general sold-listing research. You can
either maintain the marketplace request flow yourself or use an API that returns the public listing
evidence in a stable response.

I own CompSniper, and we recently added dedicated Poshmark and Mercari
endpoints alongside our eBay sold-listing API. This walkthrough uses the production Poshmark endpoint.

Make one Poshmark sold-listing request

Create a free CompSniper account, copy the API key, and keep it in an environment variable:

export COMPSNIPER_API_KEY="cs_live_REPLACEWITHYOURKEY"
Enter fullscreen mode Exit fullscreen mode

Install Requests:

python -m pip install requests
Enter fullscreen mode Exit fullscreen mode

Then search one page of Poshmark US sold listings:

import os
import requests

response = requests.get(
    "https://api.compsniper.com/v1/poshmark/sold",
    headers={
        "Authorization": f"Bearer {os.environ['COMPSNIPER_API_KEY']}"
    },
    params={
        "keyword": "louis vuitton neverfull",
        "department": "women",
        "page": 1,
    },
    timeout=45,
)
response.raise_for_status()

data = response.json()
print("Listings:", data["totalItems"])
print("Median displayed price:", data["summary"]["median"])

for item in data["items"][:5]:
    print(item["title"], item["soldPrice"], item["soldAt"])
Enter fullscreen mode Exit fullscreen mode

One page returns up to 48 sold listings. A successful page uses one CompSniper request.

What comes back

The Poshmark response keeps marketplace-specific fields instead of forcing them into an eBay schema:

{
  "keyword": "louis vuitton neverfull",
  "page": 1,
  "totalItems": 48,
  "hasNextPage": true,
  "items": [
    {
      "listingId": "6478a1b2c3d4e5f6a7b8c9d0",
      "title": "Louis Vuitton Neverfull MM Damier Ebene",
      "soldPrice": 1250,
      "originalPrice": 1960,
      "brand": "Louis Vuitton",
      "size": "OS",
      "category": "Women > Bags",
      "condition": "Pre-owned",
      "soldAt": "2026-07-15T18:30:00-07:00",
      "listedAt": "2026-06-01T12:00:00-07:00",
      "daysToSell": 44.27,
      "sellerUsername": "luxurycloset"
    }
  ],
  "summary": {
    "count": 48,
    "currency": "USD",
    "median": 1175,
    "p25": 925,
    "p75": 1395
  }
}
Enter fullscreen mode Exit fullscreen mode

The summary is calculated deterministically from the returned displayed prices. Keep the sample size
beside the median so a three-item niche query is not treated like a forty-eight-item market.

Add useful filters

The endpoint supports:

  • page
  • minPrice and maxPrice
  • department: all, women, men, kids, home, pets, or electronics
  • condition: all or nwt
  • brand
  • sortBy: sold_recently, price_asc, price_desc, or likes
  • enrich=true for optional public seller evidence

For example:

params = {
    "keyword": "nike air max 90",
    "department": "women",
    "condition": "nwt",
    "minPrice": 25,
    "maxPrice": 200,
    "sortBy": "price_desc",
}
Enter fullscreen mode Exit fullscreen mode

Handle the important price limitation

A public sold listing does not necessarily reveal the private transaction amount. An item may have sold
through an offer or as part of a bundle. Treat soldPrice as the public displayed sold-listing price,
not proof of an undisclosed accepted amount.

The same principle applies when data is missing. Seller enrichment fields and item-level shipping remain
null when the marketplace does not expose that evidence.

Mercari uses the same API key

The Mercari endpoint returns up to 100 public US listings per page:

response = requests.get(
    "https://api.compsniper.com/v1/mercari",
    headers={
        "Authorization": f"Bearer {os.environ['COMPSNIPER_API_KEY']}"
    },
    params={
        "keyword": "sony wh-1000xm5",
        "count": 25,
        "sold": "true",
    },
    timeout=45,
)
response.raise_for_status()
Enter fullscreen mode Exit fullscreen mode

Set sold=false for active listings. Mercari public search does not provide a reliable exact sold
timestamp, so the API returns status and price without inventing a sale date.

Do not retry every 429

Two different conditions use HTTP 429:

  • rate_limited is temporary. Respect Retry-After and retry a bounded number of times.
  • quota_exceeded is not temporary. Stop and show the returned upgrade URL or wait for the reset.

Blindly retrying quota_exceeded only creates a loop.

Complete examples

The free plan includes 100 requests each month and does not require a credit card.

Top comments (0)