> ## Documentation Index
> Fetch the complete documentation index at: https://docs.exclusivo.one/llms.txt
> Use this file to discover all available pages before exploring further.

# Sales

> Completed sales for a collection, newest first.

```http theme={"system"}
GET /v1/collections/{id}/sales
```

Completed sales, newest first. Includes EXC sales and purchases made through
outside marketplaces — `marketplace` tells you which.

| Parameter | Type    | Default |
| --------- | ------- | ------- |
| `limit`   | integer | `25`    |
| `cursor`  | string  | —       |

```bash theme={"system"}
curl "https://exclusivo.one/v1/collections/base_0xabc.../sales?limit=50"
```

## A short page isn't the end

<Warning>
  The page window is cut **first**, and rows whose stored price can't be
  interpreted are dropped **afterwards**. So `data.length` can be less than
  `limit` — even zero — while `hasMore` is true.

  Page on `hasMore` and `nextCursor`. Never stop because a page came back empty.
</Warning>

```js theme={"system"}
async function allSales(collectionId, pages = 20) {
  const out = [];
  let cursor = null;
  let i = 0;

  do {
    const qs = new URLSearchParams({ limit: '50', ...(cursor ? { cursor } : {}) });
    const { data, pagination } = await fetch(
      `https://exclusivo.one/v1/collections/${collectionId}/sales?${qs}`
    ).then(r => r.json());

    out.push(...data);                      // may be empty — keep going
    cursor = pagination.hasMore ? pagination.nextCursor : null;
  } while (cursor && ++i < pages);

  return out;
}
```

## Response

Same shape as listings, with the sale fields filled in:

```json theme={"system"}
{
  "data": [
    {
      "chain": "base",
      "contractAddress": "0xabc…",
      "tokenId": "1421",
      "mint": null,
      "collectionId": "base_0xabc…",
      "price": { "raw": "48000000000000000", "decimal": 0.048, "currency": "ETH", "usd": 172.4 },
      "seller": "0x11…",
      "buyer": "0x22…",
      "marketplace": "opensea",
      "status": "sold",
      "timestamp": "2026-08-20T07:41:00.000Z",
      "orderId": null,
      "tx": "0xdead…"
    }
  ],
  "pagination": { "hasMore": true, "nextCursor": "50" }
}
```

| Field     | On a sale                                  |
| --------- | ------------------------------------------ |
| `buyer`   | Filled in                                  |
| `tx`      | The settling transaction                   |
| `status`  | `sold`                                     |
| `orderId` | The listing it came from — **may be null** |

## A 404 has two meanings

Either the id doesn't resolve, **or** it resolves to a collection with no stored
contract address — sales are keyed by that address.

So a `404` here doesn't prove the collection doesn't exist. Check
`/v1/collections/{id}` to tell the two apart.

Errors: `400`, `404`, `429`, `502`. Unlike most of this API, sales **can** 502.

## Working out volume

Sum `price.raw` and do the arithmetic in integer base units. `decimal` loses
precision on large wei values, and `usd` can be null on any row.

Also worth knowing: rows with prices we couldn't interpret are dropped before
you see them, so a total from this endpoint is a total of *interpretable* sales.

## Related

* [Listings](/api/marketplace/listings) — the active side of the same shape.
* [Collections](/api/marketplace/collections) — aggregate volume without paging the whole feed.
