> ## 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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.