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

# Matched Clusters

> How AGG links the same event across venues, and which id to use as your cross-venue join key

The same real-world question is listed separately on every venue. AGG's matching pipeline links
those listings into a **matched cluster**, and discovery responses return the cluster already
assembled — you do not need to match markets yourself.

This page explains what a cluster is, which id identifies it, and where each cluster field lives on
the response.

## What a cluster is

A cluster is a set of venue events that AGG has determined are the same real-world event, plus the
market- and outcome-level pairings between them.

```
Matched cluster
  ├── venueEvent (polymarket)   ← anchor: the row discovery returns
  ├── venueEvent (kalshi)       ← member
  ├── venueEvent (predict)      ← member
  └── venueEvent (limitless)    ← member

Each venueEvent
  └── venueMarkets[]
        ├── venueMarketOutcomes[]      (Yes / No)
        └── matchedVenueMarkets[]      ← the same market on the other venues
```

Matching combines deterministic canonicalization, in-house LLM scoring, and manual verification.
Its **output** is the cluster you see on the response — you consume the result, not the process.

## Which id to join on

`GET /venue-events` returns **one row per cluster**. Every cluster has one **anchor** event, and the
list collapses each cluster down to it — member rows are not returned separately.

<Tip>
  **Use the `id` of the event returned by `GET /venue-events` as your cross-venue key.** It is the
  cluster's canonical handle, and it is the only identifier that refers to the cluster rather than
  to one venue's listing of it.
</Tip>

Do not derive your own key from titles, slugs, venue identifiers, or `aggKey` (see
[Where aggKey fits](#where-aggkey-fits) below).

<Warning>
  **Re-resolve rather than treating the id as permanent.** Clusters can merge as matching improves;
  when they do, the surviving cluster keeps one anchor and the other's id becomes an ordinary member
  id. It stays a valid event id and still resolves, but it no longer represents the cluster.

  Caching it for a session or a page is fine. If you store it long-term — a watchlist, a saved
  view — refresh it from discovery rather than assuming it will always come back as the cluster
  head.
</Warning>

## Cluster fields, and where they live

Two different scopes are easy to confuse:

| Field                             | Scope             | What it tells you                                                                                                   |
| --------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------- |
| `venues[]`, `venueCount`          | the **event row** | Which venues that row's cluster spans — use it for venue badges.                                                    |
| `marketCount`, `groupMarketCount` | the **event row** | How many markets that row carries.                                                                                  |
| `matchedVenueMarkets[]`           | each **market**   | The same market on the other venues, with their outcomes.                                                           |
| `settlementDiff`                  | the **cluster**   | Resolution-criteria differences between members. See [Settlement Differences](/recipes/settlement-key-differences). |

Cross-venue pricing hangs off `matchedVenueMarkets[]`, not off the event row. To show a best price,
collect each market's own id plus its `matchedVenueMarkets[].id`, then batch them into one
[`GET /midpoints`](/api-reference/orderbook/getmidpoints) call, or subscribe to those ids on the
[WebSocket midpoint stream](/api/websocket).

## Querying a member id

`GET /venue-events/{id}` does not resolve a member id up to its anchor — it returns exactly the row
you asked for. That row is still usable for rendering, because each of its markets carries the full
set of `matchedVenueMarkets[]` siblings regardless of which member you queried.

What differs between an anchor and a member response:

|                                         | Anchor id                | Member id                                                                   |
| --------------------------------------- | ------------------------ | --------------------------------------------------------------------------- |
| `matchedVenueMarkets[]` per market      | full cluster             | full cluster                                                                |
| `settlementDiff`                        | cluster-wide             | cluster-wide                                                                |
| `venueMarkets[]`                        | the anchor's own markets | **that member's own markets**                                               |
| `marketCount`, `groupMarketCount`       | anchor-scoped            | **member-scoped**                                                           |
| `venues[]`, `venueCount`                | the cluster's venues     | usually the same; narrower if that member has no matched markets of its own |
| `title`, `description`, `image`, `slug` | the anchor's             | **that venue's copy**                                                       |

So a member row can be missing markets the anchor carries — props or extra legs with no counterpart
elsewhere — and its title and description are the member venue's wording. For a canonical view of
the cluster, query the id you got from `GET /venue-events`.

## Market lists: preview vs full

<Warning>
  The `venueMarkets` array embedded on **`GET /venue-events`** list items is a preview — up to three
  markets chosen to represent the event on a card, not the complete set. Which three depends on the
  event's shape, so do not treat them as the largest or most liquid markets, and do not render
  counts or totals from the array length. Use `marketCount`.
</Warning>

`GET /venue-events/{id}` returns the event's markets uncapped. Both embedded arrays are deprecated:
fetch markets from [`GET /venue-markets?venueEventId=`](/api-reference/discovery/listvenuemarkets),
which is filterable, paginated, and returns the same `matchedVenueMarkets[]` siblings per market.

Note that `?venueEventId=` is event-scoped by design: it returns that event's own markets, with
cross-venue siblings inline on each market — not a flattened list of every member's markets.

## Where aggKey fits

[`aggKey`](/recipes/agg-key) is a deterministic canonical key computed at discovery time from each
venue's native data. It is one **input** to matching and a convenient fetch filter — it is not the
mechanism that produces clusters, and it is not a join key.

* It is `null` for a substantial share of rows. Grouping by `aggKey` silently under-groups every
  cluster whose members could not be canonicalized.
* It is guaranteed identical across venues only where every venue exposes the same normalizable
  inputs — sports head-to-head and crypto up/down markets. Elsewhere it is derived from each venue's
  own slug or ticker and its own resolution timestamp, so two members of the same cluster can carry
  different keys.

Use `aggKey` to fetch related rows for a key you already hold. Use the cluster and its anchor `id`
for identity.

## Related

<CardGroup cols={2}>
  <Card title="Comparing Venue Prices" icon="scale-balanced" href="/recipes/comparing-venue-prices">
    Fetch matched events and their midpoints to build a cross-venue price comparison.
  </Card>

  <Card title="Building Market Views" icon="layer-group" href="/recipes/building-market-views">
    Compose discovery and orderbook endpoints into event grids and trading views.
  </Card>

  <Card title="Canonical Market Key (aggKey)" icon="key" href="/recipes/agg-key">
    What aggKey is, when it is null, and the one supported way to parse it.
  </Card>

  <Card title="Settlement Differences" icon="scale-unbalanced" href="/recipes/settlement-key-differences">
    Surface resolution-criteria differences between venues in a cluster.
  </Card>
</CardGroup>
