> For the complete documentation index, see [llms.txt](https://docs.hydromancer.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hydromancer.xyz/readme/rest-api/outcomes/registeredoutcomessnapshot.md).

# registeredOutcomesSnapshot

{% hint style="warning" %}

### ⚠️ This is an add-on endpoint - access has to be purchased separately.

{% endhint %}

Returns compressed snapshots for active HIP-4 outcomes — one snapshot per outcome side (YES/NO). Each snapshot lists every holder of that side and their balance.

There are two endpoints:

1. **Metadata Endpoint (Fast)** — check whether new snapshots are available.
2. **Snapshot Endpoint (Heavy)** — download compressed snapshot data.

***

## registeredOutcomesSnapshotTimestamp (Fast)

* **Endpoint:** `POST /info`
* **Purpose:** check whether new snapshots have been written
* **Response time:** \~1 ms

### Request

<table><thead><tr><th width="161">Field</th><th width="119">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>type</code></td><td>string</td><td>Must be <code>"registeredOutcomesSnapshotTimestamp"</code></td></tr></tbody></table>

```bash
curl -X POST https://api.hydromancer.xyz/info \
  -H "Authorization: Bearer $HYDROMANCER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "type": "registeredOutcomesSnapshotTimestamp" }'
```

### Response

```json
{
  "snapshot_id": "20260428_state_123456789",
  "timestamp": 1745846400
}
```

***

## registeredOutcomesSnapshot (Heavy)

* **Endpoint:** `POST /info`
* **Purpose:** download per-side outcome snapshots
* **Response time:** \~100 ms+

### Request

<table><thead><tr><th width="161">Field</th><th width="170">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>type</code></td><td>string</td><td>Must be <code>"registeredOutcomesSnapshot"</code></td></tr><tr><td><code>outcome_ids</code></td><td>array of integers</td><td>Outcome ids to fetch (optional). Each id expands to two asset_ids: YES = <code>#{id*10}</code>, NO = <code>#{id*10+1}</code>.</td></tr><tr><td><code>asset_ids</code></td><td>array of strings</td><td>Asset ids in <code>#NNNN</code> format (optional). Used as-is.</td></tr><tr><td><code>question_ids</code></td><td>array of integers</td><td>HIP-4 question ids (optional). Each is server-side-expanded to its child outcomes (named + fallback), then each child to its YES + NO asset_ids.</td></tr></tbody></table>

If `outcome_ids`, `asset_ids`, and `question_ids` are all omitted, the response contains every active outcome side.

After deduplication, the combined `outcome_ids` (expanded) + `asset_ids` + `question_ids` (expanded to children, then to asset\_ids) list must not exceed **50 entries**; larger requests are rejected with `400 Bad Request`.

```bash
# Specific outcomes (YES + NO)
curl -X POST https://api.hydromancer.xyz/info \
  -H "Authorization: Bearer $HYDROMANCER_API_KEY" \
  -H "Content-Type: application/json" \
  --output outcomes.bin \
  -d '{ "type": "registeredOutcomesSnapshot", "outcome_ids": [3, 7] }'

# All active outcomes
curl -X POST https://api.hydromancer.xyz/info \
  -H "Authorization: Bearer $HYDROMANCER_API_KEY" \
  -H "Content-Type: application/json" \
  --output outcomes.bin \
  -d '{ "type": "registeredOutcomesSnapshot" }'
```

### Response Headers

* **Single asset\_id:** `x-payload-format: msgpack`, `Content-Encoding: zstd`
* **Multiple asset\_ids:** `x-payload-format: multi-zstd`, `x-compression: inner-zstd`

### Data Format

<details>

<summary>Single Snapshot Response</summary>

**Format:** raw zstd-compressed MessagePack.

**Decompressed structure** — a positional 4-element array. rmp-serde encodes Rust structs as msgpack arrays by default, so the field order matters, not the names:

```json
[
  "20260428_state_123456789",            // [0] snapshot_id
  3,                                      // [1] outcome_id
  "#30",                                  // [2] asset_id
  {                                       // [3] balances — address → integer-lot string
    "0xa11ce...000a": "100",
    "0xb0b...000b": "5"
  }
]
```

> **Note:** balances are formatted via HL's size-decimals rules. Outcome side tokens currently ship with `szDecimals=0` and `weiDecimals=5` on-chain; the producer reads both per-token from spot meta and uses them to descale and format. With `szDecimals=0`, balances are rounded to whole lots and rendered as integer strings — no decimal point, no trailing zeros (`"100"`, not `"100.00"`). Sub-lot dust uses banker's rounding (round-half-to-even). Parse with `int(s)`.

> **Note:** the HL escrow precompile address for each token is excluded from the snapshot. Every spot token has a deterministic escrow at `0x32{:038x}` of its `token_id` (e.g. `0x32000000000000000000000000000000000001ca` for token 458) that holds the unsold/issuer supply — it isn't a real user position. Thus, it is dropped from each side's balances map.

</details>

<details>

<summary>Multiple Snapshots Response</summary>

**Format:** custom binary framing with per-snapshot zstd payloads.

**Binary structure:**

```
[count: 4 bytes LE][len1: 4 bytes LE][zstd_blob1][len2: 4 bytes LE][zstd_blob2]...
```

Each `zstd_blob` decompresses to one positional 4-element msgpack array with the `[snapshot_id, outcome_id, asset_id, balances]` shape shown above. Snapshots are returned in `(outcome_id, side_index)` order.

</details>

### Implementation Example

```python
import requests, struct, zstandard as zstd, msgpack, os

response = requests.post(
    "https://api.hydromancer.xyz/info",
    json={"type": "registeredOutcomesSnapshot", "outcome_ids": [3, 7]},
    headers={
        "Authorization": f"Bearer {os.environ['HYDROMANCER_API_KEY']}",
        "Content-Type": "application/json",
    },
)
body = response.content
fmt = response.headers.get("x-payload-format")

if fmt == "msgpack":
    snapshot = msgpack.unpackb(zstd.ZstdDecompressor().decompress(body), raw=False)
    print(snapshot)
else:
    # multi-zstd: count, then [len][blob] pairs
    count = struct.unpack("<I", body[:4])[0]
    offset = 4
    snapshots = []
    for _ in range(count):
        length = struct.unpack("<I", body[offset:offset + 4])[0]
        offset += 4
        blob = body[offset:offset + length]
        offset += length
        snapshots.append(
            msgpack.unpackb(zstd.ZstdDecompressor().decompress(blob), raw=False)
        )
    for snap in snapshots:
        # snap is a 4-element list: [snapshot_id, outcome_id, asset_id, balances_map].
        # Balance values are integer-lot strings (sz_decimals=0).
        asset_id = snap[2]
        balances = {addr: int(s) for addr, s in snap[3].items()}
        print(asset_id, len(balances), "holders")
```

### Error Handling

* **400** — `asset_id` missing the `#` prefix, or the request exceeds 50 deduped asset\_ids.
* **500** — no matching snapshots in Redis, or the producer hasn't written any yet. Retry after polling `registeredOutcomesSnapshotTimestamp`.
