---
title: "Performance for one CMS post"
method: GET
path: "/cms/posts/{id}/performance"
tags: ["CMS"]
---

# Performance for one CMS post

`GET /cms/posts/{id}/performance`

## Request

- Base URL: `https://next.frase.io/api/v1`
- URL: `https://next.frase.io/api/v1/cms/posts/{id}/performance`
- Auth: API key in header `X-API-KEY`

Quality scores, Search Console metrics and a health grade for a single post. Requires the `read:analytics` scope; the post is loaded by id together with your organization, so another organization's post is an ordinary 404. Scores resolve by precedence — the site's latest completed audit page matching the post's slug, then the content URL registry mirror (which covers posts the latest crawl missed), then the generation-time scores on the linked content row; `scores.source` names the dominant source, though individual pillars can still fall through independently. `technical_score`, `content_score`, `content_decay_score`, `word_count` and `issue_count` come only from an audit page and are null without one. Search metrics are Search Console data for the site, summed and averaged over the trailing 28 days and matched to the post's slug; `search` is null when there is no matching data. `health_status` is graded from the overall score and the search position, and is `no_data` when neither is available — a post with no computable score is no longer graded as healthy. `demand` is an optional Answers enrichment and is null when Answers is off, the post has no linked content, or no signal matches. `published_url` is resolved from the site's FraseCMS settings and is only present for published posts.

## Path parameters

- `id` string, uuid, required

## Response `200`

Post performance snapshot.

- object
  - `data` object
    - `post_id` string, uuid
    - `slug` string
    - `title` string
    - `status` 'draft' | 'published' | 'scheduled': The post row's own status (localPostStatusEnum). `scheduled` rows sit here until the publisher cron flips them.
    - `published_at` string, date-time, nullable
    - `published_url` string, nullable: Live URL built from the site's FraseCMS settings. Null unless the post is published (or the site has no CMS settings row).
    - `scores` object
      - `seo_score` number, nullable
      - `geo_score` number, nullable
      - `eeat_score` number, nullable
      - `aeo_score` number, nullable: Audit or registry only — never falls back to generation scores.
      - `overall_score` number, nullable: The audit page's overall score, or one derived from the generation scores when there is no audit page.
      - `technical_score` number, nullable: From the matching audit page only.
      - `content_score` number, nullable: From the matching audit page only.
      - `content_decay_score` number, nullable: From the matching audit page only.
      - `word_count` integer, nullable: From the matching audit page only.
      - `issue_count` integer, nullable: Issues recorded on the matching audit page; null without one.
      - `source` 'audit' | 'registry' | 'generation', nullable: Dominant provenance of the scores. Null when no source produced any.
    - `search` object, nullable: Search Console metrics for the trailing window, or null when no row matches the post's slug.
      - `clicks` integer
      - `impressions` integer
      - `ctr` number: Average click-through rate over the window, rounded to 3 decimals. 0 when the underlying average is absent.
      - `position` number: Average position over the window, rounded to 1 decimal. 0 means no position was recorded, not rank zero.
      - `window_days` integer: Days of Search Console data aggregated (28).
    - `health_status` 'healthy' | 'needs_attention' | 'critical' | 'no_data': Graded from the overall score plus the search position. `no_data` when there is no computable score and no recorded position.
    - `observed_deliveries` integer: Count of bot-excluded human deliveries recorded for this post.
    - `demand` object, nullable: Answers demand for the visitor-question cluster this post answers. Null when Answers is off, the post has no linked content, or no signal matches.
      - `demand_score` number: Composite demand, rounded to 2 decimals: member_count*0.4 + unique_conversation_count*0.4 + unanswered_rate*100*0.2.
      - `unanswered_rate` number: Share of the cluster's matched questions still unanswered (0..1).
      - `cluster_member_count` integer
      - `unique_conversation_count` integer

## Other responses

- `400`: Validation failed
- `401`: Authentication failed
- `403`: Forbidden - action not allowed on this resource
- `404`: Resource not found
- `429`: Rate limit exceeded
- `500`: Unexpected failure reading performance. The message is sanitized.

---

[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)
