---
title: "Get content gaps"
method: GET
path: "/analytics/gaps"
tags: ["Analytics"]
---

# Get content gaps

`GET /analytics/gaps`

## Request

- Base URL: `https://next.frase.io/api/v1`
- URL: `https://next.frase.io/api/v1/analytics/gaps`
- Auth: one of:
  - API key in header `X-API-KEY`
  - HTTP bearer

Get content gap opportunities. By default the response EXCLUDES dismissed, completed and auto_resolved opportunities; pass ?status= to request them explicitly. The default response also EXCLUDES rows judged off-strategy - no cluster fit and no evidence from a CONFIRMED competitor (competitor URLs belonging to competitors still awaiting your review do not count, and relevance.reason then reads 'a competitor ranks for it, but that competitor is awaiting your review'); pass ?include_off_strategy=true to include them. `competitor_urls` is filtered to confirmed competitors' hosts. Requesting a terminal status (completed, dismissed, auto_resolved) always returns the full set for that status - a strategy filter never hides history. An unrecognized status or type value returns 400 rather than being ignored.

### 2026-08-17 - Competitors are confirmed, not assumed

BREAKING for default result sets: rows whose only competitor evidence belongs to unconfirmed competitors now default-exclude as off-strategy, and `competitor_urls` no longer includes unconfirmed competitors' hosts. Confirm competitors in Brand Hub to restore their evidence.

## Query parameters

- `page` integer
- `page_size` integer
- `site_id` string, uuid
- `status` 'new' | 'reviewed' | 'in_progress' | 'dismissed' | 'completed' | 'auto_resolved'
- `type` 'missing_content' | 'thin_content' | 'outdated_content' | 'competitor_gap'
- `include_off_strategy` boolean

## Response `200`

Content gaps

- object
  - `data` object[]
    - `id` string, uuid
    - `site_id` string, nullable
    - `site_domain` string, nullable
    - `keyword` string
    - `search_volume` integer, nullable
    - `difficulty` number, nullable
    - `opportunity_type` string
    - `impressions` integer, nullable
    - `clicks` integer, nullable
    - `avg_position` number, nullable
    - `page_url` string, nullable
    - `existing_content_id` string, nullable
    - `suggested_brief_id` string, nullable
    - `competitor_urls` string[], nullable
    - `competitor_analysis` object, nullable
    - `status` string
    - `relevance` object, nullable: How far this topic travels. Suggestion rows carry the derived bucket and reason; update rows about your OWN pages are always confident. Makes include_off_strategy and the terminal-status exemption actionable.
      - `bucket` 'confident' | 'uncertain' | 'off_strategy'
      - `reason` string
    - `created_at` string, date-time
    - `updated_at` string, date-time, nullable
  - `pagination` Pagination
    - `page` integer
    - `page_size` integer
    - `total` integer
    - `has_more` boolean
  - `request_id` string

## Other responses

- `400`: Invalid status, type or site_id value

---

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