

Six GET feeds, all content-negotiated. Point any reader, crawler, or agent at a URL below — or just hand your reader a page on this site, which now advertises the right feed itself (see [Autodiscovery](#autodiscovery)).

```
GET https://api.metagraph.sh/api/v1/feeds/registry
```

## Feeds [#feeds]

| Path                             | Summary                                                                                    | Item tags                                                                                                     |
| -------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `/api/v1/feeds/registry`         | Registry changes from the 6h changelog deltas — subnets, artifacts, and coverage.          | `registry` + one of `subnet`/`artifact`/`coverage` + the change verb (`added`/`removed`/`renamed`/`modified`) |
| `/api/v1/feeds/incidents`        | Reconstructed incident history — one item per incident, opened and resolved.               | `incident`, `sn<netuid>`, and `ongoing` or `resolved`                                                         |
| `/api/v1/feeds/gaps`             | Open coverage gaps — what each subnet is still missing, by queue lane.                     | `gaps`, the queue lane, `sn<netuid>`, and each missing/direct-submission kind                                 |
| `/api/v1/feeds/upgrades`         | Bittensor runtime upgrades — releases, observed chain spec-version changes, BITs.          | `chain`, `upgrade`, and one of `release`/`mainnet`/`testnet`/`bit`                                            |
| `/api/v1/feeds/revenue`          | Revenue-coverage movement — ratio moves, provenance changes, surfaces that stop answering. | `revenue` + one of `coverage-move`/`provenance`/`surface-new`/`surface-dark`/`surface-unreadable`             |
| `/api/v1/feeds/wallets`          | Declared-wallet movement — attributions, reviews, treasury flow, burn-claim discrepancies. | `wallets` + one of `attribution`/`review`/`flow`/`burn-claim`                                                 |
| `/api/v1/feeds/subnets/{netuid}` | Everything above, narrowed to a single subnet.                                             | the same tags as the feed each item came from                                                                 |
| `/api/v1/feeds/watch?ids=`       | Registry changes + incidents for a set of watched entities, named in the URL.              | the same tags as the feed each item came from                                                                 |

## Watchlist feed [#watchlist-feed]

Watchlists are local-first — starring something writes to your browser's own storage, never to an account we hold. The watch feed keeps that posture: the entity set travels **in the URL**, not in anything stored server-side.

```
GET https://api.metagraph.sh/api/v1/feeds/watch.json?ids=s1,s7,s74
```

`?ids=` is a comma-separated list of kind-prefixed entities, up to 50 per URL:

| Prefix | Kind      | Example    |
| ------ | --------- | ---------- |
| `s`    | subnet    | `s7`       |
| `v`    | validator | `v5FHneW…` |
| `a`    | account   | `a5Grwva…` |

<Callout title="This URL is the credential" type="warn">
  Anyone with this URL sees which entities it tracks. There's no account behind it and nothing to
  revoke — treat it like you would a private RSS link, and generate a new one (drop entities,
  rebuild the URL) if it leaks somewhere you didn't intend.
</Callout>

Only `subnet` ids currently resolve to items — registry changes and incidents, exactly like `/api/v1/feeds/subnets/{netuid}`, merged across every watched subnet. `validator`/`account` ids are accepted and counted toward the 50-id cap (so a URL built from a mixed watchlist never silently exceeds it), but there is no change-tracking data source for either yet; the feed's own `description` field says plainly how many of the requested ids produced nothing, rather than staying silent about the gap. More than 50 ids in one URL is a `413`.

## Revenue movement [#revenue-movement]

`/api/v1/feeds/revenue` reports movement in what subnets earn against what the network emits to them. Four item kinds: a **material coverage-ratio move** (at least 20% of the prior ratio, both endpoints priced), a **provenance change** in either direction, a **newly-readable surface**, and a **surface that stops returning a figure**.

The last one is why this feed exists. An operator can withdraw a public revenue figure once an unflattering ratio is published, and a feed that silently stops is indistinguishable from a subnet that never had revenue. Those are very different facts, so a withdrawal becomes a dated event and the prior observations are retained.

Every item states what was observed and never why. A surface that stopped answering may have moved, may be down, may have been withdrawn, or may be misread by us — and the item says so rather than picking one. A high subsidy multiple is not an accusation; see [Revenue coverage](/docs/revenue-coverage) for what the ratio does and does not mean.

```
GET https://api.metagraph.sh/api/v1/feeds/revenue.json
GET https://api.metagraph.sh/api/v1/feeds/registry.json?tag=revenue
```

Both forms carry the same items: the standalone feed, and the site-wide registry feed narrowed by tag.

## Wallet movement [#wallet-movement]

`/api/v1/feeds/wallets` reports movement across subnets' declared wallets: a **new attribution**, a **maintainer review** of one, **material treasury flow** (over $10,000, priced through the moving token's own pool), and **outbound movement from an address declared unspendable**.

That last item is the highest-consequence thing this API emits, so its wording is fixed in one place rather than written per event. It states the published claim, the observed movement, and the delta between them — then says, in the same paragraph, that our own attribution may be wrong, that the unspendability proof may have been weaker than it looked, and that this lane cannot distinguish those. &#x2A;*It asserts nothing about intent.** If you repeat one of these, repeat its reading too.

Every attributed address in these items carries the `source_urls` that back the attribution. An item about an address with no recorded evidence says so explicitly, because that is itself worth knowing before repeating it.

Treasury flow is priced only where it can be: TAO through the TAO/USD index, alpha through **its own subnet's** pool. A leg that cannot be priced produces no item rather than one comparing two different tokens. See [Wallet attribution](/docs/wallet-attribution) for what each role means and what `unresolved` does not mean.

```
GET https://api.metagraph.sh/api/v1/feeds/wallets.json
GET https://api.metagraph.sh/api/v1/feeds/registry.json?tag=wallets
```

## Runtime upgrades [#runtime-upgrades]

Subtensor runtime upgrades reach mainnet with no on-chain warning — they are sudo-applied, and the Scheduler queue is empty — so the only observable signal is off-chain: a GitHub release, then a testnet deploy, then mainnet. `/api/v1/feeds/upgrades` carries all three.

```
GET https://api.metagraph.sh/api/v1/feeds/upgrades.rss
```

Items link to their primary source: a release item to the GitHub release, a chain item to the [runtime timeline](/docs/api-reference/blocks/runtime-versions). Chain items say the change was **detected** at that time rather than applied — the radar polls twice an hour, so it cannot pin the upgrade instant.

**No dates are predicted here.** The foundation publishes no deploy schedule, so this feed reports what has happened and never when something will. If you need the current state rather than the history, `GET /api/v1/runtime` returns a `current` block with the live mainnet and testnet spec versions and a `pending_upgrade` of `none`, `testnet_soaking`, `released_undeployed`, or `unknown`.

The same items also appear in `/api/v1/feeds/registry`, so `?tag=upgrade` narrows that feed to them alone.

## Autodiscovery [#autodiscovery]

Pages on this site advertise their feed in the document head, so a reader can find it from the page URL alone — paste `https://metagraph.sh/subnets/8` into any feed reader and it resolves that subnet's feed without you hunting for it.

| Page                | Advertises                       |
| ------------------- | -------------------------------- |
| Any page            | `/api/v1/feeds/registry`         |
| `/subnets/{netuid}` | `/api/v1/feeds/subnets/{netuid}` |

Both are offered as RSS and Atom:

```html
<link
  rel="alternate"
  type="application/rss+xml"
  href="https://api.metagraph.sh/api/v1/feeds/registry.rss"
/>
<link
  rel="alternate"
  type="application/atom+xml"
  href="https://api.metagraph.sh/api/v1/feeds/registry.atom"
/>
```

JSON Feed is not advertised for autodiscovery — readers probe for RSS and Atom — but it is served at the same paths with a `.json` suffix, and every feed is described in the [OpenAPI contract](/docs/api-reference) and in `GET /api/v1/contracts` under `feeds`.

## Formats [#formats]

Format precedence: an explicit `.rss` / `.atom` / `.json` suffix wins, then the `Accept` header, then JSON Feed as the default.

| Suffix  | Example                       | Content-Type                           |
| ------- | ----------------------------- | -------------------------------------- |
| `.json` | `/api/v1/feeds/registry.json` | `application/feed+json; charset=utf-8` |
| `.rss`  | `/api/v1/feeds/registry.rss`  | `application/rss+xml; charset=utf-8`   |
| `.atom` | `/api/v1/feeds/registry.atom` | `application/atom+xml; charset=utf-8`  |

## Filtering & paging [#filtering--paging]

Every param composes. Use `?since=` for incremental polling and `?tag=` to turn one feed URL into a focused subscription.

| Param     | Value        | Notes                                                                                                                                                         |
| --------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `?tag=`   | `<tag>`      | Narrow a feed to items carrying that tag, so one URL serves a focused subscription. An unknown tag yields an empty (but valid) feed.                          |
| `?since=` | `<ISO-8601>` | Only items at or after that instant, for incremental polling. A bare calendar date resolves to the inclusive start of that UTC day. Malformed input is a 400. |
| `?until=` | `<ISO-8601>` | Only items at or before that instant. A bare calendar date is inclusive of the whole UTC day (end-of-day), symmetric with `since`. Malformed input is a 400.  |
| `?limit=` | `1..50`      | Cap the number of returned items (default and hard cap 50). A larger value clamps; a non-integer or \< 1 is a 400.                                            |

<Callout title="50 items is a hard cap">
  `?limit=` clamps at 50 regardless of what you pass — there's no way to get more in one response.
  Poll with `?since=` for anything beyond that instead of raising the limit.
</Callout>

Feeds aren't part of the typed `@jsonbored/metagraphed` / `metagraphed` SDKs — they're plain content-negotiated HTTP, so any HTTP client works:

<CodeBlockTabs defaultValue="cURL">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="cURL">
      cURL
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="JavaScript">
      JavaScript
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="Python">
      Python
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="cURL">
    ```bash
    curl -s 'https://api.metagraph.sh/api/v1/feeds/registry.json?limit=10'
    ```
  </CodeBlockTab>

  <CodeBlockTab value="JavaScript">
    ```js
    const res = await fetch("https://api.metagraph.sh/api/v1/feeds/registry.json?limit=10");
    const feed = await res.json();
    ```
  </CodeBlockTab>

  <CodeBlockTab value="Python">
    ```python
    from urllib.request import urlopen
    import json

    with urlopen("https://api.metagraph.sh/api/v1/feeds/registry.json?limit=10") as res:
        feed = json.load(res)
    ```
  </CodeBlockTab>
</CodeBlockTabs>

Responses are cached for 600s and capped at 50 items. Browse the same data: [Gaps](/contribute). Machine index: [For agents](/agents).
