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

# Price Feed (Redis)

> Consume live item and price updates from the AssetPay feed over Redis Streams.

## Overview

The AssetPay **price feed** is a low-latency firehose of item and price changes, delivered over **Redis Streams** at `feed.assetpay.gg`. Instead of polling the REST API, you tail a stream and react to each change as it happens - items entering the pool, items leaving, and price updates per marketplace.

Each consumer reads a **snapshot** of current state once, then **tails the stream** for incremental changes. If you fall behind or reconnect, you re-read the snapshot and resume - no events are lost.

## Connection

<ParamField path="endpoint">`rediss://feed.assetpay.gg:6382` - TLS only; plaintext connections are rejected.</ParamField>

The feed is TLS-encrypted with a private CA. To connect you need:

* The **CA certificate** (`ca.crt`) - AssetPay provides this; your client must trust it.
* An **ACL username + password** - AssetPay issues these per integration.
* **SNI / servername** set to `feed.assetpay.gg` when validating the certificate.

<Note>
  The feed is a raw Redis (TCP) endpoint, so `feed.assetpay.gg` resolves directly to the origin - it is not proxied like the HTTP API.
</Note>

### Access tiers

| ACL user  | Sees                                                                    | Use case                                                            |
| --------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------- |
| `market`  | Your own per-market stream only (`autoseller:items:feed:<marketplace>`) | A single-marketplace tenant - you never see another market's prices |
| `partner` | The consolidated stream, all marketplaces in one block                  | A first-party consumer aggregating every market                     |

## Streams & keys

| Key                                            | Type   | Contents                                                     |
| ---------------------------------------------- | ------ | ------------------------------------------------------------ |
| `autoseller:items:feed`                        | Stream | Consolidated - every item with all marketplace prices        |
| `autoseller:items:feed:snapshot`               | Hash   | Current state for the consolidated stream (`itemId` → block) |
| `autoseller:items:feed:<marketplace>`          | Stream | Per-marketplace stream                                       |
| `autoseller:items:feed:<marketplace>:snapshot` | Hash   | Current state for that marketplace                           |

Marketplaces: `white_market`, `waxpeer`, `shadowpay`, `csgoempire`, `market_csgo`, `assetpay`. Only priced markets emit data (today that is `white_market`).

## Event format

Each stream entry has two fields: `type` and `data` (a JSON string).

<ResponseField name="type" type="string">
  `item.add`, `item.remove`, or `price.update`.
</ResponseField>

<ResponseField name="data" type="JSON string">
  The event payload - see below.
</ResponseField>

### `item.add`

A new item entered the pool (or its block was refreshed). On a **per-market** stream the payload is flattened to that market's price:

```json theme={null}
{
  "id": "0f9c...",
  "appid": 730,
  "assetId": "39482...",
  "name": "AK-47 | Redline (Field-Tested)",
  "marketHashName": "AK-47 | Redline (Field-Tested)",
  "iconUrl": "https://...",
  "tradable": true,
  "delivery": "instant",
  "marketplace": "white_market",
  "price": "12.43",
  "updatedAt": "2026-06-15T12:00:00.000Z"
}
```

On the **consolidated** stream, prices are nested per marketplace:

```json theme={null}
{
  "id": "0f9c...",
  "appid": 730,
  "name": "AK-47 | Redline (Field-Tested)",
  "prices": {
    "white_market": { "price": "12.43", "updatedAt": "2026-06-15T12:00:00.000Z" }
  }
}
```

### `price.update`

An existing item's price changed on one marketplace:

```json theme={null}
{ "id": "0f9c...", "marketplace": "white_market", "price": "12.10", "updatedAt": "2026-06-15T12:05:00.000Z" }
```

### `item.remove`

An item left the pool (sold or pulled):

```json theme={null}
{ "id": "0f9c..." }
```

## Consuming the feed

<Steps>
  <Step title="Capture the stream position">
    Record the latest stream id (`XREVRANGE <stream> + - COUNT 1`) **before** seeding, so anything added during seeding replays from the stream - no gap.
  </Step>

  <Step title="Seed from the snapshot">
    `HGETALL` the snapshot hash to load current state in one round trip. Each field is an item id; each value is the JSON block.
  </Step>

  <Step title="Tail the stream">
    `XREAD BLOCK 0 STREAMS <stream> <lastId>` in a loop, applying each event and advancing `lastId` to each entry's id.
  </Step>

  <Step title="Resync on disconnect">
    On reconnect, re-run from step 1. The snapshot is always current, so re-seeding reconciles any missed events.
  </Step>
</Steps>

### Example (Node.js / ioredis)

```typescript theme={null}
import Redis from "ioredis";
import { readFileSync } from "node:fs";

const STREAM = "autoseller:items:feed:white_market";

const r = new Redis("rediss://market:<YOUR_FEED_PASSWORD>@feed.assetpay.gg:6382", {
  tls: { ca: readFileSync("ca.crt"), servername: "feed.assetpay.gg" },
});

// 1. capture position  2. seed snapshot  3. tail
const top = await r.xrevrange(STREAM, "+", "-", "COUNT", 1);
let lastId = top[0]?.[0] ?? "0";

const snapshot = await r.hgetall(`${STREAM}:snapshot`);
for (const [, json] of Object.entries(snapshot)) {
  upsert(JSON.parse(json)); // your handler
}

while (true) {
  const res = await r.xread("COUNT", 200, "BLOCK", 0, "STREAMS", STREAM, lastId);
  for (const [, entries] of res ?? []) {
    for (const [id, fields] of entries) {
      lastId = id;
      const type = fields[fields.indexOf("type") + 1];
      const data = JSON.parse(fields[fields.indexOf("data") + 1]);
      handle(type, data); // item.add | item.remove | price.update
    }
  }
}
```

<Warning>
  Use a dedicated client for the blocking `XREAD ... BLOCK` loop - it ties up the connection. Don't reuse it for other commands.
</Warning>

## Notes

* **Prices are decimal-dollar strings** (e.g. `"12.43"`), not cents - parse carefully to avoid float rounding.
* **Streams are length-capped** (\~100k entries, trimmed automatically) - always seed from the snapshot rather than reading the stream from `0`.
* **Credentials and the CA** are issued per integration - contact AssetPay to provision feed access.
