> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appeeky.com/llms.txt
> Use this file to discover all available pages before exploring further.

# App Keyword Clusters

> Per-cluster strength of one app — which intent groups the app dominates and which it doesn't.

```
GET /v1/apps/:id/keyword-clusters
```

Groups every keyword the app ranks for into intent clusters and returns per-cluster metrics: how many keywords were ranked, the average rank, and a weighted visibility score on the same curve as the [Visibility Score](/docs/keyword-visibility) endpoint. Clusters are refreshed daily using semantic similarity, so two keywords with completely different wording but the same intent (e.g. `"to-do"` and `"task list"`) live in the same group.

***

## Path Parameters

| Name | Type   | Required | Description          |
| ---- | ------ | -------- | -------------------- |
| id   | string | Yes      | Numeric Apple App ID |

## Query Parameters

| Name          | Type   | Required | Default  | Description                                                                                                                                                                           |
| ------------- | ------ | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| country       | string | No       | `us`     | ISO 3166-1 alpha-2 storefront                                                                                                                                                         |
| device        | string | No       | `iphone` | `iphone` or `ipad`                                                                                                                                                                    |
| competitorIds | string | No       | —        | Comma-separated competitor app IDs (max 5). When supplied, the response also includes a `gaps` array — clusters competitors are strong in but the owner has zero ranked keywords for. |

***

## Response

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "data": {
    "appId": "284882215",
    "country": "us",
    "device": "iphone",
    "asOf": "2026-04-21T04:18:33Z",
    "total": 7,
    "clusters": [
      {
        "clusterId": "ai-chat",
        "clusterLabel": "ai chat",
        "keywordsRanked": 14,
        "avgRank": 7.2,
        "weightedVisibility": 432.5,
        "topKeywords": ["ai chat", "claude ai", "ai assistant"],
        "strength": "strong"
      },
      {
        "clusterId": "translation",
        "clusterLabel": "translator",
        "keywordsRanked": 5,
        "avgRank": 28.4,
        "weightedVisibility": 87.1,
        "topKeywords": ["translator", "translate app", "voice translator"],
        "strength": "moderate"
      }
    ],
    "gaps": [
      {
        "clusterId": "voice-typing",
        "clusterLabel": "voice typing",
        "topKeywords": ["voice typing", "speech to text", "dictation"],
        "competitorsPresent": 3,
        "competitorAvgRank": 8.4,
        "yourRank": null,
        "interpretation": "3 competitors ranked in this cluster at avg rank 8.4, you're absent. Worth investigating as a content/keyword gap."
      }
    ]
  },
  "meta": {
    "lastScrapedAt": "2026-04-21T04:18:33Z",
    "dataAgeHours": 8,
    "source": "stored",
    "freshness": "fresh"
  }
}
```

`asOf` is the timestamp of the latest cluster refresh for the country (refreshed daily).

Clusters are returned sorted by `weightedVisibility` desc — the first row is the cluster the app dominates most.

### `strength`

Each cluster row carries a `strength` bucket derived from `weightedVisibility` and `avgRank`:

| Bucket     | Condition                                    |
| ---------- | -------------------------------------------- |
| `strong`   | `weightedVisibility ≥ 10` and `avgRank ≤ 15` |
| `moderate` | `weightedVisibility ≥ 4` and `avgRank ≤ 30`  |
| `weak`     | everything else                              |

Use this for at-a-glance UI badges; the underlying numerical fields stay the source of truth.

### `gaps` (when `competitorIds` is supplied)

For each supplied competitor, the same intent clusters are evaluated and any cluster where competitors are meaningfully present but the requested app has zero ranked keywords is surfaced. Sorted by "biggest threat first" — more competitors present + lower (better) competitor rank ranks higher.

| Field              | Type           | Meaning                                                                               |
| ------------------ | -------------- | ------------------------------------------------------------------------------------- |
| clusterId          | string         | Stable slug for the cluster across all apps in the same country.                      |
| clusterLabel       | string         | Human-readable cluster name.                                                          |
| topKeywords        | string\[]      | Up to 5 representative keywords across competitors in this cluster.                   |
| competitorsPresent | number         | How many of the supplied competitors have at least one ranked keyword in the cluster. |
| competitorAvgRank  | number \| null | Average rank competitors hold across keywords in the cluster (lower = stronger).      |
| yourRank           | null           | Always `null` — gaps are clusters the owner is absent from.                           |
| interpretation     | string         | Plain-English read of why the cluster is a gap.                                       |

When `competitorIds` is omitted, `gaps` is always an empty array — no extra cost is added to the request.

The response-level `meta` envelope describes data freshness — see [Keyword Metrics → meta envelope](/docs/keyword-metrics#meta-envelope-response-level) for the schema.

## Credit Cost

3 credits per request.

## Use Cases

* "Where am I strong, where am I weak?" planning view for ASO teams.
* Detect mis-positioning: if the strongest cluster is unrelated to the app's category, the keyword set needs rethinking.
* Combine with `/keywords/gap` to identify clusters where competitors dominate but you don't yet.
