Skip to main content
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 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

NameTypeRequiredDescription
idstringYesNumeric Apple App ID

Query Parameters

NameTypeRequiredDefaultDescription
countrystringNousISO 3166-1 alpha-2 storefront
devicestringNoiphoneiphone or ipad
competitorIdsstringNoComma-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

{
  "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:
BucketCondition
strongweightedVisibility ≥ 10 and avgRank ≤ 15
moderateweightedVisibility ≥ 4 and avgRank ≤ 30
weakeverything 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.
FieldTypeMeaning
clusterIdstringStable slug for the cluster across all apps in the same country.
clusterLabelstringHuman-readable cluster name.
topKeywordsstring[]Up to 5 representative keywords across competitors in this cluster.
competitorsPresentnumberHow many of the supplied competitors have at least one ranked keyword in the cluster.
competitorAvgRanknumber | nullAverage rank competitors hold across keywords in the cluster (lower = stronger).
yourRanknullAlways null — gaps are clusters the owner is absent from.
interpretationstringPlain-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 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.