> For the complete documentation index, see [llms.txt](https://mute-sh.gitbook.io/mute.sh/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mute-sh.gitbook.io/mute.sh/api/api-reference/discovery-and-groups.md).

# Discovery & Groups

Two surfaces for finding what matters in the market universe: **discovery** ranks live outcomes by signals (trending, movers, closing soon, contested, new), and **groups** rolls volume up by market family for product-level analytics.

## Discovery: GET /v1/markets/discovery/\*

All five routes share the same conventions: optional `underlying` filter (`400 Invalid underlying` on bad input), `limit` (default 25, capped at 100, silently defaulted on garbage), and settled outcomes excluded (except `/new`). **An empty array is a valid response** — each route has a gate (volume floor, expiry present, price data at both window edges) and returns only rows that clear it.

### /trending

Composite momentum score, published so you can explain the ranking to users:

```
gate  = volume_24h ≥ $50
score = gate × recency × ( 0.40·log1p(max(vol_accel−1, 0))
                         + 0.20·log1p(new_traders_1h)
                         + 0.25·(4·|Δprob_1h| clamped to 0..1)
                         + 0.15·log1p(max(trade_accel−1, 0)) )
```

* `vol_accel` / `trade_accel` — last hour vs the 7-day hourly average (a market doing 3× its normal hourly volume accelerates).
* `new_traders_1h` — wallets whose **first-ever** trade on this outcome happened in the last hour.
* `Δprob_1h` — change in the Up-side price over the hour (a price proxy; quiet markets show 0 even if beliefs moved).
* `recency` — linear decay to 0 over 24h since the last trade.

Rows with `score ≤ 0` are dropped, so `{ "count": 0, "trending": [] }` simply means nothing cleared the gate in the current window.

Response rows carry the score *and* all its inputs (`volume_24h`, `volume_1h`, `vol_accel`, `new_traders_1h`, `prob_delta_1h`, `trades_1h`, `trade_accel`, `recency`, `trending_score`) — you can re-rank with your own weights client-side. Cached 30 s.

### /movers

Biggest absolute price moves over a window:

| Query param           | Default  | Description |
| --------------------- | -------- | ----------- |
| `window_hours`        | `24`     | 1–720       |
| `underlying`, `limit` | — / `25` | as above    |

```json
{
  "count": 25,
  "window_hours": 24,
  "movers": [
    {
      "outcome_id": 739,
      "title": "World Cup Round of 16: Brazil vs Norway",
      "prob_now": 0.00001,
      "prob_then": 0.70502,
      "prob_delta": -0.70501,
      "abs_prob_delta": 0.70501,
      "volume_window": 676459.81
    }
  ]
}
```

That top row is a live World Cup elimination: 70.5% → \~0% in a day. `prob_now`/`prob_then` are the latest Up-side trade prices now and at the window edge; **both edges must have real trades** or the row is dropped (no interpolation) — so thin markets don't produce phantom moves. `prob_delta` is signed; ranking is by `abs_prob_delta`, ties by window volume. Cached 30 s.

### /closing-soon

Unsettled outcomes with a scheduled expiry, soonest first — the "act before it locks" list. Rows carry `expiry`, all-time `volume_usd`/`trades`, and `open_interest` (a proxy: |buy notional − sell notional| over all time; HIP-4 has no positions table). Only markets with a real `expiry` qualify — event markets (`named` class, no scheduled end) never appear here by design. Cached 30 s.

### /most-contested

Unsettled outcomes whose current Up-side price sits closest to 0.5 — maximum disagreement.

| Query param  | Default | Description                                                                           |
| ------------ | ------- | ------------------------------------------------------------------------------------- |
| `min_volume` | `0`     | minimum all-time USD volume floor (use this — a dead market at 0.5 isn't "contested") |

Rows: `implied_prob`, `distance_from_half` (ranking key, ascending), `volume_usd`. Cached 30 s.

### /new

Recently listed outcomes, newest first — the only discovery route that **includes settled outcomes** (a market can list and resolve within the window).

| Query param     | Default | Description           |
| --------------- | ------- | --------------------- |
| `max_age_hours` | `168`   | listing window, 1–720 |

```json
{
  "count": 25,
  "new_outcomes": [
    {
      "outcome_id": 769,
      "title": "Recurring Named Outcome",
      "first_seen": "2026-07-06 06:47:21.000",
      "underlying": "BTC",
      "market_class": "priceBucket",
      "volume_since": 136,
      "trades_since": 4,
      "traders_since": 3
    }
  ]
}
```

`underlying` can legitimately be `""` (e.g. `fallback`-class legs). Note: despite the `_since` naming, the traction stats are the outcome's all-time totals — for outcomes inside the window that's the same thing, but don't build on the name. Cached 1 h (listings churn slowly).

***

## Groups: GET /v1/groups\*

A **group** is a market family: the tuple `(group_class, underlying, period)` — e.g. "daily BTC binaries" — aggregated across every coin, side, bucket, and round it has ever run. Groups answer product questions ("how big is the daily-BTC franchise?") that per-coin endpoints can't. All three routes are cached 1 h and accept the same filters: `group_class`, `underlying`, `period` — AND-combined equality filters. Values with invalid characters `400`; well-formed-but-unknown values simply match nothing (`200` with empty `data`).

### GET /v1/groups

The rollup, sorted by volume:

```bash
curl "https://api.mute.sh/v1/groups?group_class=priceBinary&underlying=BTC"
```

```json
{
  "data": [
    {
      "group_class": "priceBinary",
      "underlying": "BTC",
      "period": "1d",
      "volume_usdc": 106945492.49,
      "trades": 2756485,
      "traders": 8174,
      "coins": 132,
      "rounds": 66
    }
  ]
}
```

* `volume_usdc`/`trades` count **real Buy/Sell activity only** (no settlement or plumbing legs).
* `traders` is an upper-bound approximation at this rollup level, not an exact distinct count.
* `coins` / `rounds` show the family's footprint: 66 daily rounds × 2 coins per round per bucket.
* `group_class` values in live data: `priceBinary`, `priceBucket`, `named`, and `other` (the catch-all).
* The whole universe is currently a handful of rows — this endpoint is your "products" table.

### GET /v1/groups/daily

The same rollup as a per-day time series, for charting:

| Query param   | Description                                                         |
| ------------- | ------------------------------------------------------------------- |
| `from` / `to` | day window; accepts `YYYY-MM-DD`, ISO-8601, or unix (seconds or ms) |

```json
{
  "group_class": "all",
  "underlying": "all",
  "period": "all",
  "from": null,
  "to": null,
  "data": [
    { "day": "2026-05-02", "group_class": "priceBinary", "underlying": "BTC", "period": "1d", "volume_usdc": 2351355.36, "trades": 35781, "traders": 1860 }
  ]
}
```

Unfiltered it returns full history (HIP-4 launched 2026-05-02). The filter echo uses the literal string `"all"` when a filter wasn't supplied. `traders` here is summed per day — don't compare it directly against the lifetime `traders` in `/v1/groups`, they aggregate differently.

### GET /v1/groups/breakdown

The drill-down: one row per coin **side** within a group — the finest grain.

| Query param | Default | Description                              |
| ----------- | ------- | ---------------------------------------- |
| `limit`     | `100`   | capped at 500; garbage falls back to 100 |

```json
{
  "data": [
    {
      "group_class": "named",
      "underlying": "",
      "period": "",
      "coin": "#1730",
      "outcome_id": 173,
      "side_idx": 0,
      "side": "A",
      "volume_usdc": 4834485.34,
      "trades": 18292
    }
  ],
  "limit": 100
}
```

Sorted by volume; pass `underlying`/`group_class` filters to keep result sets meaningful. `side` is the raw `A`/`B` leg label — pair it with the outcome's `side_a_name`/`side_b_name` (from [`/v1/outcomes/:id`](/mute.sh/api/api-reference/outcomes.md)) for display names.

***

## Choosing a discovery signal

* Home-page "hot right now" → `/trending` (composite, gated, explainable)
* "Biggest moves today" ticker → `/movers` (clean, always populated when markets move)
* Countdown/urgency UI → `/closing-soon` (price markets only, by design)
* "Coin-flip markets" widget → `/most-contested` with a `min_volume` floor
* "New listings" feed → `/new` (or `/v1/registeredOutcomes` for the sync-oriented variant)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://mute-sh.gitbook.io/mute.sh/api/api-reference/discovery-and-groups.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
