---
title: "List published posts (headless delivery)"
method: GET
path: "/cms/{deliveryToken}/posts"
tags: ["CMS Delivery (Public)"]
---

# List published posts (headless delivery)

`GET /cms/{deliveryToken}/posts`

## Request

- Base URL: `https://next.frase.io/api`
- URL: `https://next.frase.io/api/cms/{deliveryToken}/posts`
- Auth: none required

Public, token-gated headless delivery. Returns published posts for the site identified by the fcms_ delivery token. No API key; the token in the path both identifies and authorizes the site. Rate-limited per-IP and per-site; metered against the site's monthly view allowance.

## Path parameters

- `deliveryToken` string, required

## Query parameters

- `type` 'blog_post' | 'page'
- `limit` integer
- `offset` integer
- `updated_since` string, date-time
- `category` string
- `tag` string

## Headers

- `If-None-Match` string

## Response `200`

Published posts. Carries an ETag (sha1 of the page's id + updated_at); send it back as If-None-Match for a 304 when nothing changed.

- object
  - `posts` CmsDeliveryPostSummary[]
    - `id` string, uuid, required
    - `slug` string, required
    - `type` 'blog_post' | 'page', required
    - `title` string, required
    - `excerpt` string, nullable
    - `featured_image` string, uri, nullable
    - `featured_image_alt` string, nullable
    - `seo_title` string, nullable
    - `seo_description` string, nullable
    - `focus_keyword` string, nullable
    - `canonical_url` string, uri, nullable
    - `schema_markup` object, nullable: Editor-authored JSON-LD structured data, if set.
    - `og_image` string, uri, nullable
    - `og_title` string, nullable
    - `og_description` string, nullable
    - `published_at` string, date-time, nullable
    - `created_at` string, date-time
    - `updated_at` string, date-time
    - `primary_author` object, nullable: Primary/canonical author, or null.
      - `id` string, uuid, required
      - `name` string, required
      - `slug` string, required
      - `avatar_url` string, uri, nullable
      - `bio` string, nullable
      - `role` string, nullable: e.g. "Head of Content". Set on author creation only; a re-import never overwrites an existing author's role.
    - `authors` object[]: Byline authors (incl. the primary), ordered by position.
      - `id` string, uuid, required
      - `name` string, required
      - `slug` string, required
      - `position` integer, required
      - `avatar_url` string, uri, nullable
      - `role` string, nullable: e.g. "Head of Content". Set on author creation only; a re-import never overwrites an existing author's role.
    - `category` object, nullable: Single category, or null.
      - `id` string, uuid, required
      - `name` string, required
      - `slug` string, required
      - `parent_id` string, uuid, nullable
    - `tags` object[]
      - `id` string, uuid, required
      - `name` string, required
      - `slug` string, required
  - `pagination` object
    - `limit` integer
    - `offset` integer
    - `hasMore` boolean

## Other responses

- `304`: Not Modified. If-None-Match matched the current ETag; empty body, ETag header still set.
- `400`: updated_since is not a valid ISO 8601 date.
- `401`: Missing or invalid fcms_ delivery token
- `402`: Site is over its monthly view limit (quota block, distinct from the transient 429 rate limit)
- `404`: CMS not enabled for this site, or post not found
- `429`: Rate limited, or the org's monthly brief limit blocked minting the refreshed brief (only the FIRST supersession of a brief is billing-exempt; repeats meter normally)
- `503`: Subscription inactive, delivery temporarily unavailable

---

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