# News & stories: API reference

12 endpoints. Base URL `https://princegeorgepulse.ca/api/v1`. Conventions (envelope, paging, fields, formats, limits) are on the [overview](https://princegeorgepulse.ca/developers/). [All endpoints](https://princegeorgepulse.ca/developers/reference/).

## GET /api/v1/news {#get-news}

Aggregated local news feed (headline, image, short description, link) plus PG Pulse stories, newest first.

Links only: follow `url` to read the original. Article bodies are never stored or served.

- Cache: 2 min (live source, cached on our server)
- Source: [PG Pulse news aggregator: headlines, short descriptions and links from local newsrooms, Reddit and YouTube](https://princegeorgepulse.ca/news/)
- Licence: Headlines and descriptions © their publishers; links only, no article bodies. Follow `url` to read the original.
- Paginated: `limit` (default 30, max 200) and `offset`; also `format=csv`

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `topic` | query | string | Topic slug (see /news/topics). Example: `civic`. |
| `source` | query | string | Source id or name, comma separated (see /news/sources). |
| `q` | query | string | Text search in title, description and source name (covers the whole archive; headlines are kept permanently, summaries for about 18 months). Example: `council`. |
| `from` | query | string | Only items published on or after this date (YYYY-MM-DD or ISO date-time). Example: `2026-09-01`. |
| `to` | query | string | Only items published on or before this date. |
| `kind` | query | string | Item kind. One of: `article`, `reddit`, `video`, `social`, `original`. |
| `sort` | query | string | `latest` (default): every item, newest first. `top`: one entry per story from the last 72 hours, ranked by score = source quality × (1 + 0.6 × (outlets − 1)) × 0.5^(age in hours / 12), each with `score` and `alsoCovered` (the other publishers’ copies), then the rest newest first. Editorial only: nothing paid or engagement-based. One of: `latest`, `top`. |
| `story` | query | string | Every copy of one story (its `storyId`). |
| `since` | query | string | Incremental pulls: only items first collected after this ISO date-time, oldest first (PG Pulse stories by publication time). Store `meta.updatedAt` and pass it next time. Example: `2026-09-30T00:00:00Z`. |
| `limit` | query | integer | Items per page (default 50, max 500 unless noted). Example: `20`. |
| `offset` | query | integer | Items to skip. Use `links.next` to page. Example: `0`. |

Also accepts `fields` (sparse fieldset) and `format` (`json`, `md`, `csv`).

**Example request**

```bash
curl "https://princegeorgepulse.ca/api/v1/news?topic=civic&limit=3"
```

**Example response** (trimmed)

```json
{
  "data": [
    {
      "id": "a786bde94e4dccb704d0",
      "url": "https://www.reddit.com/r/princegeorge/comments/1x7fxv4/city_election_last_minute_undecided_voter/",
      "title": "City Election - Last Minute Undecided Voter Info/Resource Dump",
      "description": "Alright look - you didn't make it to advance voting, and it's too late for a mail-in ballot. The only way you're voting in this municipal election is Saturday, October 17, at one of these eight locations between 8 am…",
      "image": null,
      "source": "reddit-princegeorge",
      "sourceName": "r/princegeorge",
      "date": "2026-10-16T13:15:22.000Z",
      "kind": "reddit",
      "topics": [
        "reddit",
        "civic"
      ],
      "author": "/u/YesItsASmurfAccount4",
      "firstSeen": "2026-10-16T15:15:09.343Z",
      "…": "2 more keys"
    },
    {
      "id": "52b1743d3d2f403b51a4",
      "url": "https://ckpgtoday.ca/2026/10/16/fall-storm-passing-through-prince-george-area-on-election-day/",
      "title": "Fall storm passing through Prince George area on election day",
      "description": "It's shaping up to be a messy municipal election day as Environment Canada says a fall storm will bring mixed weather conditions to th...",
      "image": "https://princegeorgepulse-media.s3.ca-central-1.amazonaws.com/prince-george/news/fall-storm-passing-through-prince-george-area-election-de6e17-480.webp",
      "source": "ckpg-today",
      "sourceName": "CKPG Today",
      "date": "2026-10-16T12:40:44.000Z",
      "kind": "article",
      "topics": [
        "local",
        "civic",
        "… 1 more"
      ],
      "firstSeen": "2026-10-16T13:45:06.271Z",
      "storyId": "52b1743d3d2f403b51a4",
      "…": "1 more keys"
    },
    "… 1 more"
  ],
  "meta": {
    "source": "PG Pulse news aggregator: headlines, short descriptions and links from local newsrooms, Reddit and YouTube",
    "sourceUrl": "https://princegeorgepulse.ca/news/",
    "updatedAt": "2026-10-16T15:45:12.915Z",
    "ageSec": 578,
    "license": "Headlines and descriptions © their publishers; links only, no article bodies. Follow `url` to read the original.",
    "attribution": "Each item’s publisher (`sourceName`), via PG Pulse (princegeorgepulse.ca)",
    "total": 213,
    "count": 3,
    "limit": 3,
    "offset": 0
  },
  "links": {
    "self": "/api/v1/news?topic=civic&limit=3",
    "next": "/api/v1/news?topic=civic&limit=3&offset=3",
    "docs": "/developers/reference/news-and-stories/#get-news"
  }
}
```

## GET /api/v1/news/archive {#get-news-archive}

The permanent headline archive: every month with headlines, with counts.

Headlines are kept permanently (headline, source, date, link, topics); summaries and images only for about 18 months. Get a month with /news/archive/{year}/{month}.

- Cache: 1 h
- Source: [PG Pulse news aggregator: headlines, short descriptions and links from local newsrooms, Reddit and YouTube](https://princegeorgepulse.ca/news/)
- Licence: Headlines and descriptions © their publishers; links only, no article bodies. Follow `url` to read the original.

Also accepts `fields` (sparse fieldset) and `format` (`json`, `md`).

**Example request**

```bash
curl "https://princegeorgepulse.ca/api/v1/news/archive"
```

**Example response** (trimmed)

```json
{
  "data": [
    {
      "year": 2026,
      "month": 10,
      "key": "2026-10",
      "count": 491,
      "label": "October 2026",
      "url": "/news/archive/2026/10/",
      "api": "/api/v1/news/archive/2026/10"
    },
    {
      "year": 2026,
      "month": 9,
      "key": "2026-09",
      "count": 274,
      "label": "September 2026",
      "url": "/news/archive/2026/09/",
      "api": "/api/v1/news/archive/2026/09"
    },
    "… 12 more"
  ],
  "meta": {
    "source": "PG Pulse news aggregator: headlines, short descriptions and links from local newsrooms, Reddit and YouTube",
    "sourceUrl": "https://princegeorgepulse.ca/news/",
    "updatedAt": "2026-10-16T15:45:12.915Z",
    "ageSec": 578,
    "license": "Headlines and descriptions © their publishers; links only, no article bodies. Follow `url` to read the original.",
    "attribution": "Each item’s publisher (`sourceName`), via PG Pulse (princegeorgepulse.ca)"
  },
  "links": {
    "self": "/api/v1/news/archive",
    "next": null,
    "docs": "/developers/reference/news-and-stories/#get-news-archive"
  }
}
```

## GET /api/v1/news/archive/{year} {#get-news-archive-year}

One year of the headline archive: its months with headline counts.

- Cache: 1 h
- Source: [PG Pulse news aggregator: headlines, short descriptions and links from local newsrooms, Reddit and YouTube](https://princegeorgepulse.ca/news/)
- Licence: Headlines and descriptions © their publishers; links only, no article bodies. Follow `url` to read the original.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `year` | path | string | Four-digit year (see /news/archive). Required. Example: `2026`. |

Also accepts `fields` (sparse fieldset) and `format` (`json`, `md`).

**Example request**

```bash
curl "https://princegeorgepulse.ca/api/v1/news/archive/2026"
```

**Example response** (trimmed)

```json
{
  "data": {
    "year": 2026,
    "total": 858,
    "url": "/news/archive/2026/",
    "months": [
      {
        "year": 2026,
        "month": 10,
        "key": "2026-10",
        "count": 491,
        "label": "October 2026",
        "url": "/news/archive/2026/10/",
        "api": "/api/v1/news/archive/2026/10"
      },
      {
        "year": 2026,
        "month": 9,
        "key": "2026-09",
        "count": 274,
        "label": "September 2026",
        "url": "/news/archive/2026/09/",
        "api": "/api/v1/news/archive/2026/09"
      },
      "… 8 more"
    ]
  },
  "meta": {
    "source": "PG Pulse news aggregator: headlines, short descriptions and links from local newsrooms, Reddit and YouTube",
    "sourceUrl": "https://princegeorgepulse.ca/news/",
    "updatedAt": "2026-10-16T15:45:12.915Z",
    "ageSec": 578,
    "license": "Headlines and descriptions © their publishers; links only, no article bodies. Follow `url` to read the original.",
    "attribution": "Each item’s publisher (`sourceName`), via PG Pulse (princegeorgepulse.ca)"
  },
  "links": {
    "self": "/api/v1/news/archive/2026",
    "next": null,
    "docs": "/developers/reference/news-and-stories/#get-news-archive-year"
  }
}
```

## GET /api/v1/news/archive/{year}/{month} {#get-news-archive-year-month}

Every headline of one month (local time), newest first: headline, source, date and link only.

- Cache: 1 h
- Source: [PG Pulse news aggregator: headlines, short descriptions and links from local newsrooms, Reddit and YouTube](https://princegeorgepulse.ca/news/)
- Licence: Headlines and descriptions © their publishers; links only, no article bodies. Follow `url` to read the original.
- Paginated: `limit` (default 200, max 1000) and `offset`; also `format=csv`

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `year` | path | string | Four-digit year. Required. Example: `2026`. |
| `month` | path | string | Two-digit month. Required. Example: `09`. |
| `q` | query | string | Words in the headline or source name. |
| `source` | query | string | Source id or name, comma separated. |
| `topic` | query | string | Topic slug (see /news/topics). |
| `limit` | query | integer | Items per page (default 50, max 500 unless noted). Example: `20`. |
| `offset` | query | integer | Items to skip. Use `links.next` to page. Example: `0`. |

Also accepts `fields` (sparse fieldset) and `format` (`json`, `md`, `csv`).

**Example request**

```bash
curl "https://princegeorgepulse.ca/api/v1/news/archive/2026/10"
```

**Example response** (trimmed)

```json
{
  "data": [
    {
      "id": "a786bde94e4dccb704d0",
      "title": "City Election - Last Minute Undecided Voter Info/Resource Dump",
      "url": "https://www.reddit.com/r/princegeorge/comments/1x7fxv4/city_election_last_minute_undecided_voter/",
      "source": "reddit-princegeorge",
      "sourceName": "r/princegeorge",
      "date": "2026-10-16T13:15:22.000Z",
      "day": "2026-10-16",
      "kind": "reddit",
      "topics": [
        "reddit",
        "civic"
      ]
    },
    {
      "id": "52b1743d3d2f403b51a4",
      "title": "Fall storm passing through Prince George area on election day",
      "url": "https://ckpgtoday.ca/2026/10/16/fall-storm-passing-through-prince-george-area-on-election-day/",
      "source": "ckpg-today",
      "sourceName": "CKPG Today",
      "date": "2026-10-16T12:40:44.000Z",
      "day": "2026-10-16",
      "kind": "article",
      "topics": [
        "local",
        "civic",
        "… 1 more"
      ]
    },
    "… 198 more"
  ],
  "meta": {
    "source": "PG Pulse news aggregator: headlines, short descriptions and links from local newsrooms, Reddit and YouTube",
    "sourceUrl": "https://princegeorgepulse.ca/news/",
    "updatedAt": "2026-10-16T15:45:12.915Z",
    "ageSec": 578,
    "license": "Headlines and descriptions © their publishers; links only, no article bodies. Follow `url` to read the original.",
    "attribution": "Each item’s publisher (`sourceName`), via PG Pulse (princegeorgepulse.ca)",
    "total": 491,
    "count": 200,
    "limit": 200,
    "offset": 0
  },
  "links": {
    "self": "/api/v1/news/archive/2026/10",
    "next": "/api/v1/news/archive/2026/10?offset=200",
    "docs": "/developers/reference/news-and-stories/#get-news-archive-year-month"
  }
}
```

## GET /api/v1/news/trends {#get-news-trends}

What Prince George is talking about: terms rising in local headlines and summaries this week.

Words and two-word phrases from the headlines and publishers’ short summaries of the last 7 days, compared with the average week of the 4 weeks before: score = (items this week − baseline) / √(baseline + 1), at least 3 items from 2 publishers; single words only when written as names. Counts only, never text. Recomputed hourly.

- Cache: 15 min
- Source: [PG Pulse news aggregator: headlines, short descriptions and links from local newsrooms, Reddit and YouTube](https://princegeorgepulse.ca/news/)
- Licence: Headlines and descriptions © their publishers; links only, no article bodies. Follow `url` to read the original.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `limit` | query | integer | How many terms (1-40, default 20). |

Also accepts `fields` (sparse fieldset) and `format` (`json`, `md`).

**Example request**

```bash
curl "https://princegeorgepulse.ca/api/v1/news/trends"
```

**Example response** (trimmed)

```json
{
  "data": [
    {
      "term": "sintich",
      "label": "Sintich",
      "rank": 1,
      "items": 5,
      "sources": 4,
      "baseline": 0,
      "score": 5,
      "sourceNames": [
        "Prince George Citizen",
        "My Prince George Now",
        "… 2 more"
      ],
      "computedAt": "2026-10-16T13:07:04.960Z",
      "url": "/news/trends/?term=sintich",
      "api": "/api/v1/news/trends/sintich"
    },
    {
      "term": "travel trailer",
      "label": "travel trailer",
      "rank": 2,
      "items": 5,
      "sources": 4,
      "baseline": 0,
      "score": 5,
      "sourceNames": [
        "Prince George Citizen",
        "My Prince George Now",
        "… 2 more"
      ],
      "computedAt": "2026-10-16T13:07:04.960Z",
      "url": "/news/trends/?term=travel%20trailer",
      "api": "/api/v1/news/trends/travel%20trailer"
    },
    "… 18 more"
  ],
  "meta": {
    "source": "PG Pulse news aggregator: headlines, short descriptions and links from local newsrooms, Reddit and YouTube",
    "sourceUrl": "https://princegeorgepulse.ca/news/",
    "updatedAt": "2026-10-16T13:07:04.960Z",
    "ageSec": 10066,
    "license": "Headlines and descriptions © their publishers; links only, no article bodies. Follow `url` to read the original.",
    "attribution": "Each item’s publisher (`sourceName`), via PG Pulse (princegeorgepulse.ca)"
  },
  "links": {
    "self": "/api/v1/news/trends",
    "next": null,
    "docs": "/developers/reference/news-and-stories/#get-news-trends"
  }
}
```

## GET /api/v1/news/trends/{term} {#get-news-trends-term}

Daily use of one term in local news: items and distinct publishers per day.

- Cache: 15 min
- Source: [PG Pulse news aggregator: headlines, short descriptions and links from local newsrooms, Reddit and YouTube](https://princegeorgepulse.ca/news/)
- Licence: Headlines and descriptions © their publishers; links only, no article bodies. Follow `url` to read the original.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `term` | path | string | A word or two-word phrase (case and plurals do not matter). Required. Example: `spruce kings`. |
| `days` | query | integer | Days back (7-400, default 60). |

Also accepts `fields` (sparse fieldset) and `format` (`json`, `md`).

**Example request**

```bash
curl "https://princegeorgepulse.ca/api/v1/news/trends/sintich"
```

**Example response** (trimmed)

```json
{
  "data": {
    "term": "sintich",
    "series": [
      {
        "day": "2026-08-18",
        "items": 0,
        "sources": 0
      },
      {
        "day": "2026-08-19",
        "items": 0,
        "sources": 0
      },
      "… 58 more"
    ],
    "total": 0,
    "search": "/api/v1/news?q=sintich"
  },
  "meta": {
    "source": "PG Pulse news aggregator: headlines, short descriptions and links from local newsrooms, Reddit and YouTube",
    "sourceUrl": "https://princegeorgepulse.ca/news/",
    "updatedAt": "2026-10-16T13:07:04.960Z",
    "ageSec": 10066,
    "license": "Headlines and descriptions © their publishers; links only, no article bodies. Follow `url` to read the original.",
    "attribution": "Each item’s publisher (`sourceName`), via PG Pulse (princegeorgepulse.ca)"
  },
  "links": {
    "self": "/api/v1/news/trends/sintich",
    "next": null,
    "docs": "/developers/reference/news-and-stories/#get-news-trends-term"
  }
}
```

## GET /api/v1/news/topics {#get-news-topics}

News topics.

- Cache: 24 h
- Source: [PG Pulse news aggregator: headlines, short descriptions and links from local newsrooms, Reddit and YouTube](https://princegeorgepulse.ca/news/)
- Licence: Headlines and descriptions © their publishers; links only, no article bodies. Follow `url` to read the original.

Also accepts `fields` (sparse fieldset) and `format` (`json`, `md`).

**Example request**

```bash
curl "https://princegeorgepulse.ca/api/v1/news/topics"
```

**Example response** (trimmed)

```json
{
  "data": [
    {
      "slug": "local",
      "label": "Local news",
      "icon": "Newspaper",
      "title": "Prince George local news",
      "description": "The latest headlines from Prince George newsrooms: the Citizen, CKPG Today, My PG Now and more, in one feed.",
      "rule": {
        "terms": [],
        "exclude": [],
        "veto": [],
        "min": 2
      },
      "url": "/news/local/",
      "api": "/api/v1/news?topic=local"
    },
    {
      "slug": "civic",
      "label": "Civic and politics",
      "icon": "Landmark",
      "title": "Prince George politics and civic news",
      "description": "City council, the regional district, school board, elections and provincial decisions that affect Prince George.",
      "rule": {
        "terms": [],
        "exclude": [],
        "veto": [],
        "min": 2
      },
      "url": "/news/civic/",
      "api": "/api/v1/news?topic=civic"
    },
    "… 11 more"
  ],
  "meta": {
    "source": "PG Pulse news aggregator: headlines, short descriptions and links from local newsrooms, Reddit and YouTube",
    "sourceUrl": "https://princegeorgepulse.ca/news/",
    "updatedAt": null,
    "license": "Headlines and descriptions © their publishers; links only, no article bodies. Follow `url` to read the original.",
    "attribution": "Each item’s publisher (`sourceName`), via PG Pulse (princegeorgepulse.ca)"
  },
  "links": {
    "self": "/api/v1/news/topics",
    "next": null,
    "docs": "/developers/reference/news-and-stories/#get-news-topics"
  }
}
```

## GET /api/v1/news/sources {#get-news-sources}

Every news source with item counts and last fetch status.

- Cache: 10 min
- Source: [PG Pulse news aggregator: headlines, short descriptions and links from local newsrooms, Reddit and YouTube](https://princegeorgepulse.ca/news/)
- Licence: Headlines and descriptions © their publishers; links only, no article bodies. Follow `url` to read the original.

Also accepts `fields` (sparse fieldset) and `format` (`json`, `md`).

**Example request**

```bash
curl "https://princegeorgepulse.ca/api/v1/news/sources"
```

**Example response** (trimmed)

```json
{
  "data": [
    {
      "id": "pg-pulse",
      "name": "PG Pulse",
      "type": "original",
      "homepage": "/articles/",
      "quality": 5,
      "topicHints": [],
      "logo": "/favicon.svg",
      "enabled": true,
      "note": "Our own reporting and explainers.",
      "items": 6,
      "ok": true,
      "error": null,
      "…": "2 more keys"
    },
    {
      "id": "pg-citizen-local",
      "name": "Prince George Citizen",
      "type": "rss",
      "homepage": "https://www.princegeorgecitizen.com/",
      "quality": 5,
      "topicHints": [
        "local"
      ],
      "logo": "https://princegeorgepulse-media.s3.ca-central-1.amazonaws.com/prince-george/logos/prince-george-citizen-logo-fb89e2-64.webp",
      "enabled": true,
      "note": null,
      "items": 102,
      "ok": true,
      "error": null,
      "…": "6 more keys"
    },
    "… 29 more"
  ],
  "meta": {
    "source": "PG Pulse news aggregator: headlines, short descriptions and links from local newsrooms, Reddit and YouTube",
    "sourceUrl": "https://princegeorgepulse.ca/news/",
    "updatedAt": "2026-10-16T15:45:12.915Z",
    "ageSec": 578,
    "license": "Headlines and descriptions © their publishers; links only, no article bodies. Follow `url` to read the original.",
    "attribution": "Each item’s publisher (`sourceName`), via PG Pulse (princegeorgepulse.ca)"
  },
  "links": {
    "self": "/api/v1/news/sources",
    "next": null,
    "docs": "/developers/reference/news-and-stories/#get-news-sources"
  }
}
```

## GET /api/v1/stories {#get-stories}

PG Pulse stories (original articles), newest first.

- Cache: 1 h
- Source: [PG Pulse stories (original reporting and explainers)](https://princegeorgepulse.ca/articles/)
- Licence: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
- Paginated: `limit` (default 50, max 200) and `offset`; also `format=csv`

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `category` | query | string | Story category. Example: `Guide`. |
| `tag` | query | string | Tag. |
| `q` | query | string | Text search. |
| `body` | query | boolean | Include the Markdown body. |
| `limit` | query | integer | Items per page (default 50, max 500 unless noted). Example: `20`. |
| `offset` | query | integer | Items to skip. Use `links.next` to page. Example: `0`. |

Also accepts `fields` (sparse fieldset) and `format` (`json`, `md`, `csv`).

**Example request**

```bash
curl "https://princegeorgepulse.ca/api/v1/stories"
```

**Example response** (trimmed)

```json
{
  "data": [
    {
      "slug": "fall-in-prince-george-whats-on",
      "title": "Fall in Prince George: what’s on this season",
      "description": "Autumn in Prince George: golden trails, hockey’s return, a new arts season, Truth and Reconciliation Day, two elections and getting ready for winter.",
      "date": "2026-09-26T00:00:00.000Z",
      "author": "PG Pulse staff",
      "category": "Events",
      "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/42/Rainbow_over_Prince_George%2C_BC.jpg/1280px-Rainbow_over_Prince_George%2C_BC.jpg",
      "imageAlt": "A bright rainbow arching over the rooftops and trees of Prince George",
      "imageCredit": "Tim Wenzel, CC BY-SA 3.0 via Wikimedia Commons",
      "tags": [
        "fall",
        "events",
        "… 4 more"
      ],
      "featured": false,
      "sources": [
        {
          "title": "Tourism Prince George events",
          "url": "https://tourismpg.com/"
        },
        {
          "title": "Fall in Love with Fall: 6 Trails to Stroll This Autumn (Tourism Prince George)",
          "url": "https://tourismpg.com/fall-in-love-with-fall-6-trails-to-stroll-this-autumn/"
        },
        "… 4 more"
      ],
      "…": "4 more keys"
    },
    {
      "slug": "prince-george-weather-compared-with-bc",
      "title": "How Prince George’s weather compares with the rest of BC",
      "description": "Official 1991–2020 climate normals for Prince George and eight other BC cities: the north is colder in winter, but summer highs match Vancouver’s.",
      "date": "2026-09-22T00:00:00.000Z",
      "author": "PG Pulse staff",
      "category": "Data",
      "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c7/UNBC.jpg/1280px-UNBC.jpg",
      "imageAlt": "Snow on the buildings of the University of Northern British Columbia campus in winter",
      "imageCredit": "Thegrammarguy, CC BY-SA 4.0 via Wikimedia Commons",
      "tags": [
        "weather",
        "climate",
        "… 4 more"
      ],
      "featured": false,
      "sources": [
        {
          "title": "Canadian Climate Normals 1991–2020 (Environment and Climate Change Canada)",
          "url": "https://climate.weather.gc.ca/climate_normals/index_e.html"
        },
        {
          "title": "Prince George weather box (Wikipedia template summarizing ECCC normals)",
          "url": "https://en.wikipedia.org/wiki/Template:Prince_George_weatherbox"
        },
        "… 8 more"
      ],
      "…": "4 more keys"
    },
    "… 4 more"
  ],
  "meta": {
    "source": "PG Pulse stories (original reporting and explainers)",
    "sourceUrl": "https://princegeorgepulse.ca/articles/",
    "updatedAt": null,
    "license": "CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)",
    "attribution": "PG Pulse (princegeorgepulse.ca)",
    "total": 6,
    "count": 6,
    "limit": 50,
    "offset": 0
  },
  "links": {
    "self": "/api/v1/stories",
    "next": null,
    "docs": "/developers/reference/news-and-stories/#get-stories"
  }
}
```

## GET /api/v1/stories/{slug} {#get-stories-slug}

One PG Pulse story with its Markdown body.

- Cache: 1 h
- Source: [PG Pulse stories (original reporting and explainers)](https://princegeorgepulse.ca/articles/)
- Licence: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `slug` | path | string | Story slug. Required. Example: `why-prince-george-sits-in-a-bowl`. |

Also accepts `fields` (sparse fieldset) and `format` (`json`, `md`).

**Example request**

```bash
curl "https://princegeorgepulse.ca/api/v1/stories/why-prince-george-sits-in-a-bowl"
```

**Example response** (trimmed)

```json
{
  "data": {
    "slug": "why-prince-george-sits-in-a-bowl",
    "title": "Why Prince George sits in a bowl: the lake that disappeared",
    "description": "Downtown Prince George sits on the floor of a vanished ice-age landscape. How ice, a glacial lake and two rivers made the bowl and cutbanks.",
    "date": "2026-09-03T00:00:00.000Z",
    "author": "PG Pulse staff",
    "category": "Explainer",
    "image": "https://upload.wikimedia.org/wikipedia/commons/7/7f/Prince_George_British_Columbia_2011.JPG",
    "imageAlt": "View east over Prince George from University Hill, with the city in the valley and forested plateau beyond",
    "imageCredit": "Iranianson, CC BY-SA 3.0 via Wikimedia Commons",
    "tags": [
      "geology",
      "glacial lake",
      "… 3 more"
    ],
    "featured": true,
    "sources": [
      {
        "title": "Geotour Guide for Prince George, British Columbia (GSC Open File 5559 / BCGS Geofile 2010-10)",
        "url": "https://mineralsed.ca/site/assets/files/3451/princegeorgegeotourguide.pdf"
      },
      {
        "title": "Cutbanks (The Exploration Place)",
        "url": "https://theexplorationplace.com/project/cutbanks/"
      },
      "… 2 more"
    ],
    "…": "3 more keys"
  },
  "meta": {
    "source": "PG Pulse stories (original reporting and explainers)",
    "sourceUrl": "https://princegeorgepulse.ca/articles/",
    "updatedAt": null,
    "license": "CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)",
    "attribution": "PG Pulse (princegeorgepulse.ca)"
  },
  "links": {
    "self": "/api/v1/stories/why-prince-george-sits-in-a-bowl",
    "next": null,
    "docs": "/developers/reference/news-and-stories/#get-stories-slug"
  }
}
```

## GET /api/v1/posts {#get-posts}

Published PG Pulse posts from the site’s publishing tool (empty until posts exist).

- Cache: 5 min
- Source: [PG Pulse posts](https://princegeorgepulse.ca/posts/)
- Licence: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
- Paginated: `limit` (default 50, max 200) and `offset`; also `format=csv`

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `category` | query | string | Post category. Example: `Community`. |
| `limit` | query | integer | Items per page (default 50, max 500 unless noted). Example: `20`. |
| `offset` | query | integer | Items to skip. Use `links.next` to page. Example: `0`. |

Also accepts `fields` (sparse fieldset) and `format` (`json`, `md`, `csv`).

**Example request**

```bash
curl "https://princegeorgepulse.ca/api/v1/posts"
```

**Example response** (trimmed)

```json
{
  "data": [],
  "meta": {
    "source": "PG Pulse posts",
    "sourceUrl": "https://princegeorgepulse.ca/posts/",
    "updatedAt": null,
    "license": "CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)",
    "attribution": "PG Pulse (princegeorgepulse.ca)",
    "total": 0,
    "count": 0,
    "limit": 50,
    "offset": 0
  },
  "links": {
    "self": "/api/v1/posts",
    "next": null,
    "docs": "/developers/reference/news-and-stories/#get-posts"
  }
}
```

## GET /api/v1/posts/{slug} {#get-posts-slug}

One published PG Pulse post with its Markdown body.

- Cache: 5 min
- Source: [PG Pulse posts](https://princegeorgepulse.ca/posts/)
- Licence: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `slug` | path | string | Post slug (see /posts). Required. |

Also accepts `fields` (sparse fieldset) and `format` (`json`, `md`).

**Example request**

```bash
curl "https://princegeorgepulse.ca/api/v1/posts/{slug}"
```
