---
title: "A story's articles (cursor-paginated)"
method: GET
path: "/api/stories/{story_id}/materials/"
tags: ["stories"]
---

# A story's articles (cursor-paginated)

`GET /api/stories/{story_id}/materials/`

## Request

- Base URL: `https://api.alphai.io`
- URL: `https://api.alphai.io/api/stories/{story_id}/materials/`
- Auth: HTTP bearer

Every article behind a story, oldest first — the materials the
summary was written from and the ones that joined after it. Same
visibility as the feed (`relevance_score >= 4`); each row carries its
`uid` (open it with `/api/news/{uid}/`), publisher, publication time,
the publisher's URL and its relevance score. A story's `key_facts`
point into this list by `source_uid`. Keyset pagination with
`cursor` / `next_cursor` as on `/api/stories/`; most stories fit one
page. Only a current story (`status: found`) has materials: a merged
or not-a-story id is a 404 here — resolve it through
`/api/stories/{story_id}/` first.

## Path parameters

- `story_id` string, required

## Query parameters

- `cursor` string
- `page_size` integer

## Response `200`

A page of the story's articles, oldest first.

- StoryMaterials
  - `story_id` string, required
  - `results` StoryMaterial[], required
    - `uid` string, required: The article's uid — `/api/news/{uid}/` has the enriched row.
    - `title` string, required
    - `publisher` string, required: Source domain.
    - `time_published` string, date-time, required
    - `url` string, required: The publisher's own URL of the article. For a PR Newswire release (`publisher` `prnewswire.com`) it is the release's permanent page on alphai.io.
    - `relevance_score` integer, required
    - `seo_eligible` boolean, required: Whether the article passes AlphAI's quality gate (relevance 7+, tickers, a news category, new information); the story page links such articles to their alphai.io page. Informational — the API serves every row regardless.
  - `next_cursor` string, nullable, required: Opaque cursor for the next (newer) page; `null` at the end.

## Other responses

- `400`: Invalid cursor
- `401`: Missing or invalid API key.
- `404`: No current story with that id (merged and single-publisher ids included)
- `429`: Rate limit exceeded — either the per-minute burst cap or the per-day volume cap. The `Retry-After` header is the earliest second a retry can succeed, not a guess (a burst block is short, ≤60s; a day-cap block is capped at 3600s — the true reset is `X-RateLimit-Reset`). The `X-RateLimit-*` trio shows the daily volume budget. The body's `extra` names your tier, its `limit_per_minute` / `limit_per_day`, `retry_after_seconds`, and — below Pro — an `upgrade` block with the higher tiers' caps and the pricing URL. A rejected request does not itself consume quota, so retrying after `Retry-After` costs you nothing extra.

## Changes

- **2026-10-09** `8cf7aaea97e3`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/alphai/apis/alphai-rest-api/changes/api/stories/:story_id/materials/get.md)

---

[API](https://skmtc.dev/alphai/apis/alphai-rest-api.md) - [All operations](https://skmtc.dev/alphai/apis/alphai-rest-api/llms.txt) - [OpenAPI document](https://skmtc.dev/alphai/apis/alphai-rest-api/revisions/7a88b559ccfa?raw)
