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

# Ranking markets by CLOB rewards

> Surface markets currently paying CLOB rewards, sorted by combined daily rate, for makers shopping for liquidity programs.

The Polymarket CLOB pays maker rewards on a curated set of markets. Each rewarded market carries one or more reward configs (`clob_rewards[]`), each with a daily rate, a max spread, and a min order size. Struct exposes these through the markets list (filter `has_rewards=true`, sort `sort_by=rewards`) and via the markets stream and CLOB rewards rooms for live updates.

## When to use this

* Market-maker tooling: a leaderboard of "where can I earn the most rebates today?"
* Reward eligibility checks: filter to markets with `rewards_max_spread` your strategy can hit.
* Sponsor monitoring: track new sponsors joining or rates changing on a watched market.

## Step 1: list reward-bearing markets

`has_rewards=true` keeps only markets with at least one active reward config; `sort_by=rewards` orders the response by combined daily rate descending.

<CodeGroup>
  ```typescript SDK theme={null}
  import { StructClient } from "@structbuild/sdk";

  const client = new StructClient({ apiKey: "sk_live_xxx" });

  const { data: markets } = await client.markets.getMarkets({
    has_rewards: true,
    sort_by: "rewards",
    limit: 50,
  });
  ```

  ```bash cURL theme={null}
  curl "https://api.struct.to/v1/polymarket/market?has_rewards=true&sort_by=rewards&limit=50" \
    -H "X-API-Key: YOUR_API_KEY"
  ```

  ```python Python theme={null}
  import requests

  markets = requests.get(
      "https://api.struct.to/v1/polymarket/market",
      headers={"X-API-Key": "YOUR_API_KEY"},
      params={"has_rewards": "true", "sort_by": "rewards", "limit": 50},
  ).json()["data"]
  ```
</CodeGroup>

## Step 2: read the reward fields

Each market row carries a top-level `total_daily_rate` (the combined daily rate across all sponsors on that market) and a `clob_rewards[]` array with the per-config breakdown:

| Field                                 | Type              | Description                                     |
| ------------------------------------- | ----------------- | ----------------------------------------------- |
| `total_daily_rate`                    | `number \| null`  | Top-level: combined daily rate across sponsors. |
| `clob_rewards[].rewards_daily_rate`   | `number \| null`  | Daily rate for this config.                     |
| `clob_rewards[].native_daily_rate`    | `number \| null`  | Polymarket native rate.                         |
| `clob_rewards[].sponsored_daily_rate` | `number \| null`  | Sponsor-funded rate.                            |
| `clob_rewards[].rewards_max_spread`   | `number \| null`  | Max eligible spread (probability units).        |
| `clob_rewards[].rewards_min_size`     | `number \| null`  | Min eligible order size (USD).                  |
| `clob_rewards[].start_date`           | `string \| null`  | ISO date the reward starts.                     |
| `clob_rewards[].end_date`             | `string \| null`  | ISO date the reward ends.                       |
| `clob_rewards[].sponsors_count`       | `integer \| null` | Active sponsor count.                           |

The combined `total_daily_rate` is the right number for the dashboard's primary sort. The per-config breakdown is what you show on the row's expanded view.

## Step 3: keep the dashboard fresh

Two paths, depending on freshness needs:

### Coarse: re-poll on an interval

```typescript theme={null}
setInterval(async () => {
  const { data } = await client.markets.getMarkets({ has_rewards: true, sort_by: "rewards", limit: 50 });
  renderDashboard(data);
}, 60_000);
```

### Live: subscribe to the markets stream

`polymarket_markets_stream` accepts the same `has_rewards` filter and pushes full market rows whenever they change.

<CodeGroup>
  ```typescript SDK theme={null}
  import { StructWebSocket } from "@structbuild/sdk";

  const ws = new StructWebSocket({ apiKey: "sk_live_xxx" });
  await ws.connect();

  await ws.subscribe("polymarket_markets_stream", {
    interval_ms: 1000,
    mode: "filter",
    filter: { has_rewards: true },
  });

  const byCondition = new Map<string, MarketResponse>();
  for (const row of markets) byCondition.set(row.condition_id, row);

  ws.on("markets_stream_update", (event) => {
    for (const row of event.data) byCondition.set(row.condition_id, row);
    const sorted = [...byCondition.values()].sort(
      (a, b) => (b.total_daily_rate ?? 0) - (a.total_daily_rate ?? 0),
    );
    renderDashboard(sorted);
  });
  ```

  ```bash cURL theme={null}
  > {"type":"room_message","payload":{"room_id":"polymarket_markets_stream","message":{"action":"subscribe","interval_ms":1000,"mode":"filter","filter":{"has_rewards":true}}}}
  ```

  ```python Python theme={null}
  await ws.send(json.dumps({
      "type": "room_message",
      "payload": {
          "room_id": "polymarket_markets_stream",
          "message": {
              "action": "subscribe",
              "interval_ms": 1000,
              "mode": "filter",
              "filter": {"has_rewards": True},
          },
      },
  }))
  ```
</CodeGroup>

The stream only pushes rows that actually changed since the last flush, so re-sorting by `total_daily_rate` runs in bounded time per tick.

## Step 4: stream raw reward config changes

For sponsor-level events (a new sponsor joining a market, a rate change, an end date hit) subscribe to `polymarket_clob_rewards`. Each `clob_rewards_update` carries the full reward config so you can merge into a per-market reward map.

```typescript theme={null}
await ws.subscribe("polymarket_clob_rewards", {
  condition_ids: ["0xabc123..."],
});

ws.on("clob_rewards_update", (event) => {
  upsertRewardConfig(event.condition_id, event.reward);
});
```

See [CLOB Rewards](/websockets/rooms/clob-rewards) for the full config shape.

## Common combinations

| Goal                                 | Parameters                                                            |
| ------------------------------------ | --------------------------------------------------------------------- |
| Top reward markets right now         | `has_rewards=true&sort_by=rewards&limit=50`                           |
| High-liquidity rewarded markets only | `has_rewards=true&min_liquidity=50000&sort_by=rewards`                |
| Crypto rewards only                  | `has_rewards=true&categories=crypto&sort_by=rewards`                  |
| High-volume rewarded markets         | `has_rewards=true&min_volume=100000&timeframe=24h`                    |
| Track specific markets               | `polymarket_markets_stream` with `mode=ids` and `condition_ids=[...]` |

## Follow-on

To check whether your own quotes are inside the reward band on the markets you maker on, subscribe to [`polymarket_order_book`](/websockets/rooms/order-book) for the same `condition_ids`. Compare the live spread to each market's `rewards_max_spread` and flag rows where you're out of band.
