# Answers Source: https://docs.appeeky.com/docs/ai-visibility-answers Drill into a single AI assistant response — raw text, parsed app mentions, citations, and sentiment per app ``` GET /v1/ai-visibility/:appId/answers/:answerId ``` Returns the **raw** response from one AI assistant for one prompt, plus the parsed list of apps the assistant mentioned. This is the "show me the receipts" endpoint — useful for: * Verifying *why* a particular intent has the visibility / sentiment score it does. * Debugging an unexpected mention (e.g. why the assistant suggested an unrelated competitor). * Embedding a "view source answer" link in your dashboard so users can see the actual model output. `answerId` is exposed by the [intent drill-down](/docs/ai-visibility-intents) endpoint inside the `prompts[].latestAnswers[]` array. *** ## Path parameters | Name | Type | Required | Description | | -------- | ------------- | -------- | ----------------------------------------------------------------------- | | appId | string | Yes | Apple App ID (numeric) — must match the answer's owner app for security | | answerId | string (UUID) | Yes | Returned by [GET /intents/:intentId](/docs/ai-visibility-intents) | *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "answer": { "id": "a789...", "promptId": "p123...", "intentId": "9c1f...", "modelSlug": "perplexity", "modelId": "sonar", "rawText": "If you have trouble winding down at night, here are five apps to consider:\n1. Calm — guided sleep stories and meditations [1].\n2. Headspace — sleepcasts that ease you into rest [2].\n3. Sleepwell — focuses on adaptive bedtime routines.\n4. Insight Timer — large free library of sleep sounds [3].\n5. Pillow — works with Apple Watch for gentle wake-ups.", "citations": [ { "url": "https://www.calm.com/sleep", "title": "Sleep — Calm" }, { "url": "https://www.headspace.com/sleep", "title": "Sleep with Headspace" }, { "url": "https://insighttimer.com/", "title": "Insight Timer" } ], "fetchedAt": "2026-05-12T08:14:02.000Z", "status": "ok" }, "mentions": [ { "name": "Calm", "position": 1, "trackId": "571800810", "isOwnerApp": false, "sentiment": 0.8 }, { "name": "Headspace", "position": 2, "trackId": "493145008", "isOwnerApp": false, "sentiment": 0.6 }, { "name": "Sleepwell", "position": 3, "trackId": "1234567890", "isOwnerApp": true, "sentiment": 0.4 }, { "name": "Insight Timer", "position": 4, "trackId": "337472899", "isOwnerApp": false, "sentiment": 0.5 }, { "name": "Pillow", "position": 5, "trackId": "878988933", "isOwnerApp": false, "sentiment": 0.3 } ] } } ``` ### `answer` fields | Field | Description | | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | UUID. Same as the `answerId` you requested. | | `promptId` | The prompt this answer was for. Use [GET /intents/:intentId](/docs/ai-visibility-intents) to find the prompt text. | | `intentId` | The intent the prompt belongs to. | | `modelSlug` | One of `chatgpt`, `claude`, `gemini`, `perplexity`. | | `modelId` | Exact model ID used (e.g. `gpt-4.1`, `claude-sonnet-4-20250514`, `gemini-2.5-flash`, `sonar`). | | `rawText` | The assistant's complete answer. Whitespace-preserved. May contain markdown. | | `citations` | Web sources the assistant cited, when the model supports them. Currently only Perplexity returns citations; other models return `[]`. | | `fetchedAt` | When Appeeky fetched the answer (UTC). | | `status` | `ok` (parsed cleanly), `parse_failed` (we kept the raw text but couldn't extract structured mentions), `refusal` (assistant declined to answer), `error` (the model call itself failed; `rawText` will be empty). | ### `mentions` fields One row per app the assistant referred to in the answer, ordered by `position`. | Field | Description | | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | App name as the assistant wrote it. | | `position` | 1-based rank when the assistant gave a list. `null` for free-form prose. | | `trackId` | Canonical App Store ID. `null` when the resolver couldn't confidently match the assistant's spelling. | | `isOwnerApp` | `true` when this mention is your tracked app. | | `sentiment` | −1 (negative) to +1 (positive), reflecting the assistant's framing in the surrounding sentence. `null` when the answer was a neutral list with no per-app commentary. | *** ## Citation handling For models with web search (currently Perplexity), the assistant often inserts inline citation markers like `[1]`, `[2]`, `[3]` — those numbers map **1-indexed** into the `answer.citations` array. If you want to match a mention back to a citation: when we extract mentions, the second-pass parser also returns a `citationIndex` on each app. That value is stored in the underlying `ai_visibility_app_mentions` table; it isn't currently exposed on this endpoint, but you can match the `[N]` markers in `rawText` to `answer.citations[N-1]` yourself for now. *** ## Code examples ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/ai-visibility/1234567890/answers/a789..." \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/ai-visibility/1234567890/answers/a789...", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { answer, mentions } = (await res.json()).data; console.log(`[${answer.modelSlug}] ${answer.fetchedAt}`); console.log("\nRaw answer:\n" + answer.rawText); console.log("\nApps mentioned:"); mentions.forEach((m) => { const star = m.isOwnerApp ? " ★" : ""; console.log(` ${m.position ?? "·"}. ${m.name}${star} sentiment=${m.sentiment ?? "n/a"}`); }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests r = requests.get( "https://api.appeeky.com/v1/ai-visibility/1234567890/answers/a789...", headers={"X-API-Key": "YOUR_API_KEY"}, ) data = r.json()["data"] print(data["answer"]["rawText"]) print() for m in data["mentions"]: star = " ★" if m["isOwnerApp"] else "" print(f" {m['position']}. {m['name']}{star} sentiment={m['sentiment']}") ``` *** ## Pattern: walk every answer for an intent ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} const HEADERS = { "X-API-Key": "YOUR_API_KEY" }; // 1. Get the intent drill-down const intent = ( await fetch( "https://api.appeeky.com/v1/ai-visibility/1234567890/intents/9c1f...?model=chatgpt", { headers: HEADERS } ).then((r) => r.json()) ).data; // 2. For each prompt's latest answer, fetch the raw text for (const prompt of intent.prompts) { for (const a of prompt.latestAnswers) { const detail = await fetch( `https://api.appeeky.com/v1/ai-visibility/1234567890/answers/${a.answerId}`, { headers: HEADERS } ).then((r) => r.json()); console.log(`\n## Prompt: ${prompt.text}`); console.log(`### Model: ${detail.data.answer.modelSlug}`); console.log(detail.data.answer.rawText); } } ``` *** ## Credits * **1 credit** per call. *** ## Errors | Status | Code | When | | ------ | ---------- | -------------------------------------------------- | | 404 | NOT\_FOUND | Answer doesn't exist or doesn't belong to this app | | 401 | — | Missing or invalid API key / JWT | | 429 | — | Insufficient monthly credits | *** ## See also * [Intents endpoint](/docs/ai-visibility-intents) — source of `answerId`s in `prompts[].latestAnswers[]` * [Prompts](/docs/ai-visibility-prompts) — see/manage the prompt that produced this answer * [Overview](/docs/ai-visibility-overview) — concept page # Bootstrap Source: https://docs.appeeky.com/docs/ai-visibility-bootstrap Generate the initial intents and prompts for an app and queue the first scan ``` POST /v1/ai-visibility/:appId/bootstrap ``` Bootstrap is the **one-time setup call** that turns AI Visibility on for an app. It runs three things in sequence: 1. **Reads your app metadata** (title, subtitle, description, primary genre) and a sample of the keywords you already rank for. 2. **Generates intents** — 8–12 user goals your app addresses, written in the language a real user would use when asking an AI assistant. 3. **Generates prompts per intent** — 5–8 user-style queries spread across different styles (problem-led, branded, comparison, …). 4. **Queues the first scan** automatically so fresh data appears within \~10 minutes. After bootstrap finishes, the app is on autopilot — daily scheduled scans take over. > Bootstrap is **idempotent**. Re-running it later (after editing your app's title, for example) only adds *new* intents the LLM proposes — it never deletes intents you've edited or added manually. *** ## Path parameters | Name | Type | Required | Description | | ----- | ------ | -------- | ---------------------- | | appId | string | Yes | Apple App ID (numeric) | ## Body ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "country": "us", "language": "en" } ``` | Field | Type | Default | Description | | -------- | ------ | ------- | --------------------------------------------------------------------------- | | country | string | `us` | ISO country code — biases the LLM toward apps available in that storefront. | | language | string | `en` | ISO 639-1 language code — used as a hint for prompt phrasing. | *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "taskRunId": "run_a1b2c3d4..." } } ``` `taskRunId` is the durable Trigger.dev run id; you can ignore it for normal use. The endpoint returns immediately (HTTP 201) and the work continues in the background. *** ## What happens after the call The background task takes about **3–8 minutes** end-to-end: | Phase | Typical duration | | ---------------------------------------------------- | ------------------------------------------------------------------------------- | | Pull app metadata + keyword sample | \< 5s | | Generate intents (LLM) | 5–15s | | Generate prompts for each new intent (LLM, parallel) | 30–60s | | Insert rows | \< 5s | | **Trigger first scan** (`scan-app-ai-visibility`) | runs immediately after, takes another 3–6 min for the answers + parsing to land | You can verify progress by polling the read endpoints: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} # 1. Confirm intents were generated curl "https://api.appeeky.com/v1/ai-visibility/1234567890/intents?country=us" \ -H "X-API-Key: YOUR_API_KEY" # 2. After a few minutes, the overview gauge will populate curl "https://api.appeeky.com/v1/ai-visibility/1234567890/overview?country=us&model=chatgpt" \ -H "X-API-Key: YOUR_API_KEY" ``` *** ## When to re-run bootstrap | Situation | Action | | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------- | | You changed the app's title or subtitle and want the LLM to reconsider intents | Re-run bootstrap | | You launched a major new feature and the LLM-generated intents are out of date | Re-run bootstrap | | You just want to add a few more intents | Use [POST /intents](/docs/ai-visibility-intents) instead — much cheaper | | You want to scan with the same intents but fresh AI answers | Use [POST /scan](/docs/ai-visibility-scan-settings) instead | Re-running bootstrap doesn't disturb the prompts or answers you've already collected; it only inserts intents/prompts that don't already exist. *** ## Code examples ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/ai-visibility/1234567890/bootstrap" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "country": "us", "language": "en" }' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/ai-visibility/1234567890/bootstrap", { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ country: "us", language: "en" }), } ); const { data } = await res.json(); console.log("Bootstrap queued:", data.taskRunId); // Poll the intents endpoint after ~30s setTimeout(async () => { const r = await fetch( "https://api.appeeky.com/v1/ai-visibility/1234567890/intents?country=us", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); console.log(await r.json()); }, 30_000); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests r = requests.post( "https://api.appeeky.com/v1/ai-visibility/1234567890/bootstrap", headers={"X-API-Key": "YOUR_API_KEY"}, json={"country": "us", "language": "en"}, ) print("Bootstrap queued:", r.json()["data"]["taskRunId"]) ``` *** ## Credits Bootstrap charges against your **dedicated AI Visibility credit bucket** — separate from your general API quota. * **The bootstrap call itself is free** (intent + prompt generation runs on Appeeky's account, not yours). * The **initial scan** that runs immediately afterwards is metered like any other scheduled scan — see the [Scan & Settings cost table](/docs/ai-visibility-scan-settings#cost-controls). For an 8-intent × 6-prompt × 1-model (`chatgpt`) setup that's \~432 credits up front. Two safeguards before the call ever runs: * **Tier gate**: bootstrap requires the `Indie` plan or higher. Free-plan calls return `403 TIER_LIMIT_EXCEEDED`. * **Quota gate**: when the AI Visibility bucket is empty, bootstrap returns `429 AI_VISIBILITY_QUOTA_EXCEEDED` instead of queuing intents+prompts the user can't measure. Bootstrap is also rate-limited per app — calling it twice in the same hour is allowed but the second call usually returns the same intents (deduplicated by fingerprint) so you waste credits. *** ## Errors | Status | Code | When | | ------ | ------------------------------- | --------------------------------------------------------------------- | | 400 | INVALID\_APP\_ID | Missing or non-numeric app ID | | 403 | TIER\_LIMIT\_EXCEEDED | Caller is on the `free` plan — upgrade to `Indie` or higher | | 404 | APP\_NOT\_FOUND | App is not available in the requested country | | 401 | — | Missing or invalid API key / JWT | | 429 | AI\_VISIBILITY\_QUOTA\_EXCEEDED | Out of AI Visibility credits — upgrade or wait for the quota to reset | *** ## See also * [Overview](/docs/ai-visibility-overview) — what AI Visibility is * [Overview endpoint](/docs/ai-visibility-overview-endpoint) — read the gauge after bootstrap completes * [Intents](/docs/ai-visibility-intents) — view, edit, or add intents the LLM generated * [Scan & Settings](/docs/ai-visibility-scan-settings) — control the daily cadence # Competitors Source: https://docs.appeeky.com/docs/ai-visibility-competitors Discover the apps AI assistants recommend instead of (or alongside) yours ``` GET /v1/ai-visibility/:appId/competitors ``` Across all your tracked prompts, this endpoint returns the apps that ChatGPT, Gemini, Claude, or Perplexity surfaces — ranked by how often they appear. It's the "you should know about these competitors" list: every app the assistants think a user with your tracked intents might pick instead of yours. Use it to: * Identify competitors you didn't realise the AI consistently mentions in your space. * Quantify how often a known competitor is recommended versus you. * Find ASO benchmarking targets — the apps with the highest visibility in *your* prompts are the ones to study. *** ## Path parameters | Name | Type | Required | Description | | ----- | ------ | -------- | ---------------------- | | appId | string | Yes | Apple App ID (numeric) | ## Query parameters | Name | Type | Default | Description | | ---------- | ------ | --------- | -------------------------------------------------- | | country | string | `us` | ISO country code | | model | string | `chatgpt` | One of `chatgpt`, `claude`, `gemini`, `perplexity` | | windowDays | int | `14` | Lookback window for the rollup. Min 1, max 90. | *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": [ { "trackId": "571800810", "name": "Calm", "iconUrl": null, "appearancesTotal": 38, "intentsCovered": 7, "positionAvg": 1.4, "visibilityPct": 79.2 }, { "trackId": "493145008", "name": "Headspace", "iconUrl": null, "appearancesTotal": 31, "intentsCovered": 6, "positionAvg": 2.1, "visibilityPct": 64.6 }, { "trackId": "337472899", "name": "Insight Timer", "iconUrl": null, "appearancesTotal": 18, "intentsCovered": 4, "positionAvg": 3.6, "visibilityPct": 37.5 } ] } ``` Returned sorted by `appearancesTotal` desc. Up to 20 rows. | Field | Description | | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `trackId` | Canonical App Store ID. `null` if AI Visibility couldn't confidently match the assistant's spelling against the App Store (rare, usually web-only products). | | `name` | The app name as the assistant most recently spelled it. | | `iconUrl` | Reserved — currently `null`. Will be populated in a future release; use [GET /apps/:id](/docs/get-app) with `trackId` to fetch the icon today. | | `appearancesTotal` | Total number of times this app appeared across every (prompt × model × day) within the window. One assistant mentioning it in 3 prompts on 5 days counts as 15. | | `intentsCovered` | Number of distinct intents (out of your tracked set) where this app appeared at least once. A high number means a broad competitor; a low number means a niche one. | | `positionAvg` | Average rank in the assistants' lists when this app was mentioned. Lower is better. `null` if the assistants only mentioned the app in non-list prose. | | `visibilityPct` | % of distinct prompts (in the window) where this app appeared. The closest single number to your own [overview](/docs/ai-visibility-overview-endpoint) `visibilityScore` for direct comparison. | *** ## Reading the table The most useful comparison is your own `visibilityPct` (from `GET /overview`) versus the competitors here: ``` Your AI Visibility Score: 38 ← from /overview ───────────────────────────────────── Calm visibilityPct: 79.2 ← appears in 79% of your prompts Headspace visibilityPct: 64.6 Insight Timer visibilityPct: 37.5 ← about even with you ``` Reading the row for **Insight Timer**: it appears in roughly the same share of your tracked prompts as your own app, but at average position 3.6 (vs. probably 1–2 for Calm). This means Insight Timer is a comparably-known alternative AI surfaces *with* you, not above you. A row where `intentsCovered` is high and `positionAvg` is low is a competitor that's actively winning the same buyer journeys you target. That's where your ASO effort and any AI-assistant-targeted content (review-rich blogs, listicles in major publications) will move the needle the most. *** ## Code examples ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} # 14-day window, ChatGPT curl "https://api.appeeky.com/v1/ai-visibility/1234567890/competitors?country=us&model=chatgpt" \ -H "X-API-Key: YOUR_API_KEY" # 30-day window, Perplexity curl "https://api.appeeky.com/v1/ai-visibility/1234567890/competitors?country=us&model=perplexity&windowDays=30" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} // Compare AI competitor sets across all 4 models const models = ["chatgpt", "claude", "gemini", "perplexity"]; const all = await Promise.all( models.map((m) => fetch( `https://api.appeeky.com/v1/ai-visibility/1234567890/competitors?model=${m}&windowDays=14`, { headers: { "X-API-Key": "YOUR_API_KEY" } } ).then((r) => r.json()) ) ); models.forEach((m, i) => { console.log(`\n=== ${m} ===`); all[i].data.slice(0, 5).forEach((c, idx) => { console.log(`${idx + 1}. ${c.name.padEnd(20)} visibility=${c.visibilityPct}% pos=${c.positionAvg}`); }); }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests r = requests.get( "https://api.appeeky.com/v1/ai-visibility/1234567890/competitors", params={"country": "us", "model": "chatgpt", "windowDays": 30}, headers={"X-API-Key": "YOUR_API_KEY"}, ) for c in r.json()["data"][:10]: print(f"{c['name']:<24} vis={c['visibilityPct']}% intents={c['intentsCovered']}") ``` *** ## Pattern: enrich with App Store metadata The endpoint returns `trackId` for resolved competitors. To get icons, ratings, and developer info for the dashboard: ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} const competitors = ( await fetch( "https://api.appeeky.com/v1/ai-visibility/1234567890/competitors?model=chatgpt", { headers: { "X-API-Key": "YOUR_API_KEY" } } ).then((r) => r.json()) ).data; const enriched = await Promise.all( competitors .filter((c) => c.trackId) .slice(0, 5) .map(async (c) => { const meta = await fetch( `https://api.appeeky.com/v1/apps/${c.trackId}`, { headers: { "X-API-Key": "YOUR_API_KEY" } } ).then((r) => r.json()); return { ...c, iconUrl: meta.data.iconUrl, rating: meta.data.averageUserRating }; }) ); ``` Or use the dedicated [Similar Apps](/docs/get-similar-apps) endpoint if you want App Store algorithm-derived competitors instead of AI-derived ones — the two sets are often different in interesting ways. *** ## Credits * **2 credits** per call. *** ## Errors | Status | Code | When | | ------ | ---------------- | -------------------------------- | | 400 | INVALID\_APP\_ID | Missing or non-numeric app ID | | 401 | — | Missing or invalid API key / JWT | | 429 | — | Insufficient monthly credits | *** ## See also * [Overview endpoint](/docs/ai-visibility-overview-endpoint) — your own visibility, comparable side-by-side * [Intents](/docs/ai-visibility-intents) — per-intent `topApps` breakdown drives this benchmark * [Get app](/docs/get-app) — fetch icons + ratings for the resolved `trackId`s * [Similar Apps](/docs/get-similar-apps) — App Store algorithm's view of "similar" apps # Intents Source: https://docs.appeeky.com/docs/ai-visibility-intents List, drill into, and manage the user intents AI Visibility tracks for your app An **intent** is a user goal that an AI assistant might be asked to recommend an app for. Each intent powers one row in the dashboard table — visibility %, sentiment, average position, top apps shown for it. Intents are generated for you when you call [bootstrap](/docs/ai-visibility-bootstrap), but you can also add, rename, pause, or archive them at any time. Adding your own intents (e.g. *"Help me journal in Turkish"*) is the way to expand AI Visibility coverage beyond what the LLM proposed. This page covers all intent endpoints: | Endpoint | Purpose | | --------------------------------------------------- | ---------------------------------------------- | | `GET /v1/ai-visibility/:appId/intents` | List with metrics — drives the dashboard table | | `GET /v1/ai-visibility/:appId/intents/:intentId` | Drill-down: prompts + latest AI answers | | `POST /v1/ai-visibility/:appId/intents` | Manually add an intent | | `PATCH /v1/ai-visibility/:appId/intents/:intentId` | Rename / pause / archive | | `DELETE /v1/ai-visibility/:appId/intents/:intentId` | Archive (soft delete) | *** ## List intents with metrics ``` GET /v1/ai-visibility/:appId/intents ``` The data shown in the **Intent Performance** table on the dashboard. ### Query parameters | Name | Type | Default | Description | | ------- | ------ | --------- | --------------------------------------------------------------------------------- | | country | string | `us` | ISO country code | | model | string | `chatgpt` | One of `chatgpt`, `claude`, `gemini`, `perplexity` — metrics are scored per model | ### Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": [ { "intentId": "9c1f...", "label": "Build a regular meditation practice", "description": "User wants an app that helps them meditate consistently every day.", "status": "active", "source": "llm", "visibilityPct": 78, "sentimentPct": 71, "positionAvg": 2, "promptsTotal": 6, "topApps": [ { "trackId": "571800810", "name": "Calm", "isOwnerApp": false }, { "trackId": "493145008", "name": "Headspace", "isOwnerApp": false }, { "trackId": "1234567890","name": "Sleepwell", "isOwnerApp": true } ] }, { "intentId": "a7b2...", "label": "Fall asleep faster and unwind", "description": "...", "status": "active", "source": "user", "visibilityPct": 75, "sentimentPct": 84, "positionAvg": 2, "promptsTotal": 6, "topApps": [ /* ... */ ] } ] } ``` | Field | Description | | --------------- | -------------------------------------------------------------------------------------------------------------------- | | `intentId` | UUID — use this for drill-down or update calls. | | `label` | Display text, sentence-case. | | `description` | One-sentence explanation of the user goal. May be `null` for legacy or manually-added intents. | | `status` | `active` (scanned daily), `paused` (kept around but not scanned), `archived` (hidden from list endpoints). | | `source` | `llm` (generated by the bootstrap), `user` (you added it manually). | | `visibilityPct` | % of this intent's prompts where your app appeared, position-weighted. | | `sentimentPct` | Average tone of the assistant when it mentioned your app, on a 0–100% scale. `null` if you weren't mentioned at all. | | `positionAvg` | Average rank in the assistant's list when your app was mentioned. `null` if you weren't mentioned. | | `promptsTotal` | Number of active prompts under this intent (those used in the most recent scan). | | `topApps` | Up to 3 apps most frequently surfaced for this intent in the latest scan. `isOwnerApp: true` marks your own app. | *** ## Drill into one intent ``` GET /v1/ai-visibility/:appId/intents/:intentId ``` Returns the same fields as the row above **plus** the prompts under that intent and the most recent AI answers for each prompt. ### Query parameters | Name | Type | Default | Description | | ------- | ------ | --------- | ---------------------------------------- | | country | string | `us` | ISO country code | | model | string | `chatgpt` | Filters the latest answers to this model | ### Response (truncated) ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "intentId": "9c1f...", "label": "Reduce stress and anxiety to feel calm", "description": "...", "status": "active", "source": "llm", "visibilityPct": 73, "sentimentPct": 71, "positionAvg": 1, "promptsTotal": 6, "topApps": [ /* ... */ ], "prompts": [ { "promptId": "p123...", "text": "Which app offers quick calming exercises for panic or high stress?", "style": "problem", "status": "active", "source": "llm", "latestAnswers": [ { "answerId": "a789...", "modelSlug": "chatgpt", "fetchedAt": "2026-05-12T08:14:02.000Z", "apps": [ { "position": 1, "name": "Calm", "trackId": "571800810", "isOwnerApp": false }, { "position": 2, "name": "Headspace", "trackId": "493145008", "isOwnerApp": false }, { "position": 3, "name": "Sleepwell", "trackId": "1234567890", "isOwnerApp": true } ] } ] } ] } } ``` The `prompts[].latestAnswers[].apps` list is ordered by position. `apps[].trackId` is `null` when the assistant mentioned an app we couldn't confidently match against the App Store (very rare brands or a misspelling). For the **raw model output and parsed sentiment per app**, use [GET /answers/:answerId](/docs/ai-visibility-answers) with the `answerId` returned here. *** ## Add an intent manually ``` POST /v1/ai-visibility/:appId/intents ``` Useful when: * The LLM bootstrap missed something (e.g. a niche use case). * You're launching a new feature and want to start tracking visibility for it before the first scan. * You want to track a specific competitor angle (*"Apps similar to Calm"*). After insertion, generate prompts for it via [POST /intents/:intentId/prompts](/docs/ai-visibility-prompts) — until at least one active prompt exists, the intent won't be scanned. ### Body ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "label": "Help me journal in Turkish", "description": "Turkish-speaking user wants an app to keep a daily journal." } ``` | Field | Type | Required | Description | | ------------- | ------ | -------- | ------------------------------------- | | `label` | string | Yes | Sentence-case user goal, 8–80 chars | | `description` | string | No | One-sentence explanation, ≤ 200 chars | ### Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "id": "f8a7...", "label": "Help me journal in Turkish", "description": "Turkish-speaking user wants an app to keep a daily journal.", "status": "active", "source": "user", "fingerprint": "ab12cd34..." } } ``` `fingerprint` is a content hash. Re-posting the same label is safe — it returns the existing intent rather than creating a duplicate. *** ## Update / pause / archive ``` PATCH /v1/ai-visibility/:appId/intents/:intentId ``` ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "label": "Build a daily meditation habit", "description": "Optional new description.", "status": "active" } ``` All three fields are optional. Use `status: "paused"` to keep the intent and its prompts visible but stop sending them to the AI models in the next scan. Use `status: "archived"` (or DELETE) to hide it everywhere. *** ## Delete (archive) ``` DELETE /v1/ai-visibility/:appId/intents/:intentId ``` Soft-archive — the intent and its prompts/answers remain in the database but won't show up in list endpoints or be sent in future scans. Equivalent to `PATCH { status: "archived" }`. *** ## Code examples ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} # Dashboard table curl "https://api.appeeky.com/v1/ai-visibility/1234567890/intents?model=chatgpt" \ -H "X-API-Key: YOUR_API_KEY" # Drill into one intent curl "https://api.appeeky.com/v1/ai-visibility/1234567890/intents/9c1f...?model=chatgpt" \ -H "X-API-Key: YOUR_API_KEY" # Manually add an intent curl -X POST "https://api.appeeky.com/v1/ai-visibility/1234567890/intents" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "label": "Help me journal in Turkish" }' # Pause an intent curl -X PATCH "https://api.appeeky.com/v1/ai-visibility/1234567890/intents/9c1f..." \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "status": "paused" }' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const HEADERS = { "X-API-Key": "YOUR_API_KEY" }; // Render the intent table const intents = await fetch( "https://api.appeeky.com/v1/ai-visibility/1234567890/intents?model=chatgpt", { headers: HEADERS } ).then((r) => r.json()); intents.data.forEach((row) => { console.log(`${row.label} visibility=${row.visibilityPct}% sentiment=${row.sentimentPct}%`); }); // Add a custom intent const created = await fetch( "https://api.appeeky.com/v1/ai-visibility/1234567890/intents", { method: "POST", headers: { ...HEADERS, "Content-Type": "application/json" }, body: JSON.stringify({ label: "Help me journal in Turkish", description: "Turkish-speaking user wants an app to keep a daily journal.", }), } ).then((r) => r.json()); console.log("New intent:", created.data.id); ``` *** ## Credits | Endpoint | Cost | | --------------------------- | --------- | | `GET /intents` | 2 credits | | `GET /intents/:intentId` | 2 credits | | `POST /intents` | 1 credit | | `PATCH /intents/:intentId` | Free | | `DELETE /intents/:intentId` | Free | *** ## Errors | Status | Code | When | | ------ | -------------- | ------------------------------------------------------------------------------ | | 400 | INVALID\_INPUT | `label` shorter than 8 characters or `status` is not one of the allowed values | | 404 | NOT\_FOUND | Intent doesn't exist or doesn't belong to this app | | 401 | — | Missing or invalid API key / JWT | | 429 | — | Insufficient monthly credits | *** ## See also * [Prompts](/docs/ai-visibility-prompts) — manage the queries each intent sends to the assistants * [Answers](/docs/ai-visibility-answers) — drill into a single AI response * [Bootstrap](/docs/ai-visibility-bootstrap) — initial intent generation # Overview Source: https://docs.appeeky.com/docs/ai-visibility-overview Measure how often your app is recommended by ChatGPT, Gemini, Claude, and Perplexity — and benchmark against the apps that show up in your place ChatGPT, Gemini, Claude, and Perplexity are increasingly the first place users go when they ask "which app should I use to…". **AI Visibility** measures how often your app appears in those recommendations, ranks the user *intents* you serve well (and the ones you miss), and shows you the competitor apps that AI assistants suggest in your place. It is the LLM-era counterpart of keyword rank tracking. Where the App Store's search algorithm answers "which apps appear for the query *‘meditation app’*", AI assistants answer "which apps does ChatGPT recommend when a user asks *‘I can't sleep, what app can help?’*". This API tracks the second. *** ## What you get * **AI Visibility Score (0–100)** per AI model — a single composite gauge you can put on a dashboard. * **Per-intent visibility, sentiment, and average position** — tells you *which* user goals your app is showing up for and *how favorably* the assistant talks about it. * **Latest AI answers** — the actual model responses, with a parsed list of the apps mentioned and at which rank. * **Competitor benchmark** — the top apps the assistants recommend across your tracked prompts, ranked by how often they appear instead of (or alongside) you. * **Daily history** — every metric is rolled up daily so you can chart trends and link changes to your ASO work or release cadence. The same data powers both the dashboard widgets and any custom reporting you build on top of the API. *** ## How it works ``` Your App │ ▼ Bootstrap (one-time) │ LLM reads your app metadata + tracked keywords │ → generates 8–12 user intents your app addresses │ → generates 5–8 user-style prompts per intent ▼ Daily Scan │ For every (prompt × enabled AI model): │ 1. Send the prompt to the AI assistant │ 2. Parse the answer with a 2nd-pass LLM │ → which apps were mentioned, in which order, with what tone │ 3. Resolve each mentioned app to a canonical App Store entry ▼ Aggregate │ Roll up mentions into: │ • per-intent visibility, sentiment, position │ • app-level AI Visibility Score │ • competitor benchmark ▼ Read API + Dashboard ``` You only ever interact with the high-level endpoints. The pipeline runs durably in the background, so you can close the tab and come back to fresh data tomorrow. *** ## Models covered | Model slug | Provider | Notes | | ------------ | ---------- | ---------------------------------------------------------- | | `chatgpt` | OpenAI | Default. Reflects what a casual ChatGPT user gets back. | | `claude` | Anthropic | Recommendations Claude gives without web search. | | `gemini` | Google | Recommendations Gemini gives without web search. | | `perplexity` | Perplexity | Web-grounded. Citations are returned alongside the answer. | Pick which models to scan in **[Settings](/docs/ai-visibility-scan-settings)**. You can run all four side-by-side and compare scores per model — different assistants often recommend different apps for the same intent. *** ## Concepts | Term | Meaning | | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Intent** | A user goal your app addresses (e.g. *“Fall asleep faster and unwind”*). One row in the dashboard table. Generated by an LLM from your app metadata, but you can edit, pause, or add your own. | | **Prompt** | A specific user-style query bound to an intent (*“I struggle to fall asleep at night, what app can help me wind down?”*). Each intent has 5–8 prompts mixing different styles (problem-led, branded, comparison, …). | | **Answer** | A single response from one AI model to one prompt. Stored verbatim with citations and a parsed list of apps the model mentioned. | | **Mention** | One app named by the assistant inside an answer, with its position in the list and the sentiment of the surrounding sentence. | | **AI Visibility Score** | 0–100 composite per (model, app, day). 70% weighted by visibility (% of prompts where you appear, position-weighted), 30% by sentiment when mentioned. | | **Sentiment** | −1 (assistant warns against your app) to +1 (assistant strongly recommends it), surfaced as a 0–100% score in the UI. | *** ## Getting started 1. **Track the app** you want to measure with `POST /v1/user/apps` — see [My Apps](/docs/my-apps). 2. **Bootstrap** AI Visibility for that app — see [Bootstrap](/docs/ai-visibility-bootstrap). This generates intents + prompts and runs the first scan automatically. 3. **Read the dashboard data** with [GET /overview](/docs/ai-visibility-overview-endpoint), [GET /intents](/docs/ai-visibility-intents), and [GET /competitors](/docs/ai-visibility-competitors). After the initial bootstrap the agent runs **daily** by default. You don't need to call anything else — fresh data appears on tomorrow's `overview` automatically. *** ## Endpoint map | Endpoint | Purpose | | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | `POST /ai-visibility/:appId/bootstrap` | Generate intents + prompts and run the first scan. Run once per app, after that the system is on autopilot. | | `GET /ai-visibility/:appId/overview` | The big gauge: AI Visibility Score, sentiment, intents covered. | | `GET /ai-visibility/:appId/intents` | Per-intent table — visibility %, sentiment, position, top apps shown alongside you. | | `GET /ai-visibility/:appId/intents/:intentId` | Drill-down into one intent: its prompts and the latest AI answers. | | `GET /ai-visibility/:appId/competitors` | Apps the assistants recommend instead of (or with) you, ranked by appearance frequency. | | `GET /ai-visibility/:appId/answers/:answerId` | Raw model output + parsed app list for one answer. | | `POST /ai-visibility/:appId/scan` | On-demand re-scan — useful right after a metadata change. | | `GET / PUT /ai-visibility/:appId/settings` | Which models to scan, scan cadence (daily / weekly / off). | | `POST / PATCH / DELETE /ai-visibility/:appId/intents[/…]` | Manually add, edit, or pause intents. | | `POST / PATCH / DELETE /ai-visibility/:appId/{intents/:id/prompts \| prompts/:id}` | Manually add or edit prompts. | Authentication: standard Appeeky auth applies — JWT (web) or `X-API-Key` header. See [Authentication](/docs/authentication). *** ## Pricing AI Visibility uses a **dedicated credit pool** — separate from your general API quota. This way a heavy AI Visibility user never accidentally drains the credits they need for the rest of the API (App Store metadata, ASO, keyword ranks, …). You'll see both buckets on every authenticated response: ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} X-RateLimit-Limit: 100000 # general API credits/month X-RateLimit-Remaining: 97432 X-AiVisibility-Limit: 50000 # AI Visibility credits/month X-AiVisibility-Remaining: 41088 # set when the request hit an /ai-visibility/* route ``` The same numbers are returned in JSON on `GET /v1/auth/me` under the `credits` object. ### Per-call charges (the bulk of your usage) Every assistant call — whether part of a scheduled scan or a manual `/scan` — deducts credits from the **AI Visibility bucket** based on which model was used: | Model | Credits per (prompt × model) call | | ------------ | --------------------------------- | | `chatgpt` | 9 | | `claude` | 15 | | `gemini` | 5 | | `perplexity` | 3 | A typical setup of **8 intents × 6 prompts** = 48 prompts. One scan of `chatgpt`-only therefore costs `48 × 9 = 432 credits`. At the **default 3-day cadence** that's roughly `432 × 10 = 4,320 credits/month` per app. ### Flat charges (one-off endpoints) | Action | Credits | Bucket | | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------- | | Reading any AI Visibility endpoint (`overview`, `intents`, `competitors`, …) | 1 credit per call | AI Visibility | | `POST /bootstrap` | Free metering — auto-triggered initial scan is metered as a normal scan | AI Visibility | | Manual `POST /scan` | `Σ(model credit cost) × prompts_count` — same formula as scheduled scans, returned in `X-Credit-Cost` | AI Visibility | | Editing intents / prompts / settings | Free | — | ### Tier limits AI Visibility is **available on every paid plan**. The credit bucket and the number of models you can enable scale with your plan: | Plan | AI Visibility credits / month | Max models per app | Max apps | | ---------- | ----------------------------: | ------------------------: | --------: | | Free | — (not available) | — | — | | Indie | 3,000 | 1 (`chatgpt` recommended) | 1 | | Starter | 8,000 | 2 | 2 | | Growth | 20,000 | 4 (all models) | 5 | | Pro | 50,000 | 4 (all models) | 15 | | Enterprise | 150,000 | 4 (all models) | unlimited | ### What happens when the bucket runs low | Trigger | Behavior | | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Manual scan** (`POST /scan`) | **Hard fail** — `429 AI_VISIBILITY_QUOTA_EXCEEDED` with the cost the request would have charged and your remaining balance. No data is read or written. | | **Bootstrap** (`POST /bootstrap`) | Hard fail when the bucket is empty — bootstrap kicks off an initial scan and we'd rather block it upfront than create intents+prompts you can't measure. | | **Scheduled scan** (cron) | **Graceful degrade** — the scheduler drops models in cost-descending order (`claude → gemini → perplexity`, keeping `chatgpt` as the most user-valuable fallback) until the scan fits the remaining budget. The dropped models are recorded on the run row (`models_dropped`, `degraded_reason='insufficient_credits'`) so the dashboard can show "scanned with fewer models due to low credits". When even `chatgpt`-only doesn't fit, the scan is skipped and `next_scan_at` pushed out 24h. | ### Estimating your monthly cost `GET /v1/ai-visibility/:appId/settings` returns a forecast using your current cadence and enabled models: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "aiVisibilityCredits": { "limit": 50000, "used": 8912, "remaining": 41088, "estimatedMonthlyCost": 14400, "resetDate": "2026-06-12T00:00:00.000Z" } } ``` The `X-AiVisibility-*` headers and `X-Credit-Cost` are authoritative for what was actually deducted. See [Rate Limits](/docs/rate-limits) for the credit ceilings of each plan. *** ## See also * [Bootstrap](/docs/ai-visibility-bootstrap) — set up an app for AI Visibility tracking * [Overview endpoint](/docs/ai-visibility-overview-endpoint) — the gauge data * [Intents](/docs/ai-visibility-intents) — per-intent table + drill-down * [Prompts](/docs/ai-visibility-prompts) — manage the prompts the agent sends * [Competitors](/docs/ai-visibility-competitors) — who AI recommends in your place * [Scan & Settings](/docs/ai-visibility-scan-settings) — re-scan on demand and configure cadence * [Answers](/docs/ai-visibility-answers) — drill into a single AI response # Overview Endpoint Source: https://docs.appeeky.com/docs/ai-visibility-overview-endpoint AI Visibility Score, sentiment, and coverage — the data behind the dashboard gauge ``` GET /v1/ai-visibility/:appId/overview ``` Returns the **headline numbers** for AI Visibility — the AI Visibility Score gauge, the sentiment percentage, and how many of your tracked intents you actually showed up in. One row per `(app, country, model)` for the most recent scan, plus the previous-period values for trend arrows. Use this endpoint to populate the top of a dashboard. For per-intent breakdown, see [GET /intents](/docs/ai-visibility-intents). *** ## Path parameters | Name | Type | Required | Description | | ----- | ------ | -------- | ---------------------- | | appId | string | Yes | Apple App ID (numeric) | ## Query parameters | Name | Type | Default | Description | | ------- | ------ | --------- | --------------------------------------------------------------------------------------- | | country | string | `us` | ISO country code | | model | string | `chatgpt` | One of `chatgpt`, `claude`, `gemini`, `perplexity`. Each model is scored independently. | *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "1234567890", "country": "us", "modelSlug": "chatgpt", "scoredAt": "2026-05-12", "visibilityScore": { "score": 92, "label": "excellent" }, "sentimentPct": 75, "intentsTotal": 8, "intentsCovered": 7, "trend": { "visibilityScorePrev": 88, "sentimentPctPrev": 71 } } } ``` | Field | Description | | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `scoredAt` | UTC date (`YYYY-MM-DD`) of the most recent scan rolled up. `null` if the app has never been scanned for this `(country, model)`. | | `visibilityScore.score` | **0–100 composite**. Weighted 70% by visibility (% of tracked prompts where your app appeared, position-weighted), 30% by sentiment when mentioned. | | `visibilityScore.label` | One of `excellent` (≥80), `good` (≥60), `fair` (≥40), `poor` (less than 40). | | `sentimentPct` | Average sentiment of the assistant when it talked about your app, normalised to a 0–100% scale. `null` if your app wasn't mentioned. | | `intentsTotal` | Number of active intents tracked for this app. | | `intentsCovered` | Number of those intents in which your app appeared **at least once** in the most recent scan. | | `trend.visibilityScorePrev` | The same `visibilityScore.score` from the previous scan day. `null` if you don't have prior data yet. | | `trend.sentimentPctPrev` | Same idea for sentiment. | When the app has been bootstrapped but not yet scanned, the response is the same shape but `scoredAt: null`, `score: 0`, `label: "poor"`, `sentimentPct: null`, `intentsCovered: 0`. *** ## Reading the score The score is a **single composite gauge**. If you want to interpret it: * **80–100 (excellent)**: AI assistants reliably recommend your app for the intents you care about, near the top of their lists, with positive framing. * **60–79 (good)**: You're a top-of-mind option but not always the first one mentioned. Look at per-intent visibility to find where you're weak. * **40–59 (fair)**: You appear sometimes, often near the end of lists or with neutral language. Significant ASO + content opportunities. * **0–39 (poor)**: AI assistants rarely surface you for these intents. Consider whether your intents accurately reflect what you do, and whether your app's discoverability outside the App Store (web presence, reviews, mentions in content the assistants train on) is enough. *** ## Code examples ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/ai-visibility/1234567890/overview?country=us&model=chatgpt" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const params = new URLSearchParams({ country: "us", model: "chatgpt" }); const res = await fetch( `https://api.appeeky.com/v1/ai-visibility/1234567890/overview?${params}`, { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); console.log(`AI Visibility: ${data.visibilityScore.score} (${data.visibilityScore.label})`); console.log(`Sentiment: ${data.sentimentPct}%`); console.log(`Intents: ${data.intentsCovered}/${data.intentsTotal} covered`); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests r = requests.get( "https://api.appeeky.com/v1/ai-visibility/1234567890/overview", params={"country": "us", "model": "chatgpt"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = r.json()["data"] print(f"Score: {data['visibilityScore']['score']} ({data['visibilityScore']['label']})") ``` *** ## Pattern: render gauges for all enabled models side-by-side ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} const models = ["chatgpt", "claude", "gemini", "perplexity"]; const all = await Promise.all( models.map((m) => fetch( `https://api.appeeky.com/v1/ai-visibility/1234567890/overview?country=us&model=${m}`, { headers: { "X-API-Key": "YOUR_API_KEY" } } ).then((r) => r.json()) ) ); all.forEach((res, i) => console.log(`${models[i]}: ${res.data.visibilityScore.score}`) ); ``` You'll typically see different scores per model — a 92 on ChatGPT and a 58 on Gemini is meaningful signal. *** ## Credits * **2 credits** per call. *** ## Errors | Status | Code | When | | ------ | ---------------- | -------------------------------- | | 400 | INVALID\_APP\_ID | Missing or non-numeric app ID | | 401 | — | Missing or invalid API key / JWT | | 429 | — | Insufficient monthly credits | *** ## See also * [Bootstrap](/docs/ai-visibility-bootstrap) — required before this endpoint returns scored data * [Intents endpoint](/docs/ai-visibility-intents) — per-intent breakdown that drives the score * [Competitors](/docs/ai-visibility-competitors) — apps appearing alongside or instead of you * [AI Visibility overview](/docs/ai-visibility-overview) — concept page # Prompts Source: https://docs.appeeky.com/docs/ai-visibility-prompts Add, edit, pause, or remove the user-style prompts the AI Visibility agent sends to ChatGPT, Gemini, Claude, and Perplexity A **prompt** is a single user-style query the agent sends to each enabled AI model on every scan. Prompts always belong to an [intent](/docs/ai-visibility-intents) — together they're the questions a real user might ask an assistant when looking for an app like yours. The bootstrap generates 5–8 prompts per intent automatically. You can edit, pause, archive, or add your own at any time. This page covers: | Endpoint | Purpose | | --------------------------------------------------------- | ------------------------------------------ | | `POST /v1/ai-visibility/:appId/intents/:intentId/prompts` | Add a custom prompt to an intent | | `PATCH /v1/ai-visibility/:appId/prompts/:promptId` | Edit text, change style, pause, or archive | | `DELETE /v1/ai-visibility/:appId/prompts/:promptId` | Archive (soft delete) | To **list** prompts under an intent, use [GET /intents/:intentId](/docs/ai-visibility-intents) — the response includes a `prompts[]` array. *** ## Prompt styles Every prompt has a `style` tag. Variety matters: 5 paraphrases of "best meditation app" don't tell you anything new, but a problem-led prompt and a comparison prompt for the same intent often produce *very* different recommendations from the same model. | Style | What it looks like | When to use | | ------------ | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | | `unbranded` | "best app for meditation" | Pure category visibility — does the assistant know about you at all? | | `branded` | "is Calm or Headspace better for sleep?" | Discoverability when the user is comparison-shopping. | | `problem` | "I struggle to fall asleep at night, what app can help me wind down?" | Real-world phrasing — picks up apps the assistant associates with the *outcome* rather than the category. | | `use_case` | "an app I can use during my morning commute" | Scenario-driven discovery. | | `comparison` | "alternatives to Calm" | Surfaces competitor benchmarking — useful for understanding *who* AI thinks is similar to a market leader. | The bootstrap aims to produce at least 3 different styles per intent. When you add prompts manually, picking a style outside what already exists for an intent gives you the most new signal. *** ## Add a prompt ``` POST /v1/ai-visibility/:appId/intents/:intentId/prompts ``` ### Body ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "text": "what's a good app for tracking workouts and recovery on Apple Watch?", "style": "use_case" } ``` | Field | Type | Required | Description | | ------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `text` | string | Yes | The prompt the assistants will receive verbatim. 15–220 chars. Write it the way a real user would type it — conversational tone, lowercase ok, contractions ok. | | `style` | string | No | One of `unbranded`, `branded`, `problem`, `use_case`, `comparison`. Defaults to `unbranded` if not provided or invalid. | ### Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "id": "p123...", "intentId": "9c1f...", "text": "what's a good app for tracking workouts and recovery on Apple Watch?", "promptStyle": "use_case", "status": "active", "source": "user" } } ``` The prompt is added with `status: "active"` and will be included in the next scan. If you want it picked up immediately, follow with [POST /scan](/docs/ai-visibility-scan-settings). > **Deduplication.** Re-posting the same text under the same intent is a no-op — we fingerprint the normalised text and ignore duplicates. This is safe to use idempotently from import scripts. *** ## Edit / pause / archive ``` PATCH /v1/ai-visibility/:appId/prompts/:promptId ``` ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "text": "what's the best workout-tracking app for Apple Watch?", "style": "unbranded", "status": "active" } ``` All fields optional. Common patterns: ```jsonc theme={"theme":{"light":"github-light","dark":"github-dark"}} // Pause a prompt (keep it but skip in next scans) { "status": "paused" } // Re-classify the style without changing the text { "style": "comparison" } // Reword { "text": "..." } ``` Editing `text` re-fingerprints the prompt; if the new text matches an existing one under the same intent, the update will fail silently and you'll get the existing row's text on the next read. *** ## Delete (archive) ``` DELETE /v1/ai-visibility/:appId/prompts/:promptId ``` Soft-archive — historical answers stay in the database (so the dashboard's per-prompt history doesn't break) but the prompt is removed from the active rotation and from list endpoints. *** ## Code examples ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} # Add a problem-led prompt to an intent curl -X POST "https://api.appeeky.com/v1/ai-visibility/1234567890/intents/9c1f.../prompts" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "text": "I have anxiety attacks at work, what app can calm me down quickly?", "style": "problem" }' # Pause a prompt curl -X PATCH "https://api.appeeky.com/v1/ai-visibility/1234567890/prompts/p123..." \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "status": "paused" }' # Archive a prompt curl -X DELETE "https://api.appeeky.com/v1/ai-visibility/1234567890/prompts/p123..." \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const HEADERS = { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json" }; // Bulk-add prompts under one intent const intentId = "9c1f..."; const promptsToAdd = [ { text: "alternatives to Headspace that are cheaper", style: "comparison" }, { text: "an app for meditating during my lunch break", style: "use_case" }, { text: "I keep forgetting to meditate, what app sends good reminders?", style: "problem" }, ]; for (const p of promptsToAdd) { await fetch( `https://api.appeeky.com/v1/ai-visibility/1234567890/intents/${intentId}/prompts`, { method: "POST", headers: HEADERS, body: JSON.stringify(p) } ); } ``` *** ## Tips for writing good prompts * **Keep it conversational.** "best meditation app for beginners" beats "Comprehensive evaluation of meditation applications for novice users". * **Avoid mentioning yourself.** A prompt that names your app guarantees you'll appear — but it doesn't measure visibility, it measures the assistant's willingness to repeat a name back. * **One question per prompt.** Compound questions confuse the assistants and yield messier mention extraction. * **Cover the intent, not the feature.** "an app that plays nature sounds" is too narrow if your intent is *Reduce stress and anxiety to feel calm*; "what helps me calm down before bed?" is better. * **Add at least one prompt in each language you care about** — assistants behave differently across languages (a Turkish prompt often produces a different competitor set than an English one for the same intent). *** ## Credits | Endpoint | Cost | | --------------------------------- | -------- | | `POST /intents/:intentId/prompts` | 1 credit | | `PATCH /prompts/:promptId` | Free | | `DELETE /prompts/:promptId` | Free | Listing prompts is part of the [intent drill-down](/docs/ai-visibility-intents) (2 credits). *** ## Errors | Status | Code | When | | ------ | -------------- | ------------------------------------------------------------ | | 400 | INVALID\_INPUT | `text` shorter than 15 chars or contains invalid characters | | 404 | NOT\_FOUND | Intent or prompt doesn't exist or doesn't belong to this app | | 401 | — | Missing or invalid API key / JWT | | 429 | — | Insufficient monthly credits | *** ## See also * [Intents](/docs/ai-visibility-intents) — list/drill-down + intent CRUD * [Answers](/docs/ai-visibility-answers) — see the actual AI responses for each prompt * [Scan & Settings](/docs/ai-visibility-scan-settings) — re-scan after adding new prompts # Scan & Settings Source: https://docs.appeeky.com/docs/ai-visibility-scan-settings Trigger an on-demand AI Visibility scan and configure which models to use, scan cadence, and feature toggles This page covers two related groups of endpoints: * **`POST /scan`** — kick off an immediate re-scan (useful right after a metadata change or when you want fresh data without waiting for the daily run). * **`GET / PUT /settings`** — choose which AI models to scan, how often the scheduled scans run, and whether AI Visibility is enabled at all for the app. *** ## Trigger an on-demand scan ``` POST /v1/ai-visibility/:appId/scan ``` Sends every active prompt for the app to every enabled AI model, parses the answers, resolves competitor app names, and updates the daily roll-up. Returns immediately (HTTP 201); the actual work runs in the background and full results land in `GET /overview` within roughly **3–6 minutes**. > Scheduled scans run automatically — you should only need this endpoint when you want fresh data *now* (after a launch, a new feature, a metadata change, or right after adding new intents/prompts). ### Path parameters | Name | Type | Required | Description | | ----- | ------ | -------- | ---------------------- | | appId | string | Yes | Apple App ID (numeric) | ### Body No body is required. The scan uses the app's saved settings (country, models, language). ### Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "taskRunId": "run_x9z2...", "promptsTotal": 48, "models": ["chatgpt", "claude", "gemini", "perplexity"], "estimatedCost": 1536 } } ``` | Field | Description | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `taskRunId` | Trigger.dev run id for the orchestrator. Useful for support requests but not normally needed. | | `promptsTotal` | Number of active prompts that will be sent. | | `models` | The models that will receive each prompt — derived from your settings, capped at your plan's `maxModels` and filtered to providers Appeeky has API keys for. | | `estimatedCost` | AI Visibility credits this scan will consume. Same value as the `X-Credit-Cost` header. | If there are **no active prompts** the call returns `400 NO_PROMPTS`. Run [bootstrap](/docs/ai-visibility-bootstrap) first or add intents + prompts manually. ### Same-day idempotency Each `(prompt × model × day)` is fingerprinted before the model call. Re-running `/scan` later the same day **won't** charge you for a second round of model calls — the existing answers are reused. So you can hit "Refresh" in the UI without worrying about double-billing. If you really want a re-query on the same day (e.g. you suspect a flaky earlier answer), pause the prompt, scan, then re-activate it; or wait until tomorrow's automatic scan. *** ## Get or update settings ### `GET /v1/ai-visibility/:appId/settings` Returns the current scanning configuration for the app, plus the list of AI providers Appeeky currently has credentials for (so you can render a UI that only offers configurable options). ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "settings": { "ownerEmail": "you@example.com", "appId": "1234567890", "country": "us", "language": "en", "enabled": true, "models": ["chatgpt", "perplexity"], "scanCadenceDays": 3, "nextScanAt": "2026-05-15T08:00:00.000Z", "lastScanAt": "2026-05-12T08:01:14.221Z", "lastBootstrapAt": "2026-05-10T15:42:08.001Z" }, "availableModels": ["chatgpt", "claude", "gemini", "perplexity"], "tier": { "planId": "small", "enabled": true, "maxModels": 2, "maxApps": 2, "maxCountries": 1 }, "aiVisibilityCredits": { "limit": 8000, "used": 1944, "remaining": 6056, "estimatedMonthlyCost": 5760, "resetDate": "2026-06-12T00:00:00.000Z" } } } ``` `settings` is `null` until you've called [bootstrap](/docs/ai-visibility-bootstrap) at least once. `tier` reflects the caller's current plan and the per-plan AI Visibility caps (see [pricing overview](/docs/ai-visibility-overview#pricing)). Use it in your dashboard to grey out model toggles the user can't actually enable: e.g. on the `small` plan above, only the first 2 entries in `availableModels` are selectable. `aiVisibilityCredits` is the dedicated credit bucket for AI Visibility (separate from your general API quota). `estimatedMonthlyCost` is a forecast based on the current cadence and enabled models — `(prompts × Σ(model_cost)) × (30 / scanCadenceDays)`. Use it to surface "you'll spend \~5,760 / 8,000 this month" before the user commits to a setting change. ### `PUT /v1/ai-visibility/:appId/settings` ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "enabled": true, "models": ["chatgpt", "perplexity"], "scanCadenceDays": 1, "language": "en" } ``` | Field | Type | Description | | ----------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `enabled` | bool | Master switch. `false` stops scheduled scans (existing data and intents stay). | | `models` | string\[] | Which AI models to scan. Subset of `availableModels`. The `chatgpt` model is the most commonly enabled — most users start with one or two and expand later. | | `scanCadenceDays` | int | `0` disables scheduled scans, `1` = daily, `3` = every 3 days **(default)**, `7` = weekly, max `30`. The next scan after a settings change is scheduled `scanCadenceDays` from now. AI recommendations change slowly, so 3-day cadence captures most movement at a third of daily's credit cost. | | `language` | string | ISO 639-1, biases newly-generated prompts. Doesn't affect existing prompts. | All fields are optional — only the ones you provide are updated. *** ## Cost controls Both manual and scheduled scans bill against the **dedicated AI Visibility credit bucket** (`api_plans.ai_visibility_monthly_credits`) — *not* your general API quota. Each call costs **per (prompt × model)**, summed across every model you've enabled. | Model | Credits per call | | ------------ | ---------------- | | `chatgpt` | 9 | | `claude` | 15 | | `gemini` | 5 | | `perplexity` | 3 | A typical setup of **8 intents × 6 prompts = 48 prompts** at the **default 3-day cadence** (10 scans/month) burns: | Models enabled | Credits per scan | Monthly (3-day cadence) | Monthly (daily cadence) | | ------------------------ | --------------------- | ----------------------- | ----------------------- | | `chatgpt` only | `48 × 9` = **432** | \~4,300 | \~13,000 | | `chatgpt` + `perplexity` | `48 × 12` = **576** | \~5,800 | \~17,300 | | All four | `48 × 32` = **1,536** | \~15,400 | \~46,100 | The exact estimate for your current setup is returned by `GET /settings → aiVisibilityCredits.estimatedMonthlyCost`. Manual `/scan` calls return the per-call charge in `X-Credit-Cost` (and the response body's `estimatedCost`). ### What happens when the bucket runs low | Trigger | Behavior | | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Manual `/scan`** | **Hard fail** — the API returns `429 AI_VISIBILITY_QUOTA_EXCEEDED` with the requested cost and your remaining balance. No scan runs, no credits deducted. | | **Scheduled cron scan** | **Graceful degrade** — the orchestrator drops models in cost-descending order (`claude → gemini → perplexity`), keeping `chatgpt` as the most user-valuable fallback, until the scan fits remaining budget. The dropped models are recorded on the run row (`models_dropped`, `degraded_reason`). When even `chatgpt`-only doesn't fit, the scan is skipped entirely and `next_scan_at` is pushed out 24h. | You can detect a degraded scan in the dashboard by checking `ai_visibility_runs.models_dropped` — it's empty when the full requested set ran, otherwise it lists the silently-removed models so you can prompt the user to upgrade. ### Tier limits on `models[]` The PUT endpoint enforces a per-plan cap on how many models you can enable simultaneously: | Plan | Monthly AI Visibility credits | Allowed models | | ---------- | ----------------------------: | ----------------------------- | | Free | 0 | — (AI Visibility unavailable) | | Indie | 3,000 | 1 | | Starter | 8,000 | 2 | | Growth | 20,000 | 4 (all) | | Pro | 50,000 | 4 (all) | | Enterprise | 150,000 | 4 (all) | If you submit `models: ["chatgpt", "claude"]` on the Indie plan, the API returns `403 TIER_LIMIT_EXCEEDED` and your settings stay unchanged. A practical recommendation: * **Start with `["chatgpt"]`.** It's the dominant assistant by usage and gives you the most signal per credit. * **Add `perplexity`** when you want web-grounded comparisons (Perplexity returns citations alongside the answer — useful for understanding *why* the assistant picked an app). * **Add `claude` and `gemini`** when you have evidence your audience uses them. Note that `claude` is the most expensive at 15 credits/call — the scheduler will drop it first when budget gets tight. *** ## Code examples ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} # Manual scan curl -X POST "https://api.appeeky.com/v1/ai-visibility/1234567890/scan" \ -H "X-API-Key: YOUR_API_KEY" # Read settings curl "https://api.appeeky.com/v1/ai-visibility/1234567890/settings?country=us" \ -H "X-API-Key: YOUR_API_KEY" # Switch to weekly scans on chatgpt + perplexity curl -X PUT "https://api.appeeky.com/v1/ai-visibility/1234567890/settings?country=us" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "models": ["chatgpt", "perplexity"], "scanCadenceDays": 7 }' # Pause AI Visibility for this app curl -X PUT "https://api.appeeky.com/v1/ai-visibility/1234567890/settings?country=us" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "enabled": false }' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const HEADERS = { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json" }; // Trigger an immediate scan after a metadata update await fetch( "https://api.appeeky.com/v1/ai-visibility/1234567890/scan", { method: "POST", headers: HEADERS } ); // Switch the app from daily to weekly scans await fetch( "https://api.appeeky.com/v1/ai-visibility/1234567890/settings?country=us", { method: "PUT", headers: HEADERS, body: JSON.stringify({ scanCadenceDays: 7 }), } ); ``` *** ## Credits | Endpoint | Cost | Bucket | | --------------- | ------------------------------------------------------------------------------ | ------------- | | `POST /scan` | Dynamic — `Σ(model credit cost) × prompts_count`. Returned in `X-Credit-Cost`. | AI Visibility | | `GET /settings` | 1 credit | AI Visibility | | `PUT /settings` | Free | — | All AI Visibility endpoints meter against `api_plans.ai_visibility_monthly_credits`. The middleware sets `X-AiVisibility-Limit` and `X-AiVisibility-Remaining` headers on the response so dashboards can render the current bucket state. *** ## Errors | Status | Code | When | | ------ | ------------------------------- | ----------------------------------------------------------------------------------------------------------- | | 400 | NO\_PROMPTS | App has no active prompts — run [bootstrap](/docs/ai-visibility-bootstrap) first | | 400 | INVALID\_APP\_ID | Missing or non-numeric app ID | | 403 | TIER\_LIMIT\_EXCEEDED | `models[]` exceeds your plan's max, or AI Visibility is unavailable on your plan | | 401 | — | Missing or invalid API key / JWT | | 429 | AI\_VISIBILITY\_QUOTA\_EXCEEDED | Insufficient AI Visibility credits — top up, downgrade your model selection, or wait for the quota to reset | *** ## See also * [Bootstrap](/docs/ai-visibility-bootstrap) — required before the first scan * [Overview endpoint](/docs/ai-visibility-overview-endpoint) — read fresh data after the scan completes * [Intents](/docs/ai-visibility-intents) — change what's scanned * [Prompts](/docs/ai-visibility-prompts) — change which queries are sent # Trend Source: https://docs.appeeky.com/docs/ai-visibility-trend Daily history of visibility, sentiment, and prompt counts for charting ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/ai-visibility/:appId/trend ``` Returns a **daily series** of AI Visibility metrics. Use it to draw the chart on your dashboard — score over time, sentiment over time, prompt coverage over time — for a single `(app, country, model)` combination. The series can be scoped to either: * the **whole app** (default) — the same numbers the headline gauge shows in [GET /overview](/docs/ai-visibility-overview-endpoint), one row per scored day. * a **single intent** — pass `intentId` to drill into how visibility for that one user goal moved over time. *** ## Path parameters | Name | Type | Required | Description | | ----- | ------ | -------- | ---------------------- | | appId | string | Yes | Apple App ID (numeric) | ## Query parameters | Name | Type | Default | Description | | ---------- | ------ | --------- | ----------------------------------------------------------------------------------------------------------------------------- | | country | string | `us` | ISO country code | | model | string | `chatgpt` | One of `chatgpt`, `claude`, `gemini`, `perplexity` | | windowDays | int | `30` | Lookback window. Min 1, max 90. | | intentId | string | — | Optional. If provided, restricts the series to that one intent. Otherwise returns the app-level series (`intent_id IS NULL`). | *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": [ { "date": "2026-04-12", "visibilityScore": 31, "visibilityPct": 22.2, "sentimentPct": 64, "positionAvg": 3.1, "promptsTotal": 54, "promptsWithOwner": 12 }, { "date": "2026-04-15", "visibilityScore": 38, "visibilityPct": 27.8, "sentimentPct": 67, "positionAvg": 2.7, "promptsTotal": 54, "promptsWithOwner": 15 }, { "date": "2026-04-18", "visibilityScore": 42, "visibilityPct": 31.5, "sentimentPct": 70, "positionAvg": 2.4, "promptsTotal": 54, "promptsWithOwner": 17 } ] } ``` Rows are returned **oldest → newest** so a chart library can plot them directly. Days without a scan are omitted (the series isn't densified) — pass `windowDays=30` and you'll get however many days actually have data inside that window. | Field | Type | Description | | ---------------- | -------------- | --------------------------------------------------------------------------------- | | date | string | ISO date (`YYYY-MM-DD`) the scan was scored on | | visibilityScore | int | 0–100 composite score for that day | | visibilityPct | number | % of prompts that mentioned your app | | sentimentPct | number \| null | Average sentiment of mentions, expressed 0–100. Null when there were no mentions. | | positionAvg | number \| null | Average rank position of your app in mentions. Null when there were no mentions. | | promptsTotal | int | How many prompts were scanned that day | | promptsWithOwner | int | How many of those mentioned your app | *** ## Examples ### App-level 30-day trend (default) ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl https://api.appeeky.com/v1/ai-visibility/1234567890/trend \ -H "Authorization: Bearer $JWT" ``` ### Single intent, 90-day window ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/ai-visibility/1234567890/trend?intentId=8c3a-xxxx&windowDays=90" \ -H "Authorization: Bearer $JWT" ``` ### Compare models side-by-side Call the endpoint once per model and overlay the series: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} for m in chatgpt claude gemini perplexity; do curl "https://api.appeeky.com/v1/ai-visibility/1234567890/trend?model=$m" \ -H "Authorization: Bearer $JWT" > "trend-$m.json" done ``` *** ## Credits This is a **read** endpoint billed against the AI Visibility credit pool: **1 credit per call**. See [Overview & pricing](/docs/ai-visibility-overview) for the full pricing table. # App Ad Creatives Source: https://docs.appeeky.com/docs/app-ad-creatives Generate Meta-ready square ad creatives and paste-ready ad copy from a real App Store or Google Play listing ``` POST /v1/app-ad-creatives/generate GET /v1/app-ad-creatives/jobs/:jobId ``` Create a finished mobile app ad from a real store listing. Appeeky analyzes the app's title, description, icon, screenshots, audience, and positioning, then returns: * confirmed product key points * paste-ready Meta primary text, headline, description, and call to action * a square `1024x1024` PNG creative generated from the app's real icon and screenshots * the final prompt used to generate the creative The work runs asynchronously on Trigger.dev. `POST /v1/app-ad-creatives/generate` returns a `jobId` immediately with HTTP `202`; poll `GET /v1/app-ad-creatives/jobs/:jobId` until the job is `completed` or `failed`. Generated creatives use real listing assets as references. When screenshots are available, Appeeky composites the actual app UI into the creative instead of inventing unrelated product screens. *** ## Create a creative job ``` POST /v1/app-ad-creatives/generate ``` ### Request body | Field | Type | Required | Default | Description | | --------------------- | --------- | ------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | | `appUrl` | string | Conditional | — | App Store or Google Play listing URL. Required unless `platform` + `appId` are provided. | | `platform` | string | Conditional | — | `apple` or `google`. Required when using `appId` instead of `appUrl`. | | `appId` | string | Conditional | — | Apple numeric app ID or Google Play package name. Required when using `platform`. | | `country` | string | No | `us` | Storefront country, ISO 3166-1 alpha-2. | | `lang` | string | No | `en` | Google Play language code. | | `mode` | string | No | `generate` | `analyze`, `generate`, or `edit`. | | `style` | string | No | `ugc` | Creative direction. Built-ins: `ugc`, `professional`, `problem_solution`, `before_after`, `lifestyle`. Custom strings are also accepted. | | `imagePreset` | string | No | `branded_showcase` | Composition preset. See [Image presets](#image-presets). | | `angle` | string | No | inferred | Optional ad angle, e.g. `problem-solution for busy founders`. | | `audience` | string | No | inferred | Optional target audience override. | | `keyPoints` | object | No | inferred | User-confirmed product facts to steer the ad. | | `extraScreenshotUrls` | string\[] | No | `[]` | Additional public screenshots to use as visual references. | | `generateImage` | boolean | No | `true` | Set `false` to only analyze the listing and generate copy. | | `quality` | string | No | `medium` | Image quality: `low`, `medium`, or `high`. | | `editInstruction` | string | Required for `edit` | — | Natural-language edit instruction, e.g. `Make the headline bigger and use a navy background`. | | `previousAd` | object | No | — | Previous `ad` object when using `mode: "edit"`. | ### Credits Appeeky separates normal API credits from creative credits. | Mode | Bucket | Cost | Notes | | ---------------------- | ---------------- | -------------------------------: | ----------------------------------------------- | | `analyze` | API credits | 1 | Listing analysis + key points + copy, no image. | | `generateImage: false` | API credits | 1 | Copy-only run, regardless of mode. | | `generate` | Creative credits | `low`: 1, `medium`: 2, `high`: 5 | New square ad image + copy. | | `edit` | Creative credits | `low`: 1, `medium`: 2, `high`: 5 | Modify an existing image ad with a sentence. | You can pass `X-OpenAI-Key` to use your own OpenAI key. BYOK image jobs use 1 API credit and do not consume creative credits. The key is not stored in the job record. ### Image presets | Preset | Best for | What it creates | | ------------------------ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------ | | `branded_showcase` | Product-first launch ads | App icon/name, large headline, exactly one hero phone mockup with a real screenshot, and benefit callouts. | | `person_holding_phone` | Lifestyle/people ads | A realistic person or hands holding a phone, with the real app UI composited on screen and text in negative space. | | `creator_testimonial` | UGC/testimonial ads | A creator-style or diary-style ad with a short quote, app lockup, and real app screen. | | `problem_solution_split` | Direct-response ads | A before/after or problem/solution split layout with the app as the answer. | | `clean_app_store_mockup` | Premium store/social hybrids | Minimal whitespace, crisp phone mockup, icon, headline, and short subhead. | ## MCP tools The same workflow is available through the Appeeky MCP Server: | Tool | Description | | -------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | `generate_app_ad_creative` | Starts an async app ad creative job. Supports the same `mode`, `style`, `image_preset`, `angle`, and `quality` options. | | `get_app_ad_creative_job` | Polls the job and returns the generated image URL, Meta copy, key points, and final prompt when complete. | ### Code examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/app-ad-creatives/generate" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "appUrl": "https://apps.apple.com/us/app/forest-focus-for-productivity/id866450515", "country": "us", "mode": "generate", "style": "ugc", "imagePreset": "person_holding_phone", "angle": "problem-solution for people distracted by their phones", "audience": "students and knowledge workers" }' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const start = await fetch("https://api.appeeky.com/v1/app-ad-creatives/generate", { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ appUrl: "https://apps.apple.com/us/app/forest-focus-for-productivity/id866450515", country: "us", mode: "generate", style: "ugc", imagePreset: "person_holding_phone", }), }); const { data } = await start.json(); const jobId = data.jobId; async function pollCreative() { const res = await fetch( `https://api.appeeky.com/v1/app-ad-creatives/jobs/${jobId}`, { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); if (data.status === "completed" || data.status === "failed") return data; await new Promise((resolve) => setTimeout(resolve, 4000)); return pollCreative(); } console.log(await pollCreative()); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import time, requests headers = {"X-API-Key": "YOUR_API_KEY"} start = requests.post( "https://api.appeeky.com/v1/app-ad-creatives/generate", headers={**headers, "Content-Type": "application/json"}, json={ "platform": "google", "appId": "com.spotify.music", "country": "us", "lang": "en", "mode": "generate", "style": "professional", "imagePreset": "clean_app_store_mockup", }, ) job_id = start.json()["data"]["jobId"] while True: job = requests.get( f"https://api.appeeky.com/v1/app-ad-creatives/jobs/{job_id}", headers=headers, ).json()["data"] if job["status"] in ("completed", "failed"): break time.sleep(4) print(job) ``` ### Response (`202 Accepted`) ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "jobId": "4b8419b6-0d3a-4ab7-a8e8-6d1f4b53d414", "triggerRunId": "run_abc123", "status": "queued", "mode": "generate" } } ``` *** ## Poll job status ``` GET /v1/app-ad-creatives/jobs/:jobId ``` ### Response while running ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "jobId": "4b8419b6-0d3a-4ab7-a8e8-6d1f4b53d414", "status": "processing", "mode": "generate", "input": { "appUrl": "https://apps.apple.com/us/app/forest-focus-for-productivity/id866450515", "country": "us", "mode": "generate", "style": "ugc", "imagePreset": "person_holding_phone" }, "result": null, "error": null, "triggerRunId": "run_abc123", "createdAt": "2026-06-25T08:42:11.000Z", "updatedAt": "2026-06-25T08:42:16.000Z" } } ``` ### Response when completed ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "jobId": "4b8419b6-0d3a-4ab7-a8e8-6d1f4b53d414", "status": "completed", "mode": "generate", "result": { "listing": { "platform": "apple", "appId": "866450515", "country": "us", "lang": null, "title": "Forest: Focus for Productivity", "developer": "SEEKRTECH CO., LTD.", "url": "https://apps.apple.com/us/app/id866450515", "iconUrl": "https://...", "description": "Stay focused, be present...", "category": "Productivity", "rating": 4.8, "reviewsCount": 424000, "isFree": false, "screenshots": ["https://...", "https://..."] }, "keyPoints": { "whatItDoes": "Helps users stay off their phone and focus by growing virtual trees.", "targetAudience": "Students and knowledge workers who get distracted by their phones.", "painPoint": "They keep losing focus to quick phone checks.", "promise": "Turn focused time into a simple visual reward.", "brandTone": "Calm, motivating, nature-inspired.", "visualDirection": "Show real app screens inside a clean phone mockup with a focus-progress hook.", "adAngle": "Problem-solution" }, "assets": { "iconUrl": "https://...", "screenshotUrls": ["https://...", "https://..."], "referenceImageCount": 4 }, "ad": { "id": "cb02a6a2-b01f-43b3-a02f-58b4863d9f8e", "style": "ugc", "imagePreset": "person_holding_phone", "angle": "Problem-solution", "imageUrl": "https://assets.appeeky.com/app-ad-creatives/cb02.../square.png", "width": 1024, "height": 1024, "format": "png", "copy": { "primaryText": "Your phone keeps pulling you away from deep work. Forest turns focus into a tiny reward: plant a tree, stay present, and watch your progress grow.", "headline": "Grow your focus", "description": "Stay off your phone and get more done.", "callToAction": "DOWNLOAD" }, "creativeHeadline": "Stop checking your phone", "creativeSubhead": "Grow a tree every time you stay focused.", "prompt": "Create a square 1024x1024 Meta/Instagram ad creative...", "generatedAt": "2026-06-25T08:43:04.000Z" } }, "error": null, "triggerRunId": "run_abc123", "createdAt": "2026-06-25T08:42:11.000Z", "updatedAt": "2026-06-25T08:43:04.000Z" } } ``` ### Job fields | Field | Type | Description | | -------------- | ------ | --------------------------------------------------------------- | | `status` | string | `queued`, `processing`, `completed`, or `failed`. | | `mode` | string | The requested mode: `analyze`, `generate`, or `edit`. | | `input` | object | The safe job input. Sensitive BYOK values are not stored here. | | `result` | object | The completed creative payload. `null` until the job completes. | | `error` | string | Failure message when `status` is `failed`; otherwise `null`. | | `triggerRunId` | string | Trigger.dev run identifier for support/debugging. | *** ## Edit an existing ad Use `mode: "edit"` with a previous `ad` object and a natural-language instruction. ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/app-ad-creatives/generate" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "appUrl": "https://apps.apple.com/us/app/forest-focus-for-productivity/id866450515", "mode": "edit", "editInstruction": "Make the headline bigger and change the background to navy.", "previousAd": { "style": "ugc", "creativeHeadline": "Stop checking your phone", "creativeSubhead": "Grow a tree every time you stay focused." } }' ``` *** ## Errors | Status | Code | When | | ------ | ----------------------- | ------------------------------------------------------------------------------------- | | 400 | `INVALID_REQUEST` | Missing or invalid app URL, app ID, platform, or edit instruction. | | 401 | `MISSING_AUTH` | No `Authorization: Bearer ` or `X-API-Key` header. | | 401 | `INVALID_API_KEY` | Invalid or inactive API key. | | 403 | `PRO_FEATURE` | The API key owner is not on a Pro plan. | | 404 | `APP_NOT_FOUND` | The app listing could not be found in the requested storefront. | | 404 | `JOB_NOT_FOUND` | The requested job ID does not exist. | | 503 | `OPENAI_NOT_CONFIGURED` | Server-side OpenAI credentials are not configured and no `X-OpenAI-Key` was provided. | # App Keyword Clusters Source: https://docs.appeeky.com/docs/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. # App Screenshots Source: https://docs.appeeky.com/docs/app-screenshots Get all screenshots for an app, separated by device type (iPhone / iPad) ``` GET /v1/apps/:id/screenshots ``` Fetch all App Store screenshots for an app, split by device type. Returns full-resolution screenshot URLs directly from the iTunes Lookup API. Useful for ASO creative analysis, competitive benchmarking, or building screenshot galleries. ## Path Parameters | Name | Type | Required | Description | | ---- | ------ | -------- | ---------------------------------------------------------------------------------------- | | id | string | Yes | App ID — numeric for Apple (`1617391485`), package name for Google (`com.spotify.music`) | ## Query Parameters | Name | Type | Default | Description | | -------- | ------ | ------- | -------------------------------------------------------------------------------- | | platform | string | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | `us` | ISO country code (e.g. `us`, `gb`, `de`, `jp`) | | device | string | `all` | Device filter: `iphone`, `ipad`, or `all` (Apple only) | | lang | string | `en` | Google Play language code (used when `platform=google`) | ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/apps/1617391485/screenshots?country=us&device=all" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/apps/1617391485/screenshots?country=us&device=all", { headers: { "X-API-Key": "YOUR_API_KEY", }, } ); const data = await response.json(); console.log(data); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/apps/1617391485/screenshots", params={"country": "us", "device": "all"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = response.json() print(data) ``` ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "1617391485", "title": "Block Blast!", "developer": "Hungry Studio", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../512x512bb.jpg", "screenshots": { "iphone": [ "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen1.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen2.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen3.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen4.jpg" ], "ipad": [ "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../ipad-screen1.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../ipad-screen2.jpg" ] }, "totalCount": 6 } } ``` ### Response with `device=iphone` When filtering by device, only the requested device array is populated: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "1617391485", "title": "Block Blast!", "developer": "Hungry Studio", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../512x512bb.jpg", "screenshots": { "iphone": [ "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen1.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen2.jpg" ], "ipad": [] }, "totalCount": 2 } } ``` ## Response Fields | Field | Type | Description | | ----------- | ------ | ------------------------------------ | | appId | string | Apple App ID | | title | string | App name | | developer | string | Developer / publisher name | | icon | string | App icon URL (512px) | | screenshots | object | Screenshot URLs grouped by device | | totalCount | number | Total number of screenshots returned | ### Screenshots Object | Field | Type | Description | | ------ | --------- | ---------------------- | | iphone | string\[] | iPhone screenshot URLs | | ipad | string\[] | iPad screenshot URLs | Use `device=iphone` or `device=ipad` to reduce response size when you only need screenshots for a specific device. The default `all` returns both. Screenshot URLs are served from Apple's CDN (`is1-ssl.mzstatic.com`). They are high-resolution and suitable for display at full size. The number of screenshots varies by app — most apps have 5-10 iPhone screenshots and 0-5 iPad screenshots. ## Errors | Status | Code | When | | ------ | ----------------- | ----------------------------------------------------- | | 400 | INVALID\_APP\_ID | Non-numeric or missing app ID | | 400 | INVALID\_DEVICE | Device not `iphone`, `ipad`, or `all` | | 401 | MISSING\_API\_KEY | No API key in the request | | 401 | INVALID\_API\_KEY | Invalid or expired API key | | 404 | APP\_NOT\_FOUND | App not found or unavailable in the specified country | # Analytics Reports Source: https://docs.appeeky.com/docs/app-store-connect-analytics Request and download App Store analytics — downloads, sessions, commerce, engagement The Analytics Reports API gives you programmatic access to 140+ report types: App Downloads, App Sessions, Commerce, App Store Engagement, Framework Usage, and more. Data is split into segments with downloadable URLs (gzip TSV). **Role required**: Admin, Finance, or Sales and Reports. New report requests may take **24–48 hours** before data is available. *** ## Flow Overview ``` 1. List/Create report request for your app 2. List reports for the request (e.g. "App Downloads Standard") 3. List instances for a report (daily, weekly, monthly granularity) 4. List segments for an instance 5. Download segment URL (gzip TSV with actual data) ``` *** ## List Report Requests ``` GET /v1/connect/apps/:appId/analytics/report-requests ``` List analytics report requests for an app. If empty, create one first. ### Query Parameters | Name | Type | Description | | ---------- | ------- | ---------------------------------------- | | accessType | string | Filter: `ONGOING` or `ONE_TIME_SNAPSHOT` | | limit | integer | Max results (default 50) | ### Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "data": [ { "type": "analyticsReportRequests", "id": "7e9b84a2-b441-49ce-a723-649fa483029c", "attributes": { "accessType": "ONGOING", "stoppedDueToInactivity": false } } ] } } ``` *** ## Create Report Request ``` POST /v1/connect/apps/:appId/analytics/report-requests ``` Create a new analytics report request. **ONGOING** generates daily reports; **ONE\_TIME\_SNAPSHOT** fetches historical data. ### Request Body ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "accessType": "ONGOING" } ``` | Field | Type | Description | | ---------- | ------ | ------------------------------------------------------------- | | accessType | string | `ONGOING` (daily reports) or `ONE_TIME_SNAPSHOT` (historical) | *** ## List Reports for Request ``` GET /v1/connect/analytics/report-requests/:requestId/reports ``` List available reports (140+ types). Filter by category to narrow down. ### Query Parameters | Name | Type | Description | | -------- | ------- | --------------------------------------------------------------------------------- | | category | string | `APP_USAGE`, `COMMERCE`, `APP_STORE_ENGAGEMENT`, `FRAMEWORK_USAGE`, `PERFORMANCE` | | limit | integer | Max results (default 50) | ### Example Report Types | Category | Examples | | ---------------------- | ------------------------------------------------------- | | COMMERCE | App Downloads Standard, App Store Purchases, Pre-Orders | | APP\_USAGE | App Sessions, Installation and Deletion | | APP\_STORE\_ENGAGEMENT | Discovery and Engagement | | FRAMEWORK\_USAGE | Home Screen Widgets, Spatial Audio, etc. | | PERFORMANCE | App Install Performance | *** ## List Report Instances ``` GET /v1/connect/analytics/reports/:reportId/instances ``` List instances for a report. Each instance represents a granularity (daily, weekly, monthly). ### Path Parameters | Name | Type | Description | | -------- | ------ | ---------------------------------------------------------- | | reportId | string | Report ID (e.g. `r3-7e9b84a2-b441-49ce-a723-649fa483029c`) | *** ## List Segments ``` GET /v1/connect/analytics/report-instances/:instanceId/segments ``` List segments for an instance. Each segment has a **URL** to download the actual data (gzip-compressed TSV). ### Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "data": [ { "type": "analyticsReportSegments", "id": "...", "attributes": { "url": "https://...", "checksum": "...", "sizeInBytes": 1234 } } ] } } ``` ### Download Report Data 1. Take the `url` from a segment 2. Fetch with your JWT (or the segment URL may be pre-signed by Apple) 3. Decompress the gzip response 4. Parse the TSV (tab-separated values) *** ## Get Segment Details ``` GET /v1/connect/analytics/report-segments/:segmentId ``` Get a single segment's details including the download URL. *** ## Code Example ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} # 1. Create report request (if none exists) curl -X POST "https://api.appeeky.com/v1/connect/apps/6759740679/analytics/report-requests" \ -H "X-API-Key: YOUR_APEEKY_KEY" \ -H "X-ASC-Issuer-Id: YOUR_ISSUER_ID" \ -H "X-ASC-Key-Id: YOUR_KEY_ID" \ -H "X-ASC-Private-Key: YOUR_PRIVATE_KEY" \ -H "Content-Type: application/json" \ -d '{"accessType":"ONGOING"}' # 2. List reports (e.g. filter by COMMERCE) curl -X GET "https://api.appeeky.com/v1/connect/analytics/report-requests/7e9b84a2.../reports?category=COMMERCE" \ -H "X-API-Key: YOUR_APEEKY_KEY" \ -H "X-ASC-Issuer-Id: YOUR_ISSUER_ID" \ -H "X-ASC-Key-Id: YOUR_KEY_ID" \ -H "X-ASC-Private-Key: YOUR_PRIVATE_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} // Full flow: list requests → list reports → list instances → list segments const appId = "6759740679"; const headers = { "X-API-Key": "YOUR_APEEKY_KEY", "X-ASC-Issuer-Id": "YOUR_ISSUER_ID", "X-ASC-Key-Id": "YOUR_KEY_ID", "X-ASC-Private-Key": process.env.ASC_PRIVATE_KEY, }; const requests = await fetch( `https://api.appeeky.com/v1/connect/apps/${appId}/analytics/report-requests`, { headers } ).then((r) => r.json()); const requestId = requests.data.data[0]?.id; const reports = await fetch( `https://api.appeeky.com/v1/connect/analytics/report-requests/${requestId}/reports`, { headers } ).then((r) => r.json()); // Find "App Downloads Standard" const downloadReport = reports.data.data.find( (r) => r.attributes?.name === "App Downloads Standard" ); const reportId = downloadReport?.id; ``` *** ## Credits | Endpoint | Credits | | -------------------- | ------- | | GET report-requests | 2 | | POST report-requests | 3 | | GET reports | 2 | | GET instances | 2 | | GET segments | 2 | | GET segment | 1 | # Apps & Versions Source: https://docs.appeeky.com/docs/app-store-connect-apps-versions List your apps, get app details, and manage App Store versions ## List Apps ``` GET /v1/connect/apps ``` List apps in your App Store Connect account. Filter by bundle ID to find a specific app. ### Headers | Header | Required | Description | | ----------------- | -------- | ---------------------- | | X-ASC-Issuer-Id | Yes | Your Issuer ID | | X-ASC-Key-Id | Yes | Your Key ID | | X-ASC-Private-Key | Yes\* | Your private key (PEM) | ### Query Parameters | Name | Type | Default | Description | | -------- | ------- | ------- | ------------------------------------------- | | bundleId | string | — | Filter by bundle ID (e.g. `app.taperecord`) | | limit | integer | 50 | Max results (1-200) | | cursor | string | — | Pagination cursor | ### Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "data": [ { "type": "apps", "id": "6759740679", "attributes": { "name": "Voice Tape Record", "bundleId": "app.taperecord", "sku": "apptaperecord", "primaryLocale": "en-US" } } ], "meta": { "paging": { "total": 1, "limit": 50 } } } } ``` *** ## Get App ``` GET /v1/connect/apps/:appId ``` Get details for a specific app by its App Store Connect app ID. ### Path Parameters | Name | Type | Description | | ----- | ------ | ----------------------------------------------------- | | appId | string | App Store Connect app ID (numeric, e.g. `6759740679`) | ### Response Returns the app object with `name`, `bundleId`, `sku`, `primaryLocale`. *** ## List App Infos ``` GET /v1/connect/apps/:appId/app-infos ``` List App Info resources for an app. App Info resources are used for app-level localizable fields such as `name` and `subtitle`. ### Path Parameters | Name | Type | Description | | ----- | ------ | ----------------------------------------------------- | | appId | string | App Store Connect app ID (numeric, e.g. `6759740679`) | ### Query Parameters | Name | Type | Default | Description | | ------ | ------- | ------- | ------------------- | | limit | integer | 50 | Max results (1-200) | | cursor | string | — | Pagination cursor | ### Response Fields | Field | Description | | ------------- | ----------------------------------------- | | appStoreState | App-level state for the App Info resource | *** ## List App Info Localizations ``` GET /v1/connect/app-infos/:appInfoId/localizations ``` List localizations for an App Info resource. This includes app-level localized fields like `name` and `subtitle`. ### Path Parameters | Name | Type | Description | | --------- | ------ | -------------------------------------------------------- | | appInfoId | string | App Info ID from `GET /v1/connect/apps/:appId/app-infos` | ### Response Fields | Field | Description | | ----------------- | -------------------------------------------------- | | locale | Localization code (e.g. `en-US`, `tr`) | | name | Localized app name | | subtitle | Localized subtitle shown in App Information | | privacyPolicyText | Privacy policy text for that locale (if available) | *** ## Update App Info Localization ``` PATCH /v1/connect/app-info-localizations/:localizationId ``` Update app-level localized fields. Use this endpoint to update fields visible under **App Information** in App Store Connect (such as subtitle). ### Path Parameters | Name | Type | Description | | -------------- | ------ | ------------------------ | | localizationId | string | App Info Localization ID | ### Request Body ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "attributes": { "name": "Voice Tape Record", "subtitle": "Audio recording & a retro VHS" } } ``` | Attribute | Type | Description | | ----------------- | ------ | ----------------------------- | | name | string | Localized app name | | subtitle | string | Localized subtitle | | privacyPolicyText | string | Localized privacy policy text | *** ## List Versions ``` GET /v1/connect/apps/:appId/versions ``` List App Store versions for an app. Returns version string, platform, state, release type, and dates. ### Query Parameters | Name | Type | Default | Description | | -------- | ------- | ------- | -------------------------------- | | platform | string | — | Filter: `IOS`, `MAC_OS`, `TV_OS` | | limit | integer | 50 | Max results (1-200) | | cursor | string | — | Pagination cursor | ### Response Fields | Field | Description | | ------------------- | ------------------------------------------------------------ | | versionString | Version number (e.g. `1.1`) | | platform | `IOS`, `MAC_OS`, or `TV_OS` | | appStoreState | `PREPARE_FOR_SUBMISSION`, `READY_FOR_SALE`, `REJECTED`, etc. | | releaseType | `MANUAL`, `AFTER_APPROVAL`, `SCHEDULED` | | earliestReleaseDate | Scheduled release date (if applicable) | | createdDate | When the version was created | *** ## Create Version ``` POST /v1/connect/apps/:appId/versions ``` Create a new App Store version for an app. ### Request Body ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "versionString": "1.2", "platform": "IOS" } ``` | Field | Type | Required | Description | | ------------- | ------ | -------- | ---------------------------------- | | versionString | string | Yes | Version number (e.g. `1.2`, `2.0`) | | platform | string | Yes | `IOS`, `MAC_OS`, or `TV_OS` | *** ## Update Version ``` PATCH /v1/connect/versions/:versionId ``` Update version-level attributes. ### Request Body ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "attributes": { "versionString": "1.2", "copyright": "2026 Your Company", "releaseType": "MANUAL", "earliestReleaseDate": "2026-03-15T00:00:00Z", "downloadable": true } } ``` | Attribute | Type | Description | | ------------------- | ------- | --------------------------------------- | | versionString | string | Version number | | copyright | string | Copyright notice | | releaseType | string | `MANUAL`, `AFTER_APPROVAL`, `SCHEDULED` | | earliestReleaseDate | string | ISO 8601 date for scheduled release | | downloadable | boolean | Whether the version is downloadable | *** ## Code Example ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} # Find app by bundle ID curl -X GET "https://api.appeeky.com/v1/connect/apps?bundleId=app.taperecord" \ -H "X-API-Key: YOUR_APEEKY_KEY" \ -H "X-ASC-Issuer-Id: YOUR_ISSUER_ID" \ -H "X-ASC-Key-Id: YOUR_KEY_ID" \ -H "X-ASC-Private-Key: YOUR_PRIVATE_KEY" # List versions curl -X GET "https://api.appeeky.com/v1/connect/apps/6759740679/versions" \ -H "X-API-Key: YOUR_APEEKY_KEY" \ -H "X-ASC-Issuer-Id: YOUR_ISSUER_ID" \ -H "X-ASC-Key-Id: YOUR_KEY_ID" \ -H "X-ASC-Private-Key: YOUR_PRIVATE_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const headers = { "X-API-Key": "YOUR_APEEKY_KEY", "X-ASC-Issuer-Id": "YOUR_ISSUER_ID", "X-ASC-Key-Id": "YOUR_KEY_ID", "X-ASC-Private-Key": process.env.ASC_PRIVATE_KEY, }; // List apps const apps = await fetch("https://api.appeeky.com/v1/connect/apps?bundleId=app.taperecord", { headers, }).then((r) => r.json()); // List versions const appId = apps.data.data[0].id; const versions = await fetch( `https://api.appeeky.com/v1/connect/apps/${appId}/versions`, { headers } ).then((r) => r.json()); ``` # Metadata (Localizations) Source: https://docs.appeeky.com/docs/app-store-connect-metadata Read and update app store metadata — description, keywords, what's new, support URL App Store metadata is stored per **locale** (language) and per **version**. Use version localizations to manage description, keywords, promotional text, what's new, support URL, and marketing URL. `name` and `subtitle` in the **App Information** tab are not version-localization fields.\ Use App Info localization endpoints instead: * `GET /v1/connect/apps/:appId/app-infos` * `GET /v1/connect/app-infos/:appInfoId/localizations` * `PATCH /v1/connect/app-info-localizations/:localizationId` Most metadata can only be updated when the app is in an **editable state** (e.g. `PREPARE_FOR_SUBMISSION`). **Promotional text** can be updated at any time, even when the app is live. *** ## List Localizations ``` GET /v1/connect/versions/:versionId/localizations ``` List all localizations (languages) for an App Store version. Use the version ID from [List Versions](/docs/app-store-connect-apps-versions#list-versions). ### Path Parameters | Name | Type | Description | | --------- | ------ | ------------------------------------------------------------------ | | versionId | string | App Store version ID (e.g. `c2db5998-ac17-47b5-a61a-3658540592f8`) | ### Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "data": [ { "type": "appStoreVersionLocalizations", "id": "e3c96adf-bba6-4459-8c3a-ed9c54f81af8", "attributes": { "locale": "en-US", "description": "Tape Record turns your voice into beautifully styled recordings...", "keywords": "record, voice, audio, cassette", "promotionalText": null, "whatsNew": "* Bug fixes\n* Performance improvements", "supportUrl": "https://webmob.ee/support", "marketingUrl": null } } ] } } ``` ### Attribute Reference | Field | Description | Editable When | | --------------- | --------------------------------------------- | --------------- | | description | Full app description (max 4000 chars) | Editable state | | keywords | Comma-separated keywords (max 100 chars) | Editable state | | promotionalText | Promotional text (max 170 chars) | **Anytime** | | whatsNew | "What's New in This Version" (max 4000 chars) | Editable state | | supportUrl | Support URL | Editable state | | marketingUrl | Marketing URL | Editable state | | locale | Language (e.g. `en-US`, `tr`) | Set at creation | *** ## Get Localization ``` GET /v1/connect/localizations/:localizationId ``` Get a single localization's metadata. *** ## Update Localization ``` PATCH /v1/connect/localizations/:localizationId ``` Update metadata for a localization. Send only the attributes you want to change. ### Request Body ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "attributes": { "description": "Updated app description...", "keywords": "record, voice, audio, tape, memo", "whatsNew": "* Bug fixes\n* New tape styles", "promotionalText": "Try the new neon tape!", "supportUrl": "https://example.com/support", "marketingUrl": "https://example.com" } } ``` | Attribute | Type | Max Length | | --------------- | ------ | ---------- | | description | string | 4000 | | keywords | string | 100 | | promotionalText | string | 170 | | whatsNew | string | 4000 | | supportUrl | string | — | | marketingUrl | string | — | **Keywords**: Use commas to separate. No spaces after commas. Apple recommends 5–10 high-value keywords. Avoid duplication with your app name and subtitle. *** ## Create Localization ``` POST /v1/connect/versions/:versionId/localizations ``` Add a new language to a version (the version must be in an editable state). ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "locale": "de-DE", "attributes": { "description": "Tape Record verwandelt deine Stimme...", "keywords": "aufnahme,stimme,audio,kassette", "whatsNew": "* Fehlerbehebungen", "supportUrl": "https://webmob.ee/support" } } ``` `locale` is required; all attributes are optional and can be patched later. For **App Information** languages (name, subtitle), use `POST /v1/connect/app-infos/:appInfoId/localizations` with `{ "locale": "de-DE", "attributes": { "name": "...", "subtitle": "..." } }`. *** ## Delete Localization ``` DELETE /v1/connect/localizations/:localizationId DELETE /v1/connect/app-info-localizations/:localizationId ``` Removes a language from the version or app info. Only possible while the version is editable. *** ## Bulk Localization Workflow For translating an entire version in one call (all locales, AI-assisted), see the higher-level endpoints: ``` GET /v1/connect/localizations/live → current live metadata for all locales POST /v1/connect/localizations/pull → pull App Store metadata into Appeeky POST /v1/connect/localizations/publish → publish translated metadata to App Store POST /v1/connect/localizations/remove → remove locales in bulk ``` *** ## Code Example ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} # List localizations for a version curl -X GET "https://api.appeeky.com/v1/connect/versions/c2db5998-ac17-47b5-a61a-3658540592f8/localizations" \ -H "X-API-Key: YOUR_APEEKY_KEY" \ -H "X-ASC-Issuer-Id: YOUR_ISSUER_ID" \ -H "X-ASC-Key-Id: YOUR_KEY_ID" \ -H "X-ASC-Private-Key: YOUR_PRIVATE_KEY" # Update keywords curl -X PATCH "https://api.appeeky.com/v1/connect/localizations/e3c96adf-bba6-4459-8c3a-ed9c54f81af8" \ -H "X-API-Key: YOUR_APEEKY_KEY" \ -H "X-ASC-Issuer-Id: YOUR_ISSUER_ID" \ -H "X-ASC-Key-Id: YOUR_KEY_ID" \ -H "X-ASC-Private-Key: YOUR_PRIVATE_KEY" \ -H "Content-Type: application/json" \ -d '{"attributes":{"keywords":"record,voice,audio,cassette,memo"}}' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} // Update promotional text (can be done anytime, even when app is live) const response = await fetch( "https://api.appeeky.com/v1/connect/localizations/e3c96adf-bba6-4459-8c3a-ed9c54f81af8", { method: "PATCH", headers: { "X-API-Key": "YOUR_APEEKY_KEY", "X-ASC-Issuer-Id": "YOUR_ISSUER_ID", "X-ASC-Key-Id": "YOUR_KEY_ID", "X-ASC-Private-Key": process.env.ASC_PRIVATE_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ attributes: { promotionalText: "New: Neon tape style and dark mode!", }, }), } ); ``` # App Store Connect Metrics Source: https://docs.appeeky.com/docs/app-store-connect-metrics Sales and Trends data synced from your connected ASC account — downloads, revenue, IAP, subscriptions The App Store Connect Metrics API returns **aggregated sales data** from your connected App Store Connect account. Data is synced daily from Apple's Sales and Trends reports into Appeeky — no need to pass ASC credentials on each request. **Requires connected ASC account.** Connect your App Store Connect API key and Vendor Number in [appeeky.com → Settings → Integrations](https://appeeky.com). Data syncs nightly and on manual sync. **Pro feature** — requires Indie plan or higher. API key path: **2 credits** per request. JWT (web): plan check only. *** ## Overview | Endpoint | Description | | ------------------------------------- | ----------------------------------------------- | | `GET /v1/connect/metrics` | Overview: totals, per-app breakdown, daily rows | | `GET /v1/connect/metrics/apps` | List app IDs that have metrics data | | `GET /v1/connect/metrics/apps/:appId` | Single app: daily series + country breakdown | ### Data available * **App Units** (downloads) * **Revenue** (USD) * **In-App Purchases** (count) * **Subscriptions** (snapshot) * **Free Trials** (snapshot) * **Country/Territory** breakdown (per-app detail only) Sync fetches up to **90 days** of data. Use `from` and `to` to filter the range. ## Get Overview Metrics ``` GET /v1/connect/metrics ``` Returns totals and per-app breakdown for the date range. ### Query parameters | Name | Type | Default | Description | | ------- | ------ | ----------- | --------------------------- | | `from` | string | 30 days ago | Start date `YYYY-MM-DD` | | `to` | string | today | End date `YYYY-MM-DD` | | `appId` | string | — | Optional: filter to one app | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/metrics?from=2026-02-19&to=2026-03-21" \ -H "X-API-Key: apk_your_key_here" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/connect/metrics?from=2026-02-19&to=2026-03-21", { headers: { "X-API-Key": "apk_your_key_here" } } ); const { data } = await res.json(); ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "from": "2026-02-19", "to": "2026-03-21", "totals": { "downloads": 1250, "revenue": 89.5, "subscriptions": 42, "trials": 12, "iap_count": 320 }, "apps": [ { "appId": "6759740679", "appName": "Voice Tape Record", "downloads": 1100, "revenue": 75.2, "subscriptions": 38, "trials": 10, "iap_count": 280 } ], "rows": [ { "app_apple_id": "6759740679", "app_name": "Voice Tape Record", "metric_date": "2026-03-20", "downloads": 45, "revenue": 3.2, "subscriptions": 2, "trials": 0, "iap_count": 12 } ] } } ``` *** ## List Apps with Metrics ``` GET /v1/connect/metrics/apps ``` Returns distinct app IDs that have metrics data (for linking to app detail). ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/metrics/apps" \ -H "X-API-Key: apk_your_key_here" ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": [ { "app_apple_id": "6759740679", "app_name": "Voice Tape Record" } ] } ``` *** ## Get App Detail (Daily + Countries) ``` GET /v1/connect/metrics/apps/:appId ``` Returns daily time series and country/territory breakdown for a single app. ### Path parameters | Name | Description | | ------- | ---------------------------------- | | `appId` | App Store Connect app ID (numeric) | ### Query parameters | Name | Type | Default | Description | | ------ | ------ | ----------- | ----------------------- | | `from` | string | 90 days ago | Start date `YYYY-MM-DD` | | `to` | string | today | End date `YYYY-MM-DD` | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/metrics/apps/6759740679?from=2026-02-19&to=2026-03-21" \ -H "X-API-Key: apk_your_key_here" ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "6759740679", "appName": "Voice Tape Record", "from": "2026-02-19", "to": "2026-03-21", "totals": { "downloads": 1100, "revenue": 75.2, "subscriptions": 38, "trials": 10, "iap_count": 280 }, "daily": [ { "metric_date": "2026-03-20", "downloads": 45, "revenue": 3.2, "subscriptions": 2, "trials": 0, "iap_count": 12 } ], "countries": [ { "country": "US", "downloads": 520, "revenue": 42.1 }, { "country": "GB", "downloads": 180, "revenue": 12.5 } ] } } ``` *** ## Error Codes | Status | Code | When | | ------ | --------------------- | -------------------------------------- | | 401 | `MISSING_AUTH` | No Authorization or X-API-Key header | | 401 | `INVALID_TOKEN` | Invalid or expired JWT | | 401 | `INVALID_API_KEY` | Invalid or inactive API key | | 403 | `PRO_FEATURE` | Free plan — upgrade to Indie or higher | | 404 | `USER_NOT_FOUND` | User not in system (connect ASC first) | | 429 | `RATE_LIMIT_EXCEEDED` | Not enough credits (2 per request) | | 500 | `METRICS_ERROR` | Database or sync error | *** # Monetization & Pricing Source: https://docs.appeeky.com/docs/app-store-connect-monetization In-app purchases, subscriptions, price schedules, and territory availability Create and manage in-app purchases, auto-renewable subscriptions, app pricing, and territory availability. All endpoints require [App Store Connect authentication](/docs/app-store-connect-overview#authentication). Price-schedule replacements are destructive and require an explicit confirmation flag. *** ## In-App Purchases (v2) ### List / Get / Create / Update ``` GET /v1/connect/apps/:appId/iaps?type=CONSUMABLE&state=... POST /v1/connect/apps/:appId/iaps GET /v1/connect/iaps/:iapId PATCH /v1/connect/iaps/:iapId ``` ### Create Body ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "name": "500 Coins", "productId": "com.example.coins500", "inAppPurchaseType": "CONSUMABLE", "reviewNote": "Grants 500 coins", "familySharable": false } ``` `inAppPurchaseType` is one of `CONSUMABLE`, `NON_CONSUMABLE`, `NON_RENEWING_SUBSCRIPTION`. Territory availability is managed separately via [App Availability](#app--territory-availability) — Apple no longer accepts an `availableInAllTerritories` attribute. ### IAP Localizations ``` GET /v1/connect/iaps/:iapId/localizations POST /v1/connect/iaps/:iapId/localizations Body: { "locale": "de-DE", "name": "500 Münzen", "description": "..." } PATCH /v1/connect/iap-localizations/:localizationId ``` ### IAP Pricing ``` GET /v1/connect/iaps/:iapId/price-points?territory=USA GET /v1/connect/iaps/:iapId/price-schedule POST /v1/connect/iaps/:iapId/price-schedule ``` Replacing a price schedule **overwrites all manual prices**, so it requires `confirmReplaceAll: true`: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "baseTerritory": "USA", "confirmReplaceAll": true, "prices": [ { "territory": "USA", "pricePointId": "eyJzIjoi..." } ] } ``` The base-territory price without a `startDate` is the current price. Get `pricePointId` values from the price-points endpoint. *** ## Subscriptions ### Groups ``` GET /v1/connect/apps/:appId/subscription-groups POST /v1/connect/apps/:appId/subscription-groups Body: { "referenceName": "Premium" } ``` ### Subscriptions in a Group ``` GET /v1/connect/subscription-groups/:groupId/subscriptions POST /v1/connect/subscription-groups/:groupId/subscriptions GET /v1/connect/subscriptions/:subscriptionId PATCH /v1/connect/subscriptions/:subscriptionId ``` ### Create Body ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "name": "Premium Monthly", "productId": "com.example.premium.monthly", "subscriptionPeriod": "ONE_MONTH", "groupLevel": 1, "familySharable": false, "reviewNote": "Unlocks all tape styles" } ``` `subscriptionPeriod`: `ONE_WEEK`, `ONE_MONTH`, `TWO_MONTHS`, `THREE_MONTHS`, `SIX_MONTHS`, `ONE_YEAR`. ### Subscription Localizations ``` GET /v1/connect/subscriptions/:subscriptionId/localizations POST /v1/connect/subscriptions/:subscriptionId/localizations PATCH /v1/connect/subscription-localizations/:localizationId ``` ### Subscription Pricing ``` GET /v1/connect/subscriptions/:subscriptionId/price-points?territory=USA POST /v1/connect/subscriptions/:subscriptionId/prices ``` ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "territory": "USA", "pricePointId": "eyJzIjoi...", "startDate": "2026-08-01", "preserveCurrentPrice": true, "confirm": true } ``` `preserveCurrentPrice: true` keeps the old price for existing subscribers (grandfathering). Omit `startDate` to change the price as soon as possible. *** ## App & Territory Availability ### Get Availability ``` GET /v1/connect/apps/:appId/availability ``` Returns `availableInNewTerritories` plus the first 50 territory availabilities (Apple's include cap). ### Update Defaults ``` PATCH /v1/connect/availabilities/:availabilityId Body: { "availableInNewTerritories": true, "confirm": true } ``` Apple's v2 availability resource has no direct PATCH — Appeeky reads your current per-territory configuration and POSTs a full replacement that preserves it, changing only `availableInNewTerritories`. The availability ID equals the app ID. ### Toggle One Territory ``` PATCH /v1/connect/territory-availabilities/:territoryAvailabilityId Body: { "available": false, "confirm": true } ``` Territory availability IDs come from the Get Availability response. *** ## App Pricing ``` GET /v1/connect/apps/:appId/price-points?territory=USA GET /v1/connect/apps/:appId/price-schedule POST /v1/connect/apps/:appId/price-schedule ``` Replacing the app price schedule uses the same body shape as IAP price schedules (`baseTerritory`, `prices`, `confirmReplaceAll: true`). ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} # Find the price point for $2.99 in the US curl -X GET "https://api.appeeky.com/v1/connect/apps/6759740679/price-points?territory=USA" \ -H "X-API-Key: YOUR_APEEKY_KEY" \ -H "X-ASC-Issuer-Id: YOUR_ISSUER_ID" \ -H "X-ASC-Key-Id: YOUR_KEY_ID" \ -H "X-ASC-Private-Key: YOUR_PRIVATE_KEY" ``` *** ## Role Requirements | Action | Minimum Role | | -------------------------------- | ------------ | | IAPs, subscriptions (read/write) | App Manager | | Price schedules | App Manager | | Availability | App Manager | For revenue **analytics** (MRR, churn, ARPU), see [Subscription Metrics](/docs/app-store-connect-subscription-metrics) — synced daily from your connected account with no extra credentials per call. # App Store Connect Overview Source: https://docs.appeeky.com/docs/app-store-connect-overview Authenticate with your Apple credentials and manage apps, versions, metadata, and reports The App Store Connect API lets you automate tasks you normally do in [App Store Connect](https://appstoreconnect.apple.com): list your apps, manage versions, update metadata (description, keywords, what's new), and download analytics or sales reports. You provide your own **App Store Connect API key** via request headers. Appeeky proxies your requests to Apple's API—your private key never leaves your request and is not stored. *** ## Authentication All App Store Connect endpoints require these **headers** in addition to your Appeeky API key: | Header | Required | Description | | ----------------------- | -------- | ------------------------------------------------------------------------------------------------------ | | `X-ASC-Issuer-Id` | Yes | Your Issuer ID (UUID from [Users and Access > Keys](https://appstoreconnect.apple.com/access/api)) | | `X-ASC-Key-Id` | Yes | Your Key ID (e.g. `Y38QBZP2W9`) | | `X-ASC-Private-Key` | Yes\* | Your private key (PEM format, including `-----BEGIN PRIVATE KEY-----` and `-----END PRIVATE KEY-----`) | | `X-ASC-Private-Key-B64` | Yes\* | Base64-encoded private key (alternative to `X-ASC-Private-Key`) | \* Provide either `X-ASC-Private-Key` or `X-ASC-Private-Key-B64` ### Creating API Keys 1. Go to [App Store Connect](https://appstoreconnect.apple.com) → **Users and Access** → **Keys** 2. Create a new key with the required roles (e.g. **Admin** for full access, **App Manager** for app/version management) 3. Download the `.p8` file **once** — you cannot download it again 4. Note your **Issuer ID** and **Key ID** from the Keys page ### cURL Example ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/connect/apps?bundleId=app.taperecord" \ -H "X-API-Key: YOUR_APEEKY_KEY" \ -H "X-ASC-Issuer-Id: 2a40fc77-6d22-4bbd-8dad-28de4f304383" \ -H "X-ASC-Key-Id: Y38QBZP2W9" \ -H "X-ASC-Private-Key: -----BEGIN PRIVATE KEY----- MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg... -----END PRIVATE KEY-----" ``` If sending the private key in JSON or a tool that escapes newlines, you can use `\n` instead of actual line breaks. The API normalizes both formats. *** ## Endpoint Summary | Category | Endpoints | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Apps** | [List apps](/docs/app-store-connect-apps-versions#list-apps), [Get app](/docs/app-store-connect-apps-versions#get-app), [List app infos](/docs/app-store-connect-apps-versions#list-app-infos) | | **Versions** | [List versions](/docs/app-store-connect-apps-versions#list-versions), [Create version](/docs/app-store-connect-apps-versions#create-version), [Update version](/docs/app-store-connect-apps-versions#update-version) | | **App Information** | [List app info localizations](/docs/app-store-connect-apps-versions#list-app-info-localizations), [Update app info localization](/docs/app-store-connect-apps-versions#update-app-info-localization) | | **Metadata** | [List](/docs/app-store-connect-metadata#list-localizations), [Update](/docs/app-store-connect-metadata#update-localization), [Create](/docs/app-store-connect-metadata#create-localization), [Delete](/docs/app-store-connect-metadata#delete-localization) localizations | | **Release** | [Screenshots](/docs/app-store-connect-release#screenshots), [Builds](/docs/app-store-connect-release#builds), [Review details](/docs/app-store-connect-release#app-review-details), [Submit for review](/docs/app-store-connect-release#review-submission), [Phased release](/docs/app-store-connect-release#phased-release) | | **TestFlight** | [Beta groups](/docs/app-store-connect-testflight#beta-groups), [Testers](/docs/app-store-connect-testflight#beta-testers), [Test notes](/docs/app-store-connect-testflight#testflight-metadata), [Beta review](/docs/app-store-connect-testflight#external-beta-review) | | **Monetization** | [In-app purchases](/docs/app-store-connect-monetization#in-app-purchases-v2), [Subscriptions](/docs/app-store-connect-monetization#subscriptions), [Pricing](/docs/app-store-connect-monetization#app-pricing), [Availability](/docs/app-store-connect-monetization#app--territory-availability) | | **Metrics** | [Overview & app detail](/docs/app-store-connect-metrics) — synced Sales & Trends (0 credits) | | **Analytics** | [Report requests](/docs/app-store-connect-analytics), [Reports → Instances → Segments](/docs/app-store-connect-analytics#download-report-data), [Segment download & parse](/docs/app-store-connect-analytics) | | **Sales Reports** | [Download Sales and Trends reports](/docs/app-store-connect-sales-reports) — gzip TSV or parsed JSON | | **Reviews** | [List reviews](/docs/app-store-connect-reviews#list-customer-reviews), [Respond to review](/docs/app-store-connect-reviews#respond-to-review) | For **paid App Store search campaigns**, see [Apple Search Ads](/docs/apple-search-ads) — separate API key; full campaign, keyword, negative keyword, and report management. *** ## Typical Workflow ### 1. Find your app ``` GET /v1/connect/apps?bundleId=app.taperecord → Returns app ID (e.g. 6759740679) ``` ### 2. Get versions and metadata ``` GET /v1/connect/apps/6759740679/versions → Returns version IDs (e.g. c2db5998-ac17-47b5-a61a-3658540592f8) GET /v1/connect/versions/c2db5998.../localizations → Returns localization IDs with description, keywords, whatsNew, etc. ``` ### 3. Update metadata (description, keywords) ``` PATCH /v1/connect/localizations/e3c96adf... Body: { "attributes": { "keywords": "record, voice, audio, tape" } } ``` ### 3b. Update App Information subtitle/name ``` GET /v1/connect/apps/6759740679/app-infos GET /v1/connect/app-infos/:appInfoId/localizations PATCH /v1/connect/app-info-localizations/:localizationId Body: { "attributes": { "subtitle": "Audio recording & a retro VHS" } } ``` ### 4. Get analytics ``` GET /v1/connect/apps/6759740679/analytics/report-requests → Create one if empty: POST /v1/connect/apps/6759740679/analytics/report-requests GET /v1/connect/analytics/report-requests/:id/reports → Pick a report (e.g. App Downloads Standard) GET /v1/connect/analytics/reports/:reportId/instances GET /v1/connect/analytics/report-instances/:instanceId/segments → Each segment has a URL to download the data (gzip TSV) ``` *** ## Error Codes | Status | Code | When | | ------ | -------------------------- | ----------------------------------------------------------- | | 400 | `ASC_CREDENTIALS_REQUIRED` | Missing X-ASC-Issuer-Id, X-ASC-Key-Id, or X-ASC-Private-Key | | 400 | `INVALID_APP_ID` | App ID missing or invalid | | 401 | (from Apple) | Invalid or expired API key | | 403 | (from Apple) | Key lacks required role | | 404 | (from Apple) | Resource not found | | 502 | `ASC_API_ERROR` | Apple API returned an error | *** ## Role Requirements | Action | Minimum Role | | ------------------------- | -------------------------------------------------------- | | List apps, get app | App Manager | | Versions (create, update) | App Manager | | Metadata (localizations) | App Manager | | Analytics reports | Admin, Finance, or Sales and Reports | | Sales reports | Admin, Finance, or Sales and Reports (Team key required) | **Individual** Apple Developer accounts cannot use Sales and Trends reports—only **Team** (Organization) keys. # Release Management Source: https://docs.appeeky.com/docs/app-store-connect-release Screenshots, builds, App Review details, review submission, and phased release — ship a version end-to-end Manage the full App Store release pipeline: upload screenshots, attach a build, fill in App Review contact details, submit for review, and control the phased rollout — all without opening App Store Connect. All endpoints require [App Store Connect authentication](/docs/app-store-connect-overview#authentication) (your own ASC API key via headers, or a connected account). Destructive operations require `"confirm": true` in the request body. *** ## Screenshots Screenshots live in **screenshot sets**, one set per device display type per locale. ### Supported Display Types | Display Type | Devices | Accepted Sizes (portrait) | | ----------------------- | ------------------------- | ------------------------------- | | `APP_IPHONE_67` | iPhone 16/15/14 Pro Max | 1320x2868, 1290x2796, 1260x2736 | | `APP_IPHONE_65` | iPhone 11 Pro Max, XS Max | 1284x2778, 1242x2688 | | `APP_IPHONE_61` | iPhone 16/15 Pro, 14, 13 | 1206x2622, 1179x2556 | | `APP_IPAD_PRO_3GEN_129` | iPad Pro 12.9" / 13" | 2064x2752, 2048x2732 | | `APP_IPAD_PRO_3GEN_11` | iPad Pro 11" | 1668x2388, 1640x2360 | Landscape variants (width/height swapped) are also accepted. Dimensions are validated **before** upload so you get a clear error instead of a stuck asset. ### List Screenshot Sets ``` GET /v1/connect/localizations/:localizationId/screenshot-sets?displayType=APP_IPHONE_67 ``` Use the localization ID from [List Localizations](/docs/app-store-connect-metadata#list-localizations). `displayType` is optional. ### Create Screenshot Set ``` POST /v1/connect/localizations/:localizationId/screenshot-sets Body: { "displayType": "APP_IPHONE_67" } ``` ### List Screenshots in a Set ``` GET /v1/connect/screenshot-sets/:screenshotSetId/screenshots ``` ### Upload Screenshot ``` POST /v1/connect/screenshot-sets/:screenshotSetId/screenshots ``` Two ways to provide the image: 1. **Multipart upload** — send the file in a `screenshot` form field (PNG, JPEG, or WebP; max 10 MB) 2. **Public URL** — JSON body `{ "imageUrl": "https://...", "fileName": "01-hero.png" }` (HTTPS only, max 20 MB) The image is normalized to opaque PNG (alpha removed) and its dimensions validated against the set's display type. Appeeky then performs Apple's full 3-step upload for you: asset reservation, chunked byte upload, and MD5-verified commit. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} # Upload from a public URL curl -X POST "https://api.appeeky.com/v1/connect/screenshot-sets/SET_ID/screenshots" \ -H "X-API-Key: YOUR_APEEKY_KEY" \ -H "X-ASC-Issuer-Id: YOUR_ISSUER_ID" \ -H "X-ASC-Key-Id: YOUR_KEY_ID" \ -H "X-ASC-Private-Key: YOUR_PRIVATE_KEY" \ -H "Content-Type: application/json" \ -d '{"imageUrl": "https://cdn.example.com/shots/de-01.png", "fileName": "de-01.png"}' ``` ### Reorder Screenshots ``` PATCH /v1/connect/screenshot-sets/:screenshotSetId/screenshots/order Body: { "screenshotIds": ["id-1", "id-2", "id-3"] } ``` ### Delete Screenshot ``` DELETE /v1/connect/screenshots/:screenshotId Body: { "confirm": true } ``` *** ## Builds ### List Builds ``` GET /v1/connect/apps/:appId/builds ``` | Query Param | Description | | ------------------- | ------------------------------------------ | | `limit` | Max results (default 50, max 200) | | `version` | Filter by build number | | `preReleaseVersion` | Filter by marketing version (e.g. `1.4.0`) | | `processingState` | `PROCESSING`, `FAILED`, `INVALID`, `VALID` | | `expired` | `true` / `false` | | `cursor` | Pagination cursor | ### Get Build / Version Build ``` GET /v1/connect/builds/:buildId GET /v1/connect/versions/:versionId/build ``` ### Attach Build to Version ``` PATCH /v1/connect/versions/:versionId/build Body: { "buildId": "BUILD_ID" } ``` ### Detach Build ``` DELETE /v1/connect/versions/:versionId/build Body: { "confirm": true } ``` *** ## App Review Details Contact info, demo account, and notes shown to Apple's review team. ``` GET /v1/connect/versions/:versionId/review-detail POST /v1/connect/versions/:versionId/review-detail PATCH /v1/connect/review-details/:reviewDetailId ``` ### Attributes ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "attributes": { "contactFirstName": "Eren", "contactLastName": "Arica", "contactPhone": "+1 555 000 0000", "contactEmail": "review@example.com", "demoAccountRequired": true, "demoAccountName": "demo@example.com", "demoAccountPassword": "secret", "notes": "Tap the tape icon to start recording." } } ``` If `demoAccountRequired` is `true`, `demoAccountName` and `demoAccountPassword` are validated as required. *** ## Review Submission Uses Apple's current **reviewSubmissions** flow (the legacy `appStoreVersionSubmissions` resource was removed by Apple). ### Get Open Submissions ``` GET /v1/connect/versions/:versionId/submission ``` Returns the app's open review submissions (`READY_FOR_REVIEW`, `WAITING_FOR_REVIEW`, `IN_REVIEW`, `UNRESOLVED_ISSUES`) with their items. ### Submit for Review ``` POST /v1/connect/versions/:versionId/submission Body: { "confirm": true } ``` Behind the scenes this: verifies the version is `PREPARE_FOR_SUBMISSION` or `DEVELOPER_REJECTED`, reuses or creates a review submission, adds the version as a submission item, and marks the submission as submitted. Returns `409 ASC_SUBMISSION_ALREADY_IN_REVIEW` if another submission is already in flight. ### Cancel Submission ``` DELETE /v1/connect/submissions/:submissionId Body: { "confirm": true } ``` Cancels a submitted review submission (sets `canceled: true` on Apple's side). *** ## Phased Release Gradual rollout to users with automatic updates enabled over 7 days (1% → 2% → 5% → 10% → 20% → 50% → 100%). ``` GET /v1/connect/versions/:versionId/phased-release POST /v1/connect/versions/:versionId/phased-release PATCH /v1/connect/phased-releases/:phasedReleaseId DELETE /v1/connect/phased-releases/:phasedReleaseId ``` ### Update State ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "state": "PAUSED" } ``` | State | Effect | | ---------- | ------------------------------------------------------------ | | `ACTIVE` | Resume rollout | | `PAUSED` | Pause rollout (max 30 days total) | | `COMPLETE` | Release to 100% immediately — **requires** `"confirm": true` | | `INACTIVE` | Only valid before release | Delete requires `"confirm": true` and only works before the rollout begins. *** ## Typical Release Workflow ``` 1. GET /connect/apps/:appId/builds?processingState=VALID → pick latest build 2. PATCH /connect/versions/:versionId/build → attach build 3. POST /connect/localizations/:locId/screenshot-sets → per locale + device 4. POST /connect/screenshot-sets/:setId/screenshots → upload images 5. POST /connect/versions/:versionId/review-detail → review contact info 6. POST /connect/versions/:versionId/phased-release → optional staged rollout 7. POST /connect/versions/:versionId/submission {confirm:true} → submit to Apple ``` ## Role Requirements | Action | Minimum Role | | ----------------------------------- | ------------ | | Screenshots, builds, review details | App Manager | | Submit / cancel review submission | App Manager | | Phased release | App Manager | # Customer Reviews Source: https://docs.appeeky.com/docs/app-store-connect-reviews Search reviews with rich filters, get summary stats, and post or delete developer responses — all backed by a synced copy of your App Store Connect data Manage your App Store customer reviews end-to-end from a single API. Reviews are kept in sync with App Store Connect on a daily cadence and on demand, so you can search and filter them by rating, country, response state, or full-text query without paginating Apple's API on every request. **Requires connected ASC account.** Connect your App Store Connect API key and Vendor Number in [appeeky.com → Settings → Integrations](https://appeeky.com), or pass `X-ASC-*` headers per request. Reviews are synced daily and right after you connect. **Pro feature** — requires Indie plan or higher. *** ## Overview | Endpoint | Description | | ------------------------------------------------ | -------------------------------------------------------------------------- | | `GET /v1/connect/reviews` | Search reviews with filters (rating, territory, response state, full-text) | | `GET /v1/connect/reviews/summary` | Aggregated stats: avg rating, distribution, response rate, by-territory | | `POST /v1/connect/reviews/{reviewId}/response` | Create or update your reply to a review | | `DELETE /v1/connect/reviews/{reviewId}/response` | Delete your reply to a review | | `POST /v1/connect/reviews/{reviewId}/refresh` | Force-refresh one review from Apple (e.g. right after replying) | | `POST /v1/connect/reviews/sync` | Trigger an immediate background sync of all reviews | ### How freshness works * Reviews refresh automatically **every day**. * Posting or deleting a reply writes to Apple **synchronously** and updates your local copy in the same request, so the next list call reflects the change immediately. * Use `POST /v1/connect/reviews/{reviewId}/refresh` to pull the latest state of a single review from Apple on demand (useful right after replying to confirm the response was published). * Use `POST /v1/connect/reviews/sync` to trigger a full background refresh of every review. *** ## Search Reviews ``` GET /v1/connect/reviews ``` Returns reviews sorted by newest first. All filter parameters are optional and combinable — for example, "all unanswered 1-2 star reviews from the US that mention 'crash'" is a single request. ### Query parameters | Name | Type | Default | Description | | ------------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------- | | `appId` | string | — | Filter by App Store Connect app ID. Omit to search across all your connected apps. | | `rating` | number | — | Filter by exact star rating (1–5). | | `territory` | string | — | ISO 3-letter country code (e.g. `USA`, `GBR`, `DEU`). | | `hasResponse` | boolean | — | `true` for answered reviews only, `false` for unanswered only. Omit for both. | | `q` | string | — | Case-insensitive substring match in review title or body. | | `limit` | number | 50 | Max results (1–200). | | `before` | string | — | ISO timestamp cursor — returns reviews created strictly before this date (use the oldest `review_created_at` from the previous page). | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} # Triage: unanswered 1-star reviews mentioning "crash" curl "https://api.appeeky.com/v1/connect/reviews?rating=1&hasResponse=false&q=crash" \ -H "X-API-Key: apk_your_key_here" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const params = new URLSearchParams({ appId: "6759740679", rating: "1", hasResponse: "false", q: "crash", limit: "50", }); const res = await fetch( `https://api.appeeky.com/v1/connect/reviews?${params}`, { headers: { "X-API-Key": process.env.APEEKY_KEY } } ); const { data } = await res.json(); ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "reviews": [ { "id": "00000000-0000-0000-0000-000000000abc", "app_apple_id": "6759740679", "rating": 1, "title": "Crashes on launch", "body": "Latest update crashes immediately when I open the app...", "reviewer_nickname": "MarcusJ", "territory": "USA", "app_version": "2.4.1", "review_created_at": "2026-04-22T14:18:00Z", "response_id": null, "response_body": null, "response_state": null, "response_last_modified": null } ], "count": 1 } } ``` | Field | Description | | ------------------------ | ------------------------------------------------------------------ | | `id` | Apple review ID — pass this to the response and refresh endpoints. | | `rating` | 1–5 stars. | | `title` / `body` | Review text. | | `reviewer_nickname` | Anonymous nickname chosen by the reviewer. | | `territory` | ISO 3-letter country code. | | `app_version` | App version the user reviewed. | | `review_created_at` | ISO timestamp the user submitted the review. | | `response_id` | `null` if you have not replied yet. | | `response_body` | Your last reply text (or `null`). | | `response_state` | `PUBLISHED`, `PENDING_PUBLISH`, or `null`. | | `response_last_modified` | ISO timestamp of your last reply update. | ### Pagination There is no `next` cursor in the response — pagination uses the `before` parameter. To get the next page: ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} const lastReviewDate = data.reviews[data.reviews.length - 1].review_created_at; const next = await fetch( `https://api.appeeky.com/v1/connect/reviews?limit=50&before=${encodeURIComponent(lastReviewDate)}`, { headers: { "X-API-Key": process.env.APEEKY_KEY } } ); ``` *** ## Reviews Summary ``` GET /v1/connect/reviews/summary ``` Aggregated statistics for an app (or all your apps). Useful for dashboards and quick health snapshots. ### Query parameters | Name | Type | Default | Description | | ------- | ------ | ------- | -------------------------------------------------------- | | `appId` | string | — | Filter by app. Omit to roll up across all your apps. | | `from` | string | — | ISO date — only consider reviews created on/after this. | | `to` | string | — | ISO date — only consider reviews created on/before this. | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/reviews/summary?appId=6759740679" \ -H "X-API-Key: apk_your_key_here" ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "6759740679", "from": null, "to": null, "total": 482, "avg_rating": 4.32, "unanswered": 67, "response_rate": 0.8610, "distribution": { "1": 22, "2": 14, "3": 38, "4": 110, "5": 298 }, "territories": [ { "territory": "USA", "count": 219, "avg_rating": 4.41 }, { "territory": "GBR", "count": 58, "avg_rating": 4.22 }, { "territory": "DEU", "count": 41, "avg_rating": 4.05 } ] } } ``` | Field | Description | | --------------- | --------------------------------------------------------------------------- | | `total` | Total review count in the window. | | `avg_rating` | Average star rating (1–5). | | `unanswered` | Reviews with no developer response. | | `response_rate` | `(total − unanswered) / total`, rounded to 4 decimals. | | `distribution` | Count per star rating. | | `territories` | Up to 50 territories sorted by review volume, each with its own avg rating. | *** ## Respond to a Review ``` POST /v1/connect/reviews/{reviewId}/response ``` Create or update your developer response. Apple does not allow editing a response in place — under the hood, this endpoint deletes the existing response and creates a new one when needed. You always make a single API call. ### Path parameters | Name | Description | | ---------- | ------------------------------------------------ | | `reviewId` | Apple review ID (`id` from the search response). | ### Request body ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "responseBody": "Thanks for the report — this is fixed in 2.4.2 (rolling out today). Please email support@example.com if you still see the issue." } ``` | Field | Type | Required | Description | | -------------- | ------ | -------- | -------------------------------------------------------------------------- | | `responseBody` | string | Yes | Your reply text. Non-empty, max **5,970 characters** (Apple's hard limit). | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/reviews/00000000-0000-0000-0000-000000000abc/response" \ -H "X-API-Key: apk_your_key_here" \ -H "Content-Type: application/json" \ -d '{"responseBody":"Thanks for the report — fixed in 2.4.2."}' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( `https://api.appeeky.com/v1/connect/reviews/${reviewId}/response`, { method: "POST", headers: { "X-API-Key": process.env.APEEKY_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ responseBody: "Thanks for the report — fixed in 2.4.2.", }), } ); ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "reviewId": "00000000-0000-0000-0000-000000000abc", "response": { "response_id": "11111111-1111-1111-1111-111111111def", "response_body": "Thanks for the report — fixed in 2.4.2.", "response_state": "PENDING_PUBLISH", "response_last_modified": "2026-04-25T22:01:09Z" } } } ``` Apple typically returns `PENDING_PUBLISH` initially and transitions to `PUBLISHED` once moderation completes (usually within minutes). Call the refresh endpoint below to see the published state without waiting for the next daily sync. *** ## Delete a Response ``` DELETE /v1/connect/reviews/{reviewId}/response ``` Removes your developer response. The review itself remains; only your reply is deleted. **Idempotent** — calling it on a review with no response returns `200` with `response: null`. ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X DELETE "https://api.appeeky.com/v1/connect/reviews/00000000-0000-0000-0000-000000000abc/response" \ -H "X-API-Key: apk_your_key_here" ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "reviewId": "00000000-0000-0000-0000-000000000abc", "response": null } } ``` *** ## Refresh One Review ``` POST /v1/connect/reviews/{reviewId}/refresh ``` Pulls the latest state of a single review directly from Apple. Use this right after posting a response to confirm the `PUBLISHED` transition, or any time you suspect a review is stale. ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/reviews/00000000-0000-0000-0000-000000000abc/refresh" \ -H "X-API-Key: apk_your_key_here" ``` **Response (200 OK):** the same review shape returned by `GET /v1/connect/reviews`. *** ## Trigger Background Sync ``` POST /v1/connect/reviews/sync ``` Enqueues a background job that pulls every new review since the last sync (or all reviews if `mode=full`). Useful when you want fresh data on demand without waiting for the daily refresh. Returns a `syncRunId` you can use to track the job. ### Query parameters | Name | Type | Default | Description | | ------ | ------ | ------------- | ------------------------------------------------------------------- | | `mode` | string | `incremental` | `incremental` for new reviews only, `full` for a complete backfill. | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/reviews/sync?mode=incremental" \ -H "X-API-Key: apk_your_key_here" ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "syncRunId": "run_abc123", "mode": "incremental" } } ``` Sync requests are deduplicated — clicking sync twice within 30 minutes returns the same `syncRunId`. *** ## Common Workflow: Triage Low-Rated Reviews ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} // 1. Find unanswered low-rated reviews const params = new URLSearchParams({ rating: "1", hasResponse: "false", limit: "50", }); const res = await fetch( `https://api.appeeky.com/v1/connect/reviews?${params}`, { headers: { "X-API-Key": process.env.APEEKY_KEY } } ); const { data } = await res.json(); // 2. Reply to each one for (const review of data.reviews) { await fetch( `https://api.appeeky.com/v1/connect/reviews/${review.id}/response`, { method: "POST", headers: { "X-API-Key": process.env.APEEKY_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ responseBody: "We're sorry to hear that. Please email support@example.com so we can help.", }), } ); } // 3. Confirm by checking the summary const summary = await fetch( "https://api.appeeky.com/v1/connect/reviews/summary", { headers: { "X-API-Key": process.env.APEEKY_KEY } } ).then((r) => r.json()); console.log(`${summary.data.unanswered} unanswered reviews remain`); ``` *** ## MCP Tools These endpoints are also exposed as MCP tools so AI assistants can triage reviews on your behalf: | Tool | Description | | ---------------------------- | ----------------------------------------------------------------------- | | `asc_search_reviews` | Search reviews with filters (rating, territory, hasResponse, full-text) | | `asc_reviews_summary` | Aggregated stats and response rate | | `asc_respond_to_review` | Post or update a developer response | | `asc_delete_review_response` | Delete a developer response | | `asc_refresh_review` | Refresh a single review from Apple | Example prompt: ``` Find every unanswered 1- or 2-star review for app 6759740679 from the last two weeks that mentions "crash" or "bug", and reply with an empathetic message that points to support@example.com. ``` *** ## Error Codes | Status | Code | When | | ------ | ----------------------------- | ----------------------------------------------------------- | | 400 | `INVALID_REVIEW_ID` | Missing `reviewId` path parameter | | 400 | `INVALID_RESPONSE_BODY` | Empty `responseBody` | | 400 | `RESPONSE_BODY_TOO_LONG` | Reply exceeds Apple's 5,970-character limit | | 400 | `ASC_CREDENTIALS_REQUIRED` | No App Store Connect credentials connected for your account | | 401 | `INVALID_API_KEY` | Invalid or inactive API key | | 403 | `PRO_FEATURE` | Free plan — upgrade to Indie or higher | | 404 | `REVIEW_NOT_FOUND` | Review does not exist or is not owned by your account | | 500 | `REVIEWS_FETCH_FAILED` | Database error | | 500 | `REVIEWS_SYNC_ENQUEUE_FAILED` | Could not enqueue sync job | *** ## Legacy Endpoints The original live-passthrough endpoints remain available for backwards compatibility: | Method | Path | Notes | | ------ | ------------------------------------------- | ------------------------------------------------------------------- | | `GET` | `/v1/connect/apps/{appId}/customer-reviews` | Live paginated read from Apple. No filters beyond pagination. | | `POST` | `/v1/connect/customer-reviews/response` | Body: `{ reviewId, responseBody }`. Does not handle response edits. | For all new integrations, prefer the endpoints above — they are faster, support filtering and aggregation, transparently handle response edits, and keep your local view in sync with Apple. # Sales and Trends Reports Source: https://docs.appeeky.com/docs/app-store-connect-sales-reports Download sales, installs, subscription, and financial reports The Sales and Trends API lets you download the same reports available in [App Store Connect > Sales and Trends](https://appstoreconnect.apple.com/sales/reports): sales summaries, installs, subscriptions, and more. Reports are returned as **gzip-compressed TSV** files. **Requires a Team (Organization) key** — Individual Apple Developer accounts cannot access Sales Reports. You also need your **Vendor Number** from [Payments and Financial Reports](https://appstoreconnect.apple.com/itc/payments_and_financial_reports) (the numeric Vendor #). *** ## Download Sales Report ``` GET /v1/connect/sales-reports ``` Download a Sales and Trends report. The response is a gzip file—save it and decompress to get the TSV. ### Required Headers | Header | Description | | ----------------- | ---------------------- | | X-ASC-Issuer-Id | Your Issuer ID | | X-ASC-Key-Id | Your Key ID | | X-ASC-Private-Key | Your private key (PEM) | ### Required Query Parameters | Name | Type | Description | | ------------ | ------ | ----------------------------------------------------------- | | vendorNumber | string | Your vendor number (from Payments and Financial Reports) | | reportDate | string | Date in format based on frequency (see below) | | reportType | string | Report type (default: `SALES`) | | frequency | string | `DAILY`, `WEEKLY`, `MONTHLY`, `YEARLY` (default: `MONTHLY`) | ### Optional Query Parameters | Name | Type | Default | Description | | ------------- | ------ | --------- | ------------------------------- | | reportSubType | string | `SUMMARY` | Usually `SUMMARY` or `DETAILED` | | version | string | `1_0` | Report version (varies by type) | ### Report Date Format | Frequency | Format | Example | | --------- | ------------ | ------------ | | DAILY | `YYYY-MM-DD` | `2026-03-08` | | WEEKLY | `YYYY-MM-DD` | `2026-03-02` | | MONTHLY | `YYYY-MM` | `2026-02` | | YEARLY | `YYYY` | `2026` | *** ## Report Types & Valid Combinations | reportType | reportSubType | frequency | version | | ------------------- | ---------------- | ------------------------------ | ---------- | | SALES | SUMMARY | DAILY, WEEKLY, MONTHLY, YEARLY | 1\_0 | | INSTALLS | SUMMARY | MONTHLY | 1\_2 | | INSTALLS | DETAILED | MONTHLY | 1\_2 | | INSTALLS | SUMMARY\_CHANNEL | YEARLY | 1\_0, 1\_1 | | SUBSCRIPTION | SUMMARY | DAILY | 1\_3 | | SUBSCRIBER | DETAILED | DAILY | 1\_3 | | SUBSCRIPTION\_EVENT | SUMMARY | DAILY | 1\_3 | | PRE\_ORDER | SUMMARY | DAILY, WEEKLY, MONTHLY, YEARLY | 1\_0 | See [Apple's documentation](https://developer.apple.com/documentation/AppStoreConnectAPI/GET-v1-salesReports) for the full matrix. *** ## Response The API returns the raw gzip file. Save it with a `.tsv.gz` extension and decompress: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} # Download and save curl -o sales-2026-02.tsv.gz "https://api.appeeky.com/v1/connect/sales-reports?vendorNumber=XXXXX&reportDate=2026-02&reportType=SALES&frequency=MONTHLY" \ -H "X-API-Key: YOUR_KEY" \ -H "X-ASC-Issuer-Id: ..." \ -H "X-ASC-Key-Id: ..." \ -H "X-ASC-Private-Key: ..." # Decompress gunzip sales-2026-02.tsv.gz # View (TSV format) head sales-2026-02.tsv ``` *** ## Code Example ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} # Monthly sales summary curl -o sales.tsv.gz "https://api.appeeky.com/v1/connect/sales-reports?vendorNumber=12345678&reportDate=2026-02&reportType=SALES&frequency=MONTHLY&reportSubType=SUMMARY&version=1_0" \ -H "X-API-Key: YOUR_APEEKY_KEY" \ -H "X-ASC-Issuer-Id: YOUR_ISSUER_ID" \ -H "X-ASC-Key-Id: YOUR_KEY_ID" \ -H "X-ASC-Private-Key: YOUR_PRIVATE_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} // Download and decompress in Node.js import { createWriteStream } from "fs"; import { createGunzip } from "zlib"; import { pipeline } from "stream/promises"; const params = new URLSearchParams({ vendorNumber: process.env.VENDOR_NUMBER, reportDate: "2026-02", reportType: "SALES", frequency: "MONTHLY", }); const res = await fetch( `https://api.appeeky.com/v1/connect/sales-reports?${params}`, { headers: { "X-API-Key": process.env.APEEKY_KEY, "X-ASC-Issuer-Id": process.env.ASC_ISSUER_ID, "X-ASC-Key-Id": process.env.ASC_KEY_ID, "X-ASC-Private-Key": process.env.ASC_PRIVATE_KEY, }, } ); const buffer = Buffer.from(await res.arrayBuffer()); await pipeline( require("stream").Readable.from(buffer), createGunzip(), createWriteStream("sales-2026-02.tsv") ); ``` *** ## Finding Your Vendor Number 1. Go to [App Store Connect](https://appstoreconnect.apple.com) 2. **Payments and Financial Reports** (left sidebar / Finance section) 3. Find your **Vendor #** (numeric, e.g. `12345678`) — copy only the digits, not a name or team ID *** ## Credits | Endpoint | Credits | | -------------------------- | ------- | | GET /connect/sales-reports | 3 | # Subscription Metrics (MRR & Churn) Source: https://docs.appeeky.com/docs/app-store-connect-subscription-metrics SaaS-grade subscription health for your iOS apps — MRR, ARR, ARPU, churn, trial conversion — derived from App Store Connect reports and FX-converted to USD The Subscription Metrics endpoint returns the SaaS metrics App Store Connect doesn't show out of the box: **monthly recurring revenue (MRR), ARR, ARPU, churn rate, trial conversion rate**, and the daily event counts (new, canceled, refunded, reactivated, expired) behind them. We compute these from Apple's `SUBSCRIPTION` and `SUBSCRIPTION_EVENT` reports, normalize all proceeds to a monthly basis, and convert every currency to USD using historical exchange rates — so a yearly subscription priced in EUR contributes the right MRR amount on the day it was sold. **Requires connected ASC account.** Connect your App Store Connect API key and Vendor Number in [appeeky.com → Settings → Integrations](https://appeeky.com). Subscription reports refresh daily and right after you connect. **Pro feature** — requires Indie plan or higher. *** ## Get Subscription Metrics ``` GET /v1/connect/metrics/subscriptions ``` Returns headline totals, per-app breakdown, and a daily time series for the requested window. ### Query parameters | Name | Type | Default | Description | | ------- | ------ | ----------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `from` | string | 30 days ago | Start date `YYYY-MM-DD`. | | `to` | string | today | End date `YYYY-MM-DD`. | | `appId` | string | — | Filter to one App Store Connect app ID. Omit to roll up across all your apps (MRR is summed, churn is paying-subscriber-weighted). | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/metrics/subscriptions?from=2026-03-25&to=2026-04-24" \ -H "X-API-Key: apk_your_key_here" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const params = new URLSearchParams({ from: "2026-03-25", to: "2026-04-24", appId: "6759740679", }); const res = await fetch( `https://api.appeeky.com/v1/connect/metrics/subscriptions?${params}`, { headers: { "X-API-Key": process.env.APEEKY_KEY } } ); const { data } = await res.json(); ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "from": "2026-03-25", "to": "2026-04-24", "appId": null, "totals": { "mrr_usd": 12480.55, "mrr_delta_pct": 0.0832, "arr_usd": 149766.60, "paying_subscribers": 1842, "arpu_usd": 6.7755, "avg_churn_rate": 0.041200, "trial_conversion_rate": 0.3620, "new_subscriptions": 612, "canceled_subscriptions": 487, "refunded_subscriptions": 19, "trial_starts": 2280, "trial_conversions": 825, "expired_subscriptions": 102 }, "apps": [ { "appId": "6759740679", "appName": "Voice Tape Record", "iconUrl": "https://is1-ssl.mzstatic.com/image/.../icon.png", "mrr_usd": 8920.30, "arpu_usd": 7.1240, "paying_subscribers": 1252, "churn_rate": 0.038500, "new_subscriptions_30d": 410, "canceled_subscriptions_30d": 318, "trial_conversions_30d": 562 }, { "appId": "6757361049", "appName": "NewSub: App Developer Alerts", "iconUrl": "https://is1-ssl.mzstatic.com/image/.../icon.png", "mrr_usd": 3560.25, "arpu_usd": 6.0312, "paying_subscribers": 590, "churn_rate": 0.046800, "new_subscriptions_30d": 202, "canceled_subscriptions_30d": 169, "trial_conversions_30d": 263 } ], "series": [ { "metric_date": "2026-03-25", "mrr_usd": 11521.40, "arpu_usd": 6.6210, "paying_subscribers": 1740, "new_subscriptions": 18, "canceled_subscriptions": 14, "refunded_subscriptions": 0, "reactivated_subscriptions": 2, "trial_starts": 72, "trial_conversions": 24, "expired_subscriptions": 4, "billing_retries": 5, "churn_rate": 0.041800 } ] } } ``` *** ## Field reference ### `totals` Headline numbers across the requested window. | Field | Description | | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `mrr_usd` | **Monthly Recurring Revenue** for the most recent day in the window, in USD. Yearly subs are normalized to `proceeds / 12`, weekly to `proceeds × 4.345`, etc. Includes only paying subscribers (introductory \$0 offers contribute 0). | | `mrr_delta_pct` | Relative change in MRR vs the first day of the window. `0.0832` = +8.32%. | | `arr_usd` | Annual Run Rate — `mrr_usd × 12`. | | `paying_subscribers` | Active paying subscribers on the most recent day. Excludes free trials. | | `arpu_usd` | Average Revenue Per User — `mrr_usd / paying_subscribers`. | | `avg_churn_rate` | Weighted average churn over the window (weighted by `paying_subscribers` so a tiny app with 100% churn doesn't dominate the rate). `0.0412` = 4.12% monthly churn. | | `trial_conversion_rate` | `trial_conversions / trial_starts` over the window. `0.3620` = 36.2% of trials become paying subs. | | `new_subscriptions`, `canceled_subscriptions`, `refunded_subscriptions`, `trial_starts`, `trial_conversions`, `expired_subscriptions` | Cumulative event counts in the window. | ### `apps` One row per app, sorted by `mrr_usd` descending. Each row is the **latest day's snapshot** for that app, with 30-day rolling event counts. Includes the app's name and icon so dashboards don't need a second round-trip per app. ### `series` Daily time series ready for charting. When `appId` is omitted, MRR and event counts are **summed across apps**, and churn is paying-subscriber-weighted (not a simple mean — same logic as `totals.avg_churn_rate`). | Field | Description | | --------------------------- | ------------------------------------------------------------------------------- | | `metric_date` | ISO date `YYYY-MM-DD`. | | `mrr_usd` | MRR for that day. | | `arpu_usd` | `mrr_usd / paying_subscribers` for that day. | | `paying_subscribers` | Active paying subscribers on that day. | | `new_subscriptions` | New paying subs that day. | | `canceled_subscriptions` | Cancellations (including auto-renew off). | | `refunded_subscriptions` | Apple-issued refunds. | | `reactivated_subscriptions` | Lapsed users who came back. | | `trial_starts` | New free-trial starts. | | `trial_conversions` | Trials that converted to paying. | | `expired_subscriptions` | Subscriptions that lapsed without renewing. | | `billing_retries` | Renewals Apple is retrying after a payment failure. | | `churn_rate` | `canceled_subscriptions / paying_subscribers_yesterday`, rounded to 6 decimals. | *** ## How the numbers are computed We download Apple's daily `SUBSCRIPTION` report. For each row, we: 1. Skip rows with `Active Standard Price Subscriptions = 0` (the user is in a free trial or intro offer). 2. Normalize `Developer Proceeds` to a monthly basis using the subscription duration: * **1 week** → `× 4.345` * **1 month** → `× 1` * **2 months** → `÷ 2` * **3 months** → `÷ 3` * **6 months** → `÷ 6` * **1 year** → `÷ 12` 3. Convert the result from the customer's currency to USD using the **historical exchange rate** for that day (Frankfurter, ECB-sourced). A subscription sold in EUR on April 1st is converted at April 1st's rate, not today's. 4. Sum across all active subs for that app and date. This means a yearly $59.99 subscription contributes ~$5/mo to MRR, not \$59.99 — exactly how RevenueCat and ChartMogul model it. For each `(app, date)` we compute: ``` churn_rate = canceled_subscriptions[date] / paying_subscribers[date - 1] ``` When you query without `appId`, churn across apps is **weighted by paying subscribers**, not averaged: ``` weighted_churn = Σ (paying_subscribers[app] × churn_rate[app]) / Σ paying_subscribers[app] ``` A 10-user app with 100% churn next to a 10,000-user app with 5% churn produces a weighted churn near 5%, which reflects business reality. ``` trial_conversion_rate = Σ trial_conversions / Σ trial_starts (over the window) ``` Note: a trial that started in March and converted in April will appear as `trial_starts` in March and `trial_conversions` in April. Over a long enough window the ratio converges to the true conversion rate; over a short window it can read above 100% if a backlog of older trials converts (this is normal). We map Apple's free-text event strings to consistent categories: | Apple event | Mapped to | | -------------------------------------------- | --------------------------- | | `Subscribe`, `Renew` | `new_subscriptions` | | `Cancel`, `Auto-Renew Off` | `canceled_subscriptions` | | `Refund` | `refunded_subscriptions` | | `Reactivate` | `reactivated_subscriptions` | | `Subscribe with Trial`, `Free Trial Started` | `trial_starts` | | `Convert to Paid Subscription` | `trial_conversions` | | `Subscription Expired` | `expired_subscriptions` | | `Billing Retry` | `billing_retries` | Unknown event strings are logged and ignored — they won't break aggregation. *** ## MCP Tool This endpoint is exposed as an MCP tool so AI assistants can analyze revenue health on your behalf: | Tool | Description | | -------------------------- | ---------------------------------------------------------- | | `asc_subscription_metrics` | MRR / ARR / ARPU / churn / trial conversion + daily series | Example prompt: ``` Show me MRR and churn trend for the last 90 days for app 6759740679, then explain whether the recent MRR growth is driven by new subscribers or lower churn. ``` *** ## Common Workflows ### Daily revenue dashboard ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/connect/metrics/subscriptions", { headers: { "X-API-Key": process.env.APEEKY_KEY } } ); const { data } = await res.json(); console.log(`MRR: $${data.totals.mrr_usd}`); console.log(`Δ MRR: ${(data.totals.mrr_delta_pct * 100).toFixed(1)}%`); console.log(`Churn: ${(data.totals.avg_churn_rate * 100).toFixed(2)}%`); console.log(`Trial → Paid: ${(data.totals.trial_conversion_rate * 100).toFixed(1)}%`); // Render the daily MRR series in your chart of choice const points = data.series.map((d) => ({ x: d.metric_date, y: d.mrr_usd })); ``` ### Identify your highest-churn app ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const { data } = await fetch( "https://api.appeeky.com/v1/connect/metrics/subscriptions", { headers: { "X-API-Key": process.env.APEEKY_KEY } } ).then((r) => r.json()); const worst = data.apps .filter((a) => a.paying_subscribers > 50) .sort((a, b) => b.churn_rate - a.churn_rate)[0]; console.log( `${worst.appName} has ${(worst.churn_rate * 100).toFixed(2)}% monthly churn` ); ``` *** ## Error Codes | Status | Code | When | | ------ | --------------------- | -------------------------------------- | | 401 | `INVALID_API_KEY` | Invalid or inactive API key | | 403 | `PRO_FEATURE` | Free plan — upgrade to Indie or higher | | 404 | `USER_NOT_FOUND` | User not in system (connect ASC first) | | 429 | `RATE_LIMIT_EXCEEDED` | Not enough credits | | 500 | `METRICS_ERROR` | Database or sync error | *** ## Limits & Caveats * Data is computed from Apple's daily reports, which are typically available **1–2 days after the day they cover**. Today's date will usually have no data; yesterday's may be partial. * MRR uses **developer proceeds** (post-Apple-cut), not list price. This matches what hits your bank account. * Churn is computed from cancellation **events**, not from comparing subscriber counts day-over-day. This avoids skew from new subscribers added the same day. * Refunds reduce revenue but are reported separately; they do not subtract from `mrr_usd` directly. For "net MRR after refunds", subtract `refunded_subscriptions × ARPU` from `mrr_usd`. * Backfill on first connect covers the last **30 days**. # TestFlight Source: https://docs.appeeky.com/docs/app-store-connect-testflight Beta groups, testers, test notes, and external beta review — manage TestFlight distribution programmatically Manage the full TestFlight lifecycle: create beta groups, invite testers, set per-build "What to Test" notes, and submit builds for external beta review. All endpoints require [App Store Connect authentication](/docs/app-store-connect-overview#authentication). Destructive operations require `"confirm": true` in the request body. *** ## Beta Groups ### List / Create / Delete ``` GET /v1/connect/apps/:appId/beta-groups POST /v1/connect/apps/:appId/beta-groups DELETE /v1/connect/beta-groups/:betaGroupId (confirm: true) ``` ### Create Body ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "name": "External Beta", "publicLinkEnabled": true, "publicLinkLimit": 1000, "publicLinkLimitEnabled": true, "feedbackEnabled": true } ``` Only `name` is required. Enabling the public link returns a shareable TestFlight invite URL. ### Manage Group Membership Add or remove **builds** or **testers** in a group: ``` PATCH /v1/connect/beta-groups/:betaGroupId/builds PATCH /v1/connect/beta-groups/:betaGroupId/testers Body: { "operation": "add", "ids": ["BUILD_OR_TESTER_ID"] } ``` `operation` is `add` or `remove`. *** ## Beta Testers ### List Testers ``` GET /v1/connect/beta-testers?appId=...&email=...&groupId=... ``` ### Create Tester ``` POST /v1/connect/beta-testers Body: { "email": "tester@example.com", "firstName": "Test", "lastName": "User", "betaGroupIds": ["GROUP_ID"] } ``` `email` and at least one `betaGroupIds` entry are required — Apple only accepts testers created directly into a group. ### Delete Tester ``` DELETE /v1/connect/beta-testers/:betaTesterId Body: { "confirm": true } ``` Removes the tester from **all** your apps. ### Resend Invitation ``` POST /v1/connect/beta-testers/:betaTesterId/invitations Body: { "appId": "APP_ID" } ``` *** ## TestFlight Metadata ### App-level (Test Information tab) Beta description, feedback email, marketing and privacy URLs — per locale: ``` GET /v1/connect/apps/:appId/beta-app-localizations POST /v1/connect/apps/:appId/beta-app-localizations PATCH /v1/connect/beta-app-localizations/:localizationId ``` Create body: `{ "locale": "en-US", "description": "...", "feedbackEmail": "...", "marketingUrl": "...", "privacyPolicyUrl": "..." }` ### Build-level ("What to Test") ``` GET /v1/connect/builds/:buildId/beta-localizations POST /v1/connect/builds/:buildId/beta-localizations Body: { "locale": "en-US", "whatsNew": "New tape styles. Please test export." } PATCH /v1/connect/beta-localizations/:localizationId DELETE /v1/connect/beta-localizations/:localizationId (confirm: true) ``` ### Beta App Review Details Contact and demo account shown to Apple's beta review team: ``` GET /v1/connect/apps/:appId/beta-review-detail PATCH /v1/connect/beta-review-details/:reviewDetailId ``` Attributes match [App Review Details](/docs/app-store-connect-release#app-review-details) (contact fields, demo account, notes). ### Build Beta Detail (notifications) ``` GET /v1/connect/builds/:buildId/beta-detail PATCH /v1/connect/build-beta-details/:buildBetaDetailId Body: { "autoNotifyEnabled": true } ``` *** ## External Beta Review Builds must pass Apple's beta review before external testers can install them. ### Get / Submit ``` GET /v1/connect/builds/:buildId/beta-review-submission POST /v1/connect/builds/:buildId/beta-review-submission Body: { "confirm": true } ``` The submit endpoint verifies the build is valid and not expired before submitting. ### Withdraw from Review ``` DELETE /v1/connect/builds/:buildId/beta-review-submission Body: { "confirm": true } ``` Apple's API has no "cancel beta review" call — the only supported withdrawal is **expiring the build**, which is what this endpoint does. The build can no longer be tested afterwards, so use it deliberately. *** ## Typical Beta Workflow ``` 1. POST /connect/apps/:appId/beta-groups → create group w/ public link 2. POST /connect/beta-testers → invite testers by email 3. POST /connect/builds/:buildId/beta-localizations → "What to Test" notes 4. POST /connect/builds/:buildId/beta-review-submission → external review 5. PATCH /connect/beta-groups/:groupId/builds {operation:add} → distribute build to group ``` ## Role Requirements | Action | Minimum Role | | ----------------------------------- | ------------ | | Beta groups, testers, localizations | App Manager | | Beta review submission | App Manager | # Apple Ads Library Source: https://docs.appeeky.com/docs/apple-ads-library See which competitors run Apple Ads in the EU — placements, countries, impression dates, and creatives. No Apple account required. Appeeky shows you which apps are running **Apple Ads** in the EU: where they advertise, which App Store placements they use, when impressions were recorded, and what creatives they're running. Look up any competitor by App ID — no Apple Search Ads connection, no credentials. This is **competitor intelligence**. [Apple Search Ads](/docs/apple-search-ads) is for managing your own campaigns; Apple Ads Library is for researching anyone else's. Data is sourced from Apple's public Ad Repository (published under the EU Digital Services Act). **Coverage is EU-only.** The repository contains ads delivered in 25 EU storefronts (AT, BE, BG, HR, CY, CZ, DK, EE, FI, FR, DE, GR, HU, IE, IT, LV, LU, NL, PL, PT, RO, SK, SI, ES, SE). An app that advertises exclusively in the US, UK, or other non-EU regions will legitimately return zero ads here. Upstream data is delayed \~7 days. *** ## Get App Apple Ads Intelligence ``` GET /v1/apps/{appId}/apple-ads ``` Aggregated Apple Ads signal for one app, plus the underlying ad list. `appId` is the numeric Apple App ID (trackId). **Cost:** 3 credits ### Query parameters | Parameter | Type | Default | Description | | ------------ | ------- | ------------------------ | ---------------------------------------------------- | | `countries` | string | all covered EU countries | Comma-separated EU storefront codes, e.g. `DE,FR,ES` | | `datePreset` | string | `LAST_90_DAYS` | `LAST_90_DAYS`, `LAST_180_DAYS`, or `LAST_YEAR` | | `includeAds` | boolean | `true` | Set `false` to return only the summary | ### Example ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/apps/570060128/apple-ads?datePreset=LAST_90_DAYS" \ -H "X-API-Key: $APPEEKY_API_KEY" ``` ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": 570060128, "datePreset": "LAST_90_DAYS", "coverage": "EU_ONLY", "dataStartDate": "2026-03-31", "dataEndDate": "2026-06-29", "summary": { "isAdvertising": true, "totalAds": 6, "countries": [ { "code": "DE", "adCount": 3 }, { "code": "FR", "adCount": 2 }, { "code": "ES", "adCount": 1 } ], "placements": { "APPSTORE_SEARCH_RESULTS": 5, "APPSTORE_TODAY_TAB": 1 }, "formats": { "Icon + Asset Ad": 4, "Icon Ad": 2 }, "firstImpressionDate": "2026-04-02", "lastImpressionDate": "2026-07-01", "usesAudienceRefinement": false }, "ads": [ { "adId": "7704366945e755420fd4e5b7059de024", "appId": 570060128, "appName": "Duolingo: Sprachen und Schach", "developerName": "Duolingo", "placement": "APPSTORE_SEARCH_RESULTS", "format": "Icon + Asset Ad", "countryOrRegion": "DE", "firstImpressionDate": "2026-06-27", "lastImpressionDate": "2026-07-01", "adBanner": { "subtitle": "...", "primaryCategory": "..." }, "adAssets": [{ "pictureUrl": "https://...", "orientation": "PORTRAIT" }] } ] } } ``` ### Summary fields | Field | Description | | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | `isAdvertising` | Whether at least one Apple-delivered ad was recorded in the period | | `totalAds` | Total ad entries across the requested storefronts | | `countries` | Per-storefront ad counts, sorted descending | | `placements` | Ad counts per App Store surface: `APPSTORE_SEARCH_RESULTS`, `APPSTORE_TODAY_TAB`, `APPSTORE_SEARCH_TAB` | | `formats` | Ad counts per format (`Icon Ad`, `Icon + Asset Ad`) | | `firstImpressionDate` / `lastImpressionDate` | Earliest and latest recorded impression across all ads — a recent `lastImpressionDate` means the app is actively advertising | | `usesAudienceRefinement` | Whether any ad used age, gender, location, or customer-type targeting | *** ## Search Advertisers ``` GET /v1/ads/apple/search?q={name} ``` Find advertised apps and developers by name. Only entities with at least one EU-delivered ad are searchable. Returned `id` values are Apple App IDs (`type: APP`) or developer IDs (`type: DEVELOPER`). **Cost:** 1 credit | Parameter | Type | Default | Description | | --------- | ------ | --------------- | ------------------------------------------- | | `q` | string | — | App or developer name, minimum 2 characters | | `types` | string | `APP,DEVELOPER` | `APP`, `DEVELOPER`, or both | | `limit` | number | 20 | Max results (1-50) | ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/ads/apple/search?q=duolingo&types=APP" \ -H "X-API-Key: $APPEEKY_API_KEY" ``` ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "entities": [ { "id": 570060128, "name": "Duolingo: Language Lessons", "type": "APP" } ], "totalResults": 1 } } ``` *** ## Get Ad Variations ``` GET /v1/ads/apple/ads/{adId} ``` Full detail for a single ad: locale variations, per-device creative assets (image and video URLs), app icon variations, and audience refinement. Use an `adId` from the app intelligence response. **Cost:** 2 credits | Parameter | Type | Default | Description | | ------------ | ------ | -------------- | ----------------------------------------------- | | `datePreset` | string | `LAST_90_DAYS` | `LAST_90_DAYS`, `LAST_180_DAYS`, or `LAST_YEAR` | *** ## MCP Tools The same capabilities are available on the [Appeeky MCP Server](/docs/mcp): | Tool | Description | | --------------------------------- | ----------------------------------------------------------------- | | `apple_ads_library_search` | Search advertised apps/developers by name | | `apple_ads_library_app_ads` | Apple Ads intelligence summary (and optional ad list) for one app | | `apple_ads_library_ad_variations` | Locale variations and creative assets for one ad | ### Example prompts * *"Is Duolingo running Apple Ads in the EU right now? Which placements?"* * *"Compare the Apple Ads footprint of my top 3 competitors."* * *"Show me the creatives Photoroom is using in its Apple Ads."* *** ## Errors | Code | Status | Meaning | | ------------------------------------ | ------ | --------------------------------------------- | | `INVALID_APP_ID` | 400 | App ID is not numeric | | `INVALID_QUERY` | 400 | Search query shorter than 2 characters | | `INVALID_COUNTRY` | 400 | No valid EU storefront code in `countries` | | `APPLE_AD_REPOSITORY_RATE_LIMITED` | 429 | Apple's upstream rate limit hit — retry later | | `APPLE_AD_REPOSITORY_UPSTREAM_ERROR` | 502 | Apple Ad Repository returned an error | | `APPLE_AD_REPOSITORY_TIMEOUT` | 504 | Upstream request timed out | Responses are cached server-side (6-12 hours) — safe, since the upstream data itself is delayed by \~7 days. # Apple Search Ads Overview Source: https://docs.appeeky.com/docs/apple-search-ads Connect Apple Search Ads and manage paid App Store campaigns, keywords, negatives, and performance reports Apple Search Ads is Apple's paid search channel on the App Store. It is separate from [App Store Connect](/docs/app-store-connect-overview): App Store Connect handles publishing, reviews, analytics, and sales reports; Apple Search Ads handles paid keyword campaigns and ad performance. Appeeky wraps Apple's **Campaign Management API v5** for campaign management and reports, and **Platform API v1** for search term popularity, keyword suggestions, and impression share. After connecting credentials once, you can read and manage campaigns, ad groups, targeting keywords, negative keywords, performance reports, and official popularity scores through REST or [MCP tools](/docs/apple-search-ads-mcp). Apple Search Ads support requires Indie plan or higher. You also need an active [Apple Search Ads](https://searchads.apple.com) account. Apple Search Ads uses a **different API key** than App Store Connect. If you already connected App Store Connect, you still need to connect Search Ads separately. *** ## What You Can Do | Area | What it covers | Docs | | ------------------ | ------------------------------------------------------------------------------------ | ------------------------------------------------------------- | | Credentials | Search Ads API key setup, org access, secure connection options, per-request headers | [Credentials & Setup](/docs/apple-search-ads-credentials) | | Campaigns | List campaigns, pause/resume, change names and daily budgets | [Campaigns & Ad Groups](/docs/apple-search-ads-campaigns) | | Ad groups | List ad groups, pause/resume, change names and default CPT bids | [Campaigns & Ad Groups](/docs/apple-search-ads-campaigns) | | Targeting keywords | List, find, create, update, delete, recommendations, bid recommendations | [Targeting Keywords](/docs/apple-search-ads-keywords) | | Negative keywords | Campaign-level and ad-group-level negative keyword management | [Negative Keywords](/docs/apple-search-ads-negative-keywords) | | Reports | Keyword performance and real search-term reports | [Reports](/docs/apple-search-ads-reports) | | Insights | Official search term popularity, keyword suggestions, impression share | [Insights & Popularity](/docs/apple-search-ads-insights) | | Profitability | Join Apple Ads spend with RevenueCat revenue, profit, and ROAS | [Profitability](/docs/apple-search-ads-profitability) | | ROAS workflow | Integration readiness, optimization recommendations, review country gate | [ROAS Workflow](/docs/apple-search-ads-roas-workflow) | | MCP | Assistant tools for Search Ads workflows | [MCP Tools](/docs/apple-search-ads-mcp) | *** ## Customer Setup Flow 1. Create or choose an Apple Search Ads account. 2. Generate Search Ads API credentials in the Search Ads UI. 3. Save credentials in Appeeky, or pass them per request. 4. Verify accessible organizations with `/me` and `/acls`. 5. List campaigns and ad groups. 6. Pull search term and keyword performance reports. 7. Optionally manage bids, statuses, targeting keywords, and negative keywords. Start with [Credentials & Setup](/docs/apple-search-ads-credentials). If credentials are already connected, continue with [Reports](/docs/apple-search-ads-reports) for search terms and performance data, or [Profitability](/docs/apple-search-ads-profitability) to join spend with RevenueCat revenue. *** ## What Search Ads Data Is Good For | Signal | Why it matters | | ---------------------------- | ------------------------------------------------------------------------- | | Search terms report | Shows real user queries that triggered your ads | | Keyword report | Shows impressions, taps, installs, spend, and efficiency per paid keyword | | Bid recommendations | Helps estimate competitive pressure for a keyword | | Negative keywords | Blocks irrelevant paid traffic and protects spend | | Campaign and ad group status | Explains why ads may not be serving | Search Ads reports are especially useful for ASO because they reveal real query language and paid conversion behavior. Pair them with [App Store Connect Metrics](/docs/app-store-connect-metrics), [Keyword Metrics](/docs/keyword-metrics), and [Apple Ads Insights](/docs/apple-search-ads-insights) for official search popularity. Apple Ads Platform API v1 now exposes search term popularity (`asa_search_term_popularity`) and keyword suggestions with 0–100 scores (`asa_keyword_suggestions`). See [Insights & Popularity](/docs/apple-search-ads-insights). *** ## Typical Workflow ### 1. Check connection ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/apple-ads/credentials/status ``` ### 2. List campaigns ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/apple-ads/campaigns ``` ### 3. Pull search terms ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} POST /v1/connect/apple-ads/campaigns/:campaignId/reports/searchterms ``` ### 4. Review keyword performance ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} POST /v1/connect/apple-ads/campaigns/:campaignId/reports/keywords ``` ### 5. Pull official search popularity ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} POST /v1/connect/apple-ads/insights/search-term-popularity ``` See [Insights & Popularity](/docs/apple-search-ads-insights). ### 6. Manage keywords or negatives ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} POST /v1/connect/apple-ads/campaigns/:campaignId/adgroups/:adGroupId/targetingkeywords/bulk POST /v1/connect/apple-ads/campaigns/:campaignId/negativekeywords/bulk ``` ### 7. Join spend with RevenueCat revenue ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/apple-ads/profitability?level=keyword&days=14 ``` ### 8. Run the ROAS workflow ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/apple-ads/playbook/status GET /v1/connect/apple-ads/playbook/recommendations?appId=YOUR_APP_ID ``` See [ROAS Workflow](/docs/apple-search-ads-roas-workflow). *** ## Endpoint Families Base path: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} /v1/connect/apple-ads ``` | Family | Endpoints | | ------------------ | --------------------------------------------------------------------------------------- | | Credentials | `POST /credentials`, `GET /credentials/status`, `DELETE /credentials` | | Account | `GET /me`, `GET /acls` | | Campaigns | `GET /campaigns`, `PUT /campaigns/:campaignId` | | Ad groups | `GET /campaigns/:campaignId/adgroups`, `PUT /campaigns/:campaignId/adgroups/:adGroupId` | | Targeting keywords | List, get, find, create, update, delete, recommendations | | Negative keywords | Campaign and ad group negative keyword CRUD | | Reports | Keyword performance and search terms | | Insights | Search term popularity, keyword suggestions, impression share | | Profitability | Apple Ads spend joined with RevenueCat revenue | | ROAS workflow | Readiness, recommendations, review country gate | *** ## Apple Search Ads vs App Store Connect | | App Store Connect | Apple Search Ads | | -------------------- | -------------------------------------------------------------- | -------------------------------------------------- | | Product | App publishing, private app analytics, reviews, sales reports | Paid search campaigns on the App Store | | Console | [appstoreconnect.apple.com](https://appstoreconnect.apple.com) | [searchads.apple.com](https://searchads.apple.com) | | Appeeky connect path | `/v1/connect/credentials` | `/v1/connect/apple-ads/credentials` | | MCP prefix | `asc_*` | `asa_*` | | Best for | Metadata, reviews, downloads/revenue sync | Paid keyword performance and real search queries | *** ## Related Docs * [Credentials & Setup](/docs/apple-search-ads-credentials) * [Campaigns & Ad Groups](/docs/apple-search-ads-campaigns) * [Targeting Keywords](/docs/apple-search-ads-keywords) * [Negative Keywords](/docs/apple-search-ads-negative-keywords) * [Reports](/docs/apple-search-ads-reports) * [Insights & Popularity](/docs/apple-search-ads-insights) * [Profitability](/docs/apple-search-ads-profitability) * [ROAS Workflow](/docs/apple-search-ads-roas-workflow) * [MCP Tools](/docs/apple-search-ads-mcp) # Campaigns & Ad Groups Source: https://docs.appeeky.com/docs/apple-search-ads-campaigns List, inspect, pause, resume, and update Apple Search Ads campaigns and ad groups Campaigns and ad groups define where ads run, how budgets are applied, and which targeting rules are active. These endpoints operate on your connected Apple Search Ads organization. Update endpoints write to Apple Search Ads. Use them only when the customer expects campaign or ad group changes. *** ## List Campaigns ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/apple-ads/campaigns?limit=20&offset=0" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` Query parameters: | Name | Description | | -------- | ----------------------------- | | `limit` | Number of campaigns to return | | `offset` | Pagination offset | Response includes campaign IDs, names, status, countries, budget, serving state, and Apple metadata when available. *** ## Update a Campaign ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X PUT "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "status": "ENABLED", "name": "Brand - US", "dailyBudgetAmount": { "amount": "25.00", "currency": "USD" } }' ``` Supported fields: | Field | Description | | ------------------- | ------------------------- | | `status` | `ENABLED` or `PAUSED` | | `name` | Campaign name | | `dailyBudgetAmount` | Daily budget money object | You can send one field or multiple fields in the same update. *** ## List Ad Groups ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/adgroups?limit=20&offset=0" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` Ad groups contain bids, Search Match settings, audience rules, and keyword targets. *** ## Update an Ad Group ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X PUT "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/adgroups/2147258884" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "status": "ENABLED", "name": "Generic screenshot keywords", "defaultBidAmount": { "amount": "0.80", "currency": "USD" } }' ``` Supported fields: | Field | Description | | ------------------ | ---------------------------- | | `status` | `ENABLED` or `PAUSED` | | `name` | Ad group name | | `defaultBidAmount` | Default CPT bid money object | *** ## Pause and Resume Flow Campaign-level pause: ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X PUT "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{"status":"PAUSED"}' ``` Resume campaign: ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X PUT "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{"status":"ENABLED"}' ``` Ad groups can also be paused independently: ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X PUT "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/adgroups/2147258884" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{"status":"ENABLED"}' ``` `status: ENABLED` only clears a user pause. If Apple returns serving issues such as `CONTENT_PROVIDER_UNLINKED`, billing issues, missing ad groups, or no eligible ads, fix those in [Search Ads](https://searchads.apple.com). *** ## Typical Read Workflow 1. List campaigns. 2. Pick a `campaignId`. 3. List ad groups for that campaign. 4. Pull [keyword reports](/docs/apple-search-ads-reports#keyword-performance-report) or [search terms](/docs/apple-search-ads-reports#search-terms-report). 5. Decide whether to update bids, statuses, targeting keywords, or negative keywords. *** ## Related Docs * [Targeting Keywords](/docs/apple-search-ads-keywords) * [Negative Keywords](/docs/apple-search-ads-negative-keywords) * [Reports](/docs/apple-search-ads-reports) # Apple Search Ads Credentials Source: https://docs.appeeky.com/docs/apple-search-ads-credentials Generate a Search Ads API key, connect credentials, verify org access, and choose stored or per-request authentication Apple Search Ads credentials are created in the Search Ads account, not in App Store Connect. A Search Ads API key gives Appeeky access to the campaigns and reports for the selected organization. Apple Search Ads API keys are separate from App Store Connect API keys. A working App Store Connect key cannot be used for Search Ads campaigns. *** ## Create a Search Ads API Key 1. Sign in at [searchads.apple.com](https://searchads.apple.com). 2. Open **Account Settings > API** or **User Management > API**. The label can vary by account layout. 3. Generate an API client. 4. Upload your public key. Apple expects an EC P-256 key pair, similar to App Store Connect. 5. Save these values: * **Client ID**: usually starts with `SEARCHADS.` * **Team ID**: often the same value as Client ID * **Key ID** * **Org ID**: numeric Search Ads organization ID * **Private key**: `.p8` PEM, shown or downloaded once Apple's reference: [Implementing OAuth for the Apple Search Ads API](https://developer.apple.com/documentation/apple_search_ads/implementing_oauth_for_the_apple_search_ads_api). Save the private key immediately. Apple may only show or download it once. *** ## Connect Credentials ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/apple-ads/credentials" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "clientId": "SEARCHADS.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "teamId": "SEARCHADS.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "keyId": "c60fc276-80a7-440d-b800-f13a7dcc6fde", "orgId": "21106140", "privateKey": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" }' ``` | Field | Required | Description | | ------------ | -------- | ---------------------------------- | | `clientId` | Yes | Search Ads API client ID | | `teamId` | Yes | Search Ads team ID | | `keyId` | Yes | API key ID | | `orgId` | Yes | Numeric Search Ads organization ID | | `privateKey` | Yes | EC private key PEM | Appeeky validates credentials against Apple before saving them. Saved credentials are stored securely and encrypted at rest. *** ## Check Connection Status ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/apple-ads/credentials/status" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` Example response: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "connected": true, "clientId": "SEARCHADS.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "teamId": "SEARCHADS.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "orgId": "21106140", "keyId": "c60fc276-80a7-440d-b800-f13a7dcc6fde", "lastVerifiedAt": "2026-06-09T12:00:00Z" } } ``` *** ## Verify Account Access ### Current API User ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/apple-ads/me" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` ### Accessible Organizations ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/apple-ads/acls" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` Use `/acls` to confirm which Search Ads organizations the API key can access and which roles Apple assigned. *** ## Disconnect ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X DELETE "https://api.appeeky.com/v1/connect/apple-ads/credentials" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` This removes the saved Search Ads connection from Appeeky. *** ## Authentication Options ### Stored Credentials Connect once with: ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} POST /v1/connect/apple-ads/credentials ``` After that, data requests only need your Appeeky API key. This is recommended for production and for MCP tools. ### Per-Request Headers For one-off calls or tests, pass Search Ads credentials on every request: | Header | Required | Description | | ----------------------- | -------- | ------------------------------ | | `X-ASA-Client-Id` | Yes | Search Ads Client ID | | `X-ASA-Team-Id` | Yes | Team ID | | `X-ASA-Key-Id` | Yes | API Key ID | | `X-ASA-Org-Id` | Yes | Organization ID | | `X-ASA-Private-Key` | Yes\* | Private key PEM | | `X-ASA-Private-Key-B64` | Yes\* | Base64-encoded PEM alternative | \* Provide either `X-ASA-Private-Key` or `X-ASA-Private-Key-B64`. ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/apple-ads/campaigns" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "X-ASA-Client-Id: SEARCHADS.xxx" \ -H "X-ASA-Team-Id: SEARCHADS.xxx" \ -H "X-ASA-Key-Id: your-key-id" \ -H "X-ASA-Org-Id: 21106140" \ -H "X-ASA-Private-Key: -----BEGIN PRIVATE KEY-----..." ``` *** ## Plan Requirement Apple Search Ads requires Indie plan or higher. If connection fails with `PLAN_REQUIRED`, upgrade the account or use an API key tied to an eligible Appeeky account. *** ## Troubleshooting | Error | Meaning | Fix | | -------------------------- | --------------------------------------------------- | ------------------------------------------------------------- | | `MISSING_FIELDS` | Connect body is missing required Search Ads fields | Send `clientId`, `teamId`, `keyId`, `orgId`, and `privateKey` | | `INVALID_ASC_CREDENTIALS` | Apple rejected the Search Ads OAuth credentials | Recreate the Search Ads API key and verify the org ID | | `ASA_CREDENTIALS_REQUIRED` | No saved credentials and no `X-ASA-*` headers | Connect credentials once or pass per-request headers | | `PLAN_REQUIRED` | Account is not on an eligible plan | Use an eligible Appeeky account | | Apple 403 | API key has no access to that organization or route | Check Search Ads roles and `/acls` | # Apple Ads Insights & Popularity Source: https://docs.appeeky.com/docs/apple-search-ads-insights Official search term popularity, keyword suggestions, and impression share from Apple Ads Platform API v1 Apple Ads Platform API v1 adds first-class **search term popularity** and **keyword suggestions with popularity scores**. These are market demand signals, not campaign performance reports. Campaign reports (what users typed that triggered *your* ads) stay on [Reports](/docs/apple-search-ads-reports). Use Insights when you want Apple's relative search volume for a genre, country, or app. Requires connected [Apple Search Ads credentials](/docs/apple-search-ads-credentials). Same OAuth keys as Campaign Management API v5. Appeeky calls `https://api.ads.apple.com/v1` with `X-AP-Context: adAccountId=…`. *** ## Endpoints | Endpoint | Description | | ------------------------------------------------------------ | ------------------------------------------------------------------------ | | `POST /v1/connect/apple-ads/insights/search-term-popularity` | Ranked search terms for a country + genre, with 1–100 and 1–5 popularity | | `POST /v1/connect/apple-ads/insights/impression-share` | Your app's share of impressions, rank, and popularity for search terms | | `POST /v1/connect/apple-ads/suggestions/keywords` | Keyword ideas for an app, each with a 0–100 popularity score | | `POST /v1/connect/apple-ads/suggestions/phrases` | Phrase discovery (`SUGGESTION`) or lookup (`SEARCH`) with popularity | MCP tools: `asa_search_term_popularity`, `asa_impression_share`, `asa_keyword_suggestions`, `asa_phrase_popularity`. *** ## Search Term Popularity Returns the highest-volume search terms in an App Store genre and country. Scores are Apple's official relative popularity, not estimated volume. ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/apple-ads/insights/search-term-popularity" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "genre": "PRODUCTIVITY", "countries": ["US"], "granularity": "WEEKLY_SUN_SAT", "limit": 50 }' ``` | Field | Description | | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | `genre` | Required. App Store genre token, e.g. `PRODUCTIVITY`, `TRAVEL`, `GAMES`, `HEALTH_FITNESS`. iTunes names like `Health & Fitness` are normalized. | | `countries` | ISO codes. Default `["US"]`. | | `terms` | Optional. Look up specific search terms instead of the full genre ranking. | | `from` / `to` | `YYYY-MM-DD`. Default is the last complete Sun–Sat week (UTC). | | `granularity` | `WEEKLY_SUN_SAT` (default) or `MONTHLY`. | | `limit` | Max rows, 1–5000. Default 50. | Each row includes: | Field | Meaning | | ------------------------- | -------------------------------------------- | | `searchTerm` | The query | | `rankInGenre` | Volume rank inside the genre (`1` = highest) | | `searchPopularityInGenre` | 1–100 inside the genre | | `searchPopularity1to100` | 1–100 across all genres in that country | | `searchPopularity1to5` | Coarse tier, `5` = most popular | Weekly windows must start on a Sunday. Monthly windows use calendar months. Timezone is UTC. ### Look up specific keywords Pass `terms` to score known App Store queries instead of the full genre ranking. The term must appear in that country + genre that week; otherwise the row is omitted. ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/apple-ads/insights/search-term-popularity" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "genre": "PRODUCTIVITY", "countries": ["US"], "terms": ["chatgpt", "vpn"] }' ``` This is the App Store keyword-popularity path. Phrase SEARCH below is a different catalog (brand / business names). *** ## Keyword Suggestions Apple-suggested keywords for an advertised app, sorted by popularity. `adamId` must be an app in your Apple Ads account. ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/apple-ads/suggestions/keywords" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "adamId": "123456789", "countries": ["US", "GB"], "terms": ["task manager"], "limit": 20 }' ``` `adamId` is required. Optional `terms` seed related suggestions. Each result is `{ text, popularity }` on a 0–100 scale. *** ## Phrase Popularity `SEARCH` looks up Apple's brand/business phrase catalog (not App Store search terms). A single phrase uses `LIKE` (`%chat%` → Chatime, Chatr Mobile). Multiple phrases use exact `IN`. `SUGGESTION` needs an advertised `adamId`. ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/apple-ads/suggestions/phrases" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "queryType": "SEARCH", "phrases": ["chat"] }' ``` *** ## Impression Share How often your ads appeared for a search term, out of all searches on that term in the same country. Requires an `adamId` you advertise. ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/apple-ads/insights/impression-share" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "adamId": "123456789", "countries": ["US"], "reportType": "ALL_SLOTS", "limit": 50 }' ``` Share is exact from 1–90%. Above 90% Apple returns the 91–100% bucket. Each row also includes `rank` (`1` = highest share) and `searchPopularity1to5`. Daily windows are capped at 30 days. Default is the last 14 complete UTC days. *** ## What this is not * Not exact search volume (impression counts). Scores are relative. * Not organic rank or difficulty. Pair with [Keyword Metrics](/docs/keyword-metrics) and [Keyword Rankings](/docs/get-keyword-ranks). * Not a public keyword database. You still need an Apple Ads account. * Not a replacement for [search terms reports](/docs/apple-search-ads-reports), which show queries that actually triggered *your* ads. Campaign Management API v5 remains in use for campaigns, keywords, and reports until Apple retires it on **26 January 2027**. # Targeting Keywords Source: https://docs.appeeky.com/docs/apple-search-ads-keywords List, find, create, update, delete, and get recommendations for Apple Search Ads targeting keywords Targeting keywords tell Apple which searches your ads can appear for. Appeeky supports full targeting keyword management for connected Search Ads campaigns and ad groups. Create, update, and delete endpoints write to Apple Search Ads. *** ## List Keywords in an Ad Group ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/adgroups/2147258884/targetingkeywords?limit=50&offset=0" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` Use this to inspect keyword text, match type, bid, status, and Apple IDs. *** ## Get One Keyword ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/adgroups/2147258884/targetingkeywords/2251741048" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` *** ## Find Keywords Across a Campaign ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/targetingkeywords/find" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "conditions": [ { "field": "text", "operator": "CONTAINS", "values": ["screenshot"] } ], "pagination": { "offset": 0, "limit": 50 } }' ``` The body follows Apple's selector-style filtering model. *** ## Create Keywords ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/adgroups/2147258884/targetingkeywords/bulk" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "keywords": [ { "text": "screenshot maker", "matchType": "EXACT", "bidAmount": { "amount": "0.80", "currency": "USD" } }, { "text": "app screenshot", "matchType": "BROAD", "bidAmount": { "amount": "0.50", "currency": "USD" } } ] }' ``` | Field | Description | | ----------- | ------------------------------ | | `text` | Keyword text | | `matchType` | Usually `EXACT` or `BROAD` | | `bidAmount` | Optional keyword-level CPT bid | *** ## Update Keywords ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X PUT "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/adgroups/2147258884/targetingkeywords/bulk" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "keywords": [ { "id": 2251741048, "status": "ENABLED", "bidAmount": { "amount": "0.60", "currency": "USD" } } ] }' ``` Use this to pause/resume keywords or adjust bids. *** ## Delete Keywords ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/adgroups/2147258884/targetingkeywords/delete" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "keywordIds": [2251741048] }' ``` Apple treats deletion according to Search Ads API behavior for that keyword resource. *** ## Keyword Recommendations ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/adgroups/2147258884/targetingkeywords/recommendations" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` Apple returns recommendations when enough context is available for the ad group. *** ## Bid Recommendations ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/adgroups/2147258884/bid-recommendations" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "keywords": ["screenshot maker", "app screenshot"], "matchType": "EXACT", "countriesOrRegions": ["US"] }' ``` Bid recommendations are useful for paid keyword planning, but they are not the same as organic search volume. *** ## ASO Workflow 1. Pull the [search terms report](/docs/apple-search-ads-reports#search-terms-report). 2. Identify terms with good tap/install behavior. 3. Check whether those terms are already targeting keywords. 4. Add exact-match keywords for high-confidence queries. 5. Add broad-match keywords for controlled exploration. 6. Use [negative keywords](/docs/apple-search-ads-negative-keywords) to block irrelevant traffic. 7. Compare paid behavior with organic [Keyword Metrics](/docs/keyword-metrics). # Apple Search Ads MCP Tools Source: https://docs.appeeky.com/docs/apple-search-ads-mcp Use MCP tools to inspect and manage Apple Search Ads campaigns from an assistant Apple Search Ads REST capabilities are also available as MCP tools. After connecting Search Ads credentials, the assistant only needs your Appeeky API key. All tools are available on the [Appeeky MCP Server](/docs/mcp). *** ## Connection and Account | Tool | Description | | ------------------------ | ----------------------------------------------- | | `asa_credentials_status` | Whether Search Ads is connected on your account | | `asa_list_campaigns` | List campaigns in your org | | `asa_list_adgroups` | List ad groups for a campaign | *** ## Campaign and Ad Group Updates | Tool | Description | | --------------------- | ----------------------------------------------------- | | `asa_update_campaign` | Enable/pause campaign, rename, or update daily budget | | `asa_update_adgroup` | Enable/pause ad group, rename, or update default bid | *** ## Targeting Keywords | Tool | Description | | --------------------------------------- | ----------------------------------------- | | `asa_list_targeting_keywords` | List targeting keywords in an ad group | | `asa_get_targeting_keyword` | Get one targeting keyword by ID | | `asa_find_targeting_keywords` | Find targeting keywords across a campaign | | `asa_create_targeting_keywords` | Bulk-create targeting keywords | | `asa_update_targeting_keywords` | Bulk-update keyword bid or status | | `asa_delete_targeting_keywords` | Delete targeting keywords | | `asa_targeting_keyword_recommendations` | Apple keyword suggestions | | `asa_bid_recommendations` | Suggested bids for keyword texts | *** ## Negative Keywords | Tool | Description | | --------------------------------------- | ------------------------------------- | | `asa_list_campaign_negative_keywords` | List campaign-level negative keywords | | `asa_find_campaign_negative_keywords` | Find campaign negative keywords | | `asa_create_campaign_negative_keywords` | Create campaign negative keywords | | `asa_update_campaign_negative_keywords` | Update campaign negative keywords | | `asa_delete_campaign_negative_keywords` | Delete campaign negative keywords | | `asa_list_adgroup_negative_keywords` | List ad group negative keywords | | `asa_find_adgroup_negative_keywords` | Find ad group negative keywords | | `asa_create_adgroup_negative_keywords` | Create ad group negative keywords | | `asa_update_adgroup_negative_keywords` | Update ad group negative keywords | | `asa_delete_adgroup_negative_keywords` | Delete ad group negative keywords | *** ## Reports | Tool | Description | | ------------------------- | ------------------------------------------- | | `asa_report_keywords` | Keyword-level performance for a date range | | `asa_report_search_terms` | Real search queries that triggered your ads | *** ## Insights (Platform API v1) | Tool | Description | | ---------------------------- | -------------------------------------------------------------------- | | `asa_search_term_popularity` | Official popularity ranking by country + genre | | `asa_impression_share` | Your app's impression share, rank, and popularity | | `asa_keyword_suggestions` | Keyword ideas for an app with 0–100 popularity | | `asa_phrase_popularity` | Brand/business phrase catalog, or phrase ideas for an advertised app | *** ## ROAS and Attribution | Tool | Description | | ------------------------------- | ------------------------------------------------------------------------------------------------ | | `asa_profitability` | Join spend with RevenueCat revenue, profit, and ROAS. Optional `country` filter. | | `asa_playbook_status` | Check whether RevenueCat, Apple Search Ads, and attribution are ready | | `asa_admaxxing_recommendations` | Optimization bundle: setup gaps, scale/pause candidates, attribution sample, review country gate | | `asa_review_country_gate` | Flag countries with active campaigns but low App Store ratings | RevenueCat attribution tools (`rc_attribution_summary`, `rc_customer_attributes`) are documented in [RevenueCat Attribution](/docs/revenuecat-attribution). See [ROAS Workflow](/docs/apple-search-ads-roas-workflow) for REST equivalents and response shapes. *** ## Example Prompts ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Check if my Apple Search Ads account is connected. ``` ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} List my Search Ads campaigns and show the top 10 search terms by impressions for campaign 2143596801 in the last 30 days. ``` ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} For campaign 2143596801, which keywords have the highest install rate but rising CPC? Suggest 5 organic ASO keywords to test based on the search terms report. ``` ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} List targeting keywords for ad group 2147258884 in campaign 2143596801, then update the bid on keyword 2251741048 to 0.60 USD. ``` ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Add negative keyword "free" as BROAD match at campaign level for campaign 2143596801. ``` ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Show the top US Productivity search terms by Apple popularity this week. ``` ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} For app 123456789, suggest keywords with popularity scores above 60. ``` ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Is my Apple Search Ads and RevenueCat setup ready for ROAS analysis? ``` ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Which keywords should I scale or pause? Use my app ID 123456789 and check countries below 4.5 stars where I'm still spending. ``` *** ## Per-Call Credentials Stored credentials are recommended. For one-off tool calls, the assistant can pass: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} client_id team_id key_id org_id private_key ``` For setup details, see [Credentials & Setup](/docs/apple-search-ads-credentials). # Negative Keywords Source: https://docs.appeeky.com/docs/apple-search-ads-negative-keywords Manage campaign-level and ad-group-level Apple Search Ads negative keywords Negative keywords prevent ads from showing on unwanted searches. They are one of the safest ways to control spend when Search Match or broad keywords pull in irrelevant traffic. Apple Search Ads supports negative keywords at two levels: | Level | Scope | | -------------------------- | ------------------------------ | | Campaign negative keywords | Apply across the campaign | | Ad group negative keywords | Apply only inside one ad group | *** ## Campaign Negative Keywords ### List ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/negativekeywords?limit=50&offset=0" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` ### Find ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/negativekeywords/find" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "conditions": [ { "field": "text", "operator": "CONTAINS", "values": ["free"] } ], "pagination": { "offset": 0, "limit": 50 } }' ``` ### Create ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/negativekeywords/bulk" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "keywords": [ { "text": "free", "matchType": "BROAD" }, { "text": "template", "matchType": "EXACT" } ] }' ``` ### Update ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X PUT "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/negativekeywords/bulk" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "keywords": [ { "id": 2251741048, "text": "free app", "matchType": "BROAD" } ] }' ``` ### Delete ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/negativekeywords/delete" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "keywordIds": [2251741048] }' ``` *** ## Ad Group Negative Keywords Ad group negative endpoints use the same body shapes but include `adGroupId` in the path. ### List ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/adgroups/2147258884/negativekeywords?limit=50&offset=0" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` ### Create ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/adgroups/2147258884/negativekeywords/bulk" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "keywords": [ { "text": "free", "matchType": "BROAD" } ] }' ``` Other ad group negative endpoints: ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} POST /campaigns/:campaignId/adgroups/:adGroupId/negativekeywords/find PUT /campaigns/:campaignId/adgroups/:adGroupId/negativekeywords/bulk POST /campaigns/:campaignId/adgroups/:adGroupId/negativekeywords/delete ``` *** ## When to Use Campaign vs Ad Group Negatives | Situation | Recommended level | | -------------------------------------------------- | ----------------- | | Query is irrelevant for the whole app | Campaign | | Query is irrelevant only for one theme or audience | Ad group | | Search Match is finding unrelated traffic | Campaign first | | Broad keyword pulls mixed traffic | Ad group first | *** ## Practical ASO Use Use [Search Terms Reports](/docs/apple-search-ads-reports#search-terms-report) to find waste: * High impressions, low taps * Taps but no installs * Queries unrelated to the app's value prop * Queries that imply a free product when your app is paid * Brand terms for competitors you do not want to target Then add negative keywords to reduce spend and keep exploration focused. # Apple Search Ads Profitability Source: https://docs.appeeky.com/docs/apple-search-ads-profitability Join Apple Search Ads spend with RevenueCat revenue to calculate profit, ROAS, and optimization insights Apple Search Ads tells you how paid App Store campaigns perform before and at install time: spend, impressions, taps, installs, CPA, CPT, and conversion rate. RevenueCat tells you what those acquired users earn after install: purchases, subscriptions, renewals, and revenue. The profitability endpoint joins both sides so you can see which campaigns, ad groups, and keywords are actually profitable. ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/apple-ads/profitability ``` *** ## What This Answers Use this endpoint when you want to know: * Which paid keywords are profitable? * Which campaigns have positive ROAS? * Which ad groups spend money but do not earn it back? * Which keywords should be scaled? * Which rows should be paused, bid down, or moved to exact match? This is private first-party analytics. It only works for Apple Search Ads accounts and RevenueCat projects you can access. Apple Search Ads does not have a public competitor ad library like Meta Ad Library. You cannot use this endpoint to see another company's Apple Ads spend, bids, revenue, or ROAS. *** ## Data Sources | Metric | Source | | ------------------------------------------------------------- | --------------------------------------------------------------------- | | Campaigns, ad groups, keywords, status, budgets | Apple Search Ads Campaign Management API | | Spend, impressions, taps, installs, CPA, CPT, conversion rate | Apple Search Ads reports | | Revenue | RevenueCat Charts API, using Apple Search Ads attribution segments | | Profit | Calculated by Appeeky: `revenue - spend` | | ROAS | Calculated by Appeeky: `revenue / spend` | | Insights | Calculated by Appeeky from spend, revenue, profit, ROAS, and installs | *** ## Requirements You need: 1. An Appeeky API key. 2. Apple Search Ads connected in Appeeky, or Apple Search Ads credentials passed per request. 3. A RevenueCat secret API key. 4. RevenueCat's Apple Search Ads integration enabled for the project. If RevenueCat does not have Apple Search Ads attribution enabled, Appeeky can still return Apple Ads spend and install metrics, but revenue, profit, and ROAS will be missing or incomplete. *** ## Request ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl \ "https://api.appeeky.com/v1/connect/apple-ads/profitability?level=keyword&days=14¤cy=USD" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "X-RC-Key: sk_YOUR_REVENUECAT_SECRET_KEY" \ -H "X-RC-Project: proj_YOUR_PROJECT_ID" ``` ### Headers | Header | Required | Description | | -------------- | -------- | ----------------------------------------------------------------------------------- | | `X-API-Key` | Yes | Your Appeeky API key | | `X-RC-Key` | Yes | RevenueCat secret API key | | `X-RC-Project` | No | RevenueCat project ID. Required only when the API key can access multiple projects. | If Apple Search Ads is not saved in Appeeky, pass per-request Apple credentials: | Header | Description | | ----------------------- | --------------------------------- | | `X-ASA-Client-Id` | Apple Search Ads client ID | | `X-ASA-Team-Id` | Apple Search Ads team ID | | `X-ASA-Key-Id` | Apple Search Ads key ID | | `X-ASA-Org-Id` | Apple Search Ads organization ID | | `X-ASA-Private-Key` | EC private key PEM | | `X-ASA-Private-Key-B64` | Base64-encoded EC private key PEM | *** ## Query Parameters | Parameter | Default | Description | | --------------- | --------- | ------------------------------------------------------------------------------------------- | | `level` | `keyword` | Rollup level: `keyword`, `campaign`, `adgroup`, `search_term`, or `country`. | | `campaignId` | — | Optional single campaign ID. | | `campaignIds` | — | Optional comma-separated campaign IDs. Omit to scan campaigns in the connected account. | | `from` | — | Start date, `YYYY-MM-DD`. | | `to` | — | End date, `YYYY-MM-DD`. | | `days` | `14` | Trailing window when `from` and `to` are omitted. | | `limit` | `200` | Max Apple report rows per campaign. | | `campaignLimit` | `25` | Max campaigns to scan when `campaignIds` is omitted. | | `country` | — | Optional ISO country code (e.g. `US`). Limits analysis to campaigns targeting that country. | | `currency` | `USD` | RevenueCat currency. | | `minSpend` | `20` | Spend threshold used for insight buckets. | | `insights` | `true` | Set `false` to omit optimization insights. | *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "level": "keyword", "dateRange": { "from": "2026-05-27", "to": "2026-06-09" }, "currency": "USD", "rows": [ { "id": "123456789", "name": "screenshot search", "level": "keyword", "campaignId": "2143596801", "campaignName": "Search", "keywordId": "123456789", "keyword": "screenshot search", "matchType": "EXACT", "spend": 82.4, "revenue": 146.2, "profit": 63.8, "roas": 1.774, "installs": 19, "taps": 73, "impressions": 1420, "avgCpa": 4.34, "avgCpt": 1.13, "ttr": 0.0514, "conversionRate": 0.2603, "currency": "USD", "revenueMatched": true, "sourceRows": 1 } ], "totals": { "spend": 82.4, "revenue": 146.2, "profit": 63.8, "roas": 1.774, "installs": 19, "taps": 73, "impressions": 1420, "avgCpa": 4.34, "avgCpt": 1.13, "ttr": 0.0514, "conversionRate": 0.2603 }, "insights": [ { "type": "scale", "title": "Scale candidates", "detail": "Rows with ROAS >= 1.5 and at least 20 spend." } ], "meta": { "source": "live", "appleReport": "keywords", "revenueSource": "revenuecat_charts", "revenueSegmentId": "apple_search_ads_keyword", "revenueAttributionAvailable": true, "warnings": [] } } } ``` *** ## Country table (`level=country`) Returns a multi-country ROAS table in one call — the same view as a per-country spend / installs / revenue / ROAS dashboard. ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl \ "https://api.appeeky.com/v1/connect/apple-ads/profitability?level=country&days=14¤cy=USD" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "X-RC-Key: sk_YOUR_REVENUECAT_SECRET_KEY" ``` | Field | Source | | ------------------------------------------ | ----------------------------------------------------------------------------- | | `spend`, `installs`, `taps`, `impressions` | Apple Search Ads campaign reports grouped by `countryOrRegion` | | `revenue`, `profit`, `roas` | RevenueCat keyword attribution rolled up to each campaign's targeting country | Optional `country=FR` filters the table to a single ISO country code. ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "level": "country", "rows": [ { "id": "FR", "name": "FR", "level": "country", "spend": 3.53, "installs": 2, "revenue": 39.82, "profit": 36.29, "roas": 11.28 } ] } } ``` *** ## Rollup Levels | Level | Best for | Revenue support | | ------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | | `campaign` | Portfolio-level budget decisions | Supported when RevenueCat exposes Apple Search Ads campaign segments | | `adgroup` | Comparing targeting groups | Supported when RevenueCat exposes Apple Search Ads ad group segments | | `keyword` | ROAS by paid keyword | Supported when RevenueCat exposes Apple Search Ads keyword segments | | `country` | Multi-country ROAS table (spend, installs, revenue, ROAS per store country) | Revenue rolled up from keyword-level Apple Search Ads attribution by campaign targeting country | | `search_term` | Real user query analysis | Apple metrics only; RevenueCat does not expose stable search-term revenue attribution | Search terms and keywords are not the same thing. A keyword is what you target in Apple Search Ads. A search term is what the user typed. RevenueCat attribution can map revenue to Apple Search Ads keyword/campaign/ad group dimensions, but not to arbitrary search terms. *** ## Insight Types | Type | Meaning | | ------------------------ | ----------------------------------------------------------------------------------- | | `scale` | High ROAS rows with enough spend and installs to consider increasing budget or bids | | `wasting_spend` | Rows with meaningful spend but weak ROAS or zero installs | | `profitable` | Rows with the highest absolute profit | | `no_revenue_attribution` | Apple Ads metrics exist, but RevenueCat revenue did not match | | `needs_more_data` | No clear winner or loser crossed the current spend threshold | *** ## Related Docs * [Apple Search Ads Credentials](/docs/apple-search-ads-credentials) * [Apple Search Ads Reports](/docs/apple-search-ads-reports) * [Apple Search Ads ROAS Workflow](/docs/apple-search-ads-roas-workflow) * [RevenueCat Attribution](/docs/revenuecat-attribution) * [RevenueCat Overview](/docs/revenuecat-overview) * [MCP Tools](/docs/apple-search-ads-mcp) # Apple Search Ads Reports Source: https://docs.appeeky.com/docs/apple-search-ads-reports Pull keyword performance and search terms reports from Apple Search Ads Reports are the most useful Apple Search Ads data for ASO. They show how paid keywords and real user queries perform in your own campaigns. *** ## Report Endpoints | Endpoint | Description | | ---------------------------------------------------------------------- | -------------------------------------- | | `POST /v1/connect/apple-ads/campaigns/:campaignId/reports/keywords` | Performance by targeting keyword | | `POST /v1/connect/apple-ads/campaigns/:campaignId/reports/searchterms` | Real search queries that triggered ads | Both endpoints accept the same query parameters: | Parameter | Description | | --------- | ---------------------------------------------------------------- | | `from` | Start date, `YYYY-MM-DD` | | `to` | End date, `YYYY-MM-DD` | | `days` | Trailing window when `from` and `to` are omitted. Default is 30. | | `limit` | Max rows. Default 50, max 200. | ## Search Terms Report ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST \ "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/reports/searchterms?days=30&limit=50" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` Search terms are the exact queries users typed before seeing or tapping your ad. This is often the highest-signal Search Ads report for ASO work. Use it to find: * New keyword ideas * Queries worth adding as exact-match targeting keywords * Queries that should become negative keywords * Differences between paid search behavior and organic keyword rankings * Terms that convert well but are not yet in your metadata *** ## Keyword Performance Report ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST \ "https://api.appeeky.com/v1/connect/apple-ads/campaigns/2143596801/reports/keywords?from=2026-05-01&to=2026-06-01&limit=100" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` Keyword reports summarize performance for your targeting keywords. Typical metrics include impressions, taps, installs, spend, and suggested bid data where Apple returns it. Use it to answer: * Which paid keywords are efficient? * Which keywords spend but do not install? * Which keywords are good candidates for organic metadata? * Which terms need bid changes? * Which terms need exact vs broad match changes? *** ## Example ASO Analysis Flow 1. Pull the search terms report for the last 30 days. 2. Sort by installs, conversion, or spend. 3. Mark high-performing queries as ASO candidates. 4. Check each candidate in [Keyword Metrics](/docs/keyword-metrics). 5. Compare with organic rankings from [Keyword Rankings](/docs/get-keyword-ranks). 6. Add strong terms to metadata experiments or briefs. 7. Add irrelevant terms to [Negative Keywords](/docs/apple-search-ads-negative-keywords). *** ## Search Popularity Apple Ads Platform API v1 exposes official search popularity scores. Use [Insights & Popularity](/docs/apple-search-ads-insights) for genre rankings (`asa_search_term_popularity`) and app keyword suggestions (`asa_keyword_suggestions`). Campaign reports remain useful as a second signal: | Signal | How to use it | | ----------------------- | ---------------------------------------------- | | Search term impressions | Indicates paid inventory seen by your campaign | | Suggested bids | Indicates competitive pressure | | Tap rate | Indicates ad relevance | | Install rate | Indicates conversion intent | | Spend | Indicates where budget is being consumed | For organic rank and difficulty, pair with [Keyword Metrics](/docs/keyword-metrics). *** ## Reporting Caveats * Search terms only include your own campaigns. * Low-volume data may be limited by Apple. * Reports depend on campaign setup, countries, bids, and budget. * A campaign with no spend may return little or no report data. * Search Ads reporting is not a competitor ad intelligence API. For public competitor ad visibility, use [Meta Ad Library Intelligence](/docs/overview) if available for the market you are researching. To join Apple Ads spend with RevenueCat revenue, see [Apple Search Ads Profitability](/docs/apple-search-ads-profitability). # Apple Search Ads ROAS Workflow Source: https://docs.appeeky.com/docs/apple-search-ads-roas-workflow Check integration readiness, get optimization recommendations, and validate App Store ratings before scaling paid spend by country Apple Search Ads shows what you spend and how users respond at install time. RevenueCat shows what those users earn afterward. Appeeky connects both sides so you can see which keywords and campaigns are actually profitable — and whether your App Store presence is strong enough to scale spend in each country. This workflow is available through REST, the [web dashboard](https://appeeky.com), and [MCP tools](/docs/apple-search-ads-mcp). *** ## What This Covers | Capability | Endpoint | Best for | | ------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | | Integration readiness | `GET /v1/connect/apple-ads/playbook/status` | Confirm RevenueCat, Apple Search Ads, and attribution are wired up before trusting ROAS numbers | | Optimization recommendations | `GET /v1/connect/apple-ads/playbook/recommendations` | Scale winners, cut wasteful spend, and review country-level rating risks in one call | | Review country gate | `GET /v1/connect/apple-ads/review-country-gate` | Compare active campaign countries against App Store ratings | | Profitability by country | `GET /v1/connect/apple-ads/profitability?level=country&days=14` | Multi-country ROAS table in one call | | Profitability filtered to one country | `GET /v1/connect/apple-ads/profitability?country=US` | ROAS filtered to campaigns targeting a specific country | For the core spend × revenue join and insight buckets, see [Apple Search Ads Profitability](/docs/apple-search-ads-profitability). For per-customer attribution dimensions from RevenueCat, see [RevenueCat Attribution](/docs/revenuecat-attribution). *** ## Prerequisites 1. An Appeeky API key. 2. [Apple Search Ads connected](/docs/apple-search-ads-credentials) in Appeeky (or per-request ASA headers). 3. A RevenueCat secret API key (`X-RC-Key`), saved in Appeeky Settings or passed per request. 4. **Apple AdServices** enabled in RevenueCat under **Integrations**. This is what populates Apple Search Ads attribution in RevenueCat charts and customer attributes. Without Apple AdServices in RevenueCat, Appeeky can still return Apple Search Ads spend and installs, but revenue, profit, and ROAS will be incomplete. The readiness endpoint flags this before you act on recommendations. *** ## Integration Readiness Check whether your stack is ready for ROAS analysis. ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/apple-ads/playbook/status ``` ### Headers | Header | Required | Description | | -------------- | -------- | ------------------------------------------------------------------------------------------------- | | `X-API-Key` | Yes | Your Appeeky API key | | `X-RC-Key` | No | RevenueCat secret key. If omitted, Appeeky uses credentials saved in your account when available. | | `X-RC-Project` | No | RevenueCat project ID when your key has multiple projects | ### Example ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/apple-ads/playbook/status" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "X-RC-Key: sk_YOUR_REVENUECAT_SECRET_KEY" ``` ### Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "ready": false, "appleSearchAdsConnected": true, "revenueCatConnected": true, "appleAdsAttributionEnabled": false, "profitabilityAvailable": false, "steps": [ { "id": "revenuecat", "title": "Connect RevenueCat", "status": "complete", "detail": "RevenueCat secret key is saved in Appeeky." }, { "id": "apple_adservices", "title": "Enable Apple AdServices in RevenueCat", "status": "warning", "detail": "No Apple Search Ads attribution detected yet. Enable Apple AdServices in RevenueCat Integrations and wait for installs." }, { "id": "apple_search_ads", "title": "Connect Apple Search Ads", "status": "complete", "detail": "Apple Search Ads API credentials are connected." }, { "id": "profitability", "title": "ROAS analysis ready", "status": "warning", "detail": "Connect both integrations and enable Apple AdServices attribution to unlock ROAS." } ] } } ``` ### Step statuses | Status | Meaning | | ------------ | ------------------------------------------------------------- | | `complete` | Requirement met | | `warning` | Connected but data not flowing yet (e.g. attribution pending) | | `incomplete` | Not configured | When `ready` is `true`, all steps are `complete` and profitability analysis is fully available. *** ## Optimization Recommendations Returns a bundled analysis: integration gaps, ROAS-based scale and pause candidates, a RevenueCat attribution sample, and optional review-rating warnings by country. ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/apple-ads/playbook/recommendations ``` ### Query parameters | Parameter | Default | Description | | ----------- | --------- | ------------------------------------------------------------------------ | | `level` | `keyword` | Rollup for profitability: `keyword`, `campaign`, or `adgroup` | | `days` | `14` | Trailing date window | | `minSpend` | `20` | Minimum spend for insight buckets | | `minRating` | `4.5` | App Store rating threshold for review country gate | | `country` | — | Optional ISO country code (e.g. `US`) to scope profitability | | `appId` | — | Apple App Store numeric app ID. Required for review country gate section | ### Headers Same as [Profitability](/docs/apple-search-ads-profitability): `X-API-Key`, `X-RC-Key`, optional `X-RC-Project`, and optional per-request ASA headers. ### Example ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl \ "https://api.appeeky.com/v1/connect/apple-ads/playbook/recommendations?level=keyword&days=14&appId=123456789&country=US" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "X-RC-Key: sk_YOUR_REVENUECAT_SECRET_KEY" ``` ### Response shape ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "playbook": { "...": "same as /playbook/status" }, "profitability": { "...": "same as /profitability" }, "attribution": { "sampleSize": 50, "customersWithAttribution": 12, "appleSearchAdsAttributed": 8, "attributionEnabled": true, "rows": [ { "mediaSource": "Apple Search Ads", "campaign": "US Brand", "adGroup": null, "keyword": "competitor name", "country": "US", "customerCount": 3, "totalRevenue": 47.97 } ] }, "reviewGate": { "appId": "123456789", "minRating": 4.5, "rows": [], "warnings": [] }, "recommendations": [ { "type": "scale", "priority": "high", "title": "Scale: competitor name", "detail": "ROAS 2.10x with $82 spend and 19 installs.", "keyword": "competitor name", "roas": 2.1, "spend": 82 } ] } } ``` ### Recommendation types | Type | Meaning | | ------------- | --------------------------------------------------------------------------------- | | `setup` | Missing integration or attribution step | | `scale` | Strong ROAS with enough spend — candidate to increase budget or bids | | `kill` | Meaningful spend with weak ROAS or zero installs — candidate to pause or bid down | | `attribution` | Top rows from RevenueCat customer attribution sample | | `review_gate` | Country with active campaigns but App Store rating below threshold | *** ## Review Country Gate Low App Store ratings in a country can hurt conversion on paid traffic. This endpoint compares **active** Apple Search Ads campaign targeting against the app's **App Store rating in each country**. ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/apple-ads/review-country-gate?appId=123456789 ``` ### Query parameters | Parameter | Required | Default | Description | | ----------- | -------- | ------- | ---------------------------------------------- | | `appId` | Yes | — | Apple App Store numeric app ID | | `minRating` | No | `4.5` | Flag countries at or below this average rating | ### Example ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl \ "https://api.appeeky.com/v1/connect/apple-ads/review-country-gate?appId=123456789&minRating=4.5" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` ### Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "123456789", "minRating": 4.5, "rows": [ { "country": "TH", "countryCode": "TH", "rating": 3.8, "reviewCount": 42, "campaignCount": 1, "campaignNames": ["SEA Tier 3"], "status": "critical", "recommendation": "Rating 3.8 is below 4.5. Pause or remove this country from campaigns to avoid burning budget on low-trust listings." } ], "warnings": [] } } ``` ### Row status | Status | Meaning | | ---------- | ---------------------------------------------------------------- | | `ok` | Rating meets threshold | | `warning` | Rating could not be fetched — verify manually | | `critical` | Rating below threshold while campaigns still target this country | Ratings are sourced from the public App Store catalog per country. They reflect the listing users see when your ad appears in that storefront. *** ## Country-Scoped Profitability Filter ROAS analysis to campaigns that target a single country. Useful when you run separate campaigns per region. ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/apple-ads/profitability?level=keyword&days=14&country=US ``` Appeeky filters using each campaign's `countriesOrRegions` metadata from Apple Search Ads. Revenue attribution remains at the keyword, ad group, or campaign level from RevenueCat charts — country filtering applies to spend and campaign scope, not a second revenue dimension. See [Apple Search Ads Profitability](/docs/apple-search-ads-profitability) for full parameters, response fields, and insight types. *** ## Typical Workflow 1. **Check readiness** — `GET /playbook/status` until `ready` is `true`. 2. **Review recommendations** — `GET /playbook/recommendations?appId=…` for scale, pause, and country warnings. 3. **Drill into keywords** — `GET /profitability?level=keyword&country=US` for a focused table. 4. **Validate attribution** — [RevenueCat Attribution](/docs/revenuecat-attribution) for customer-level keyword × campaign × country rows. 5. **Act** — pause campaigns, adjust bids, or add negatives via [Campaigns](/docs/apple-search-ads-campaigns) and [Keywords](/docs/apple-search-ads-keywords), or ask your assistant via [MCP](/docs/apple-search-ads-mcp). *** ## MCP Tools | Tool | Description | | ------------------------------- | ------------------------------------------------------------------- | | `asa_playbook_status` | Integration readiness checklist | | `asa_admaxxing_recommendations` | Full optimization bundle (setup + ROAS + attribution + review gate) | | `asa_review_country_gate` | Country rating vs active campaign targeting | | `asa_profitability` | ROAS join; supports `country` filter | See [Apple Search Ads MCP Tools](/docs/apple-search-ads-mcp) for parameters and example prompts. *** ## Related Docs * [Apple Search Ads Profitability](/docs/apple-search-ads-profitability) * [RevenueCat Attribution](/docs/revenuecat-attribution) * [Apple Search Ads Overview](/docs/apple-search-ads) * [RevenueCat Overview](/docs/revenuecat-overview) # ASO Audit Source: https://docs.appeeky.com/docs/aso-audit Full App Store Optimization health audit with scoring, breakdown, and recommendations ``` GET /v1/aso/audit/:appId ``` Run a comprehensive ASO (App Store Optimization) health audit on any App Store or Google Play app. Returns an overall ASO score (0-100), a 9-factor breakdown, prioritized recommendations, and keyword coverage stats. Think of it as a full SEO audit but for the app stores. *** ## Path Parameters | Name | Type | Required | Description | | ----- | ------ | -------- | --------------------------------------------------------------------------------------- | | appId | string | Yes | App ID — numeric for Apple (`913335252`), package name for Google (`com.spotify.music`) | ## Query Parameters | Name | Type | Default | Description | | -------- | ------ | ------- | -------------------------------------------------------------------------------- | | platform | string | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | `us` | ISO country code (e.g. `us`, `gb`, `de`) | | lang | string | `en` | Google Play language code (used when `platform=google`) | *** ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/aso/audit/913335252?country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/aso/audit/913335252?country=us", { headers: { "X-API-Key": "YOUR_API_KEY" }, } ); const { data } = await response.json(); console.log(`ASO Score: ${data.asoScore}/100 (${data.gradeLabel})`); console.log(`Recommendations: ${data.recommendations.length}`); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/aso/audit/913335252", params={"country": "us"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = response.json()["data"] print(f"ASO Score: {data['asoScore']}/100 ({data['gradeLabel']})") for rec in data["recommendations"]: print(f" [{rec['priority']}] {rec['category']}: {rec['suggestion']}") ``` *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "913335252", "country": "us", "app": { "title": "Brilliant: Learn by doing", "developer": "Brilliant.org", "icon": "https://is1-ssl.mzstatic.com/.../512x512bb.jpg", "rating": 4.74, "reviews": 27315, "category": "Education", "price": 0, "lastUpdated": "2026-01-26T12:19:36Z", "description": "Master concepts in math, data science, and computer science through fun...", "screenshotCount": 6, "languageCount": 1 }, "asoScore": 72, "gradeLabel": "Average", "scoreBreakdown": { "titleScore": 85, "subtitleScore": 70, "descriptionScore": 75, "ratingScore": 90, "reviewsScore": 60, "keywordCoverageScore": 55, "visualAssetsScore": 75, "localizationScore": 20, "updateFrequencyScore": 80 }, "recommendations": [ { "priority": "high", "category": "Keywords", "issue": "Only ranking for 8 keywords with limited top-10 positions", "suggestion": "Research and target 15-25 keywords mixing high-volume (competitive) and long-tail (easier to rank) terms. Update keyword field regularly." }, { "priority": "medium", "category": "Reviews", "issue": "Only 27,315 reviews - need more social proof", "suggestion": "Use Apple's SKStoreReviewController to prompt reviews at strategic moments. Respond to existing reviews to boost engagement." }, { "priority": "low", "category": "Localization", "issue": "Only 1 languages supported", "suggestion": "Localize metadata (title, subtitle, keywords, description) for top markets: Spanish, German, French, Japanese, Chinese, Portuguese, Korean." } ], "keywords": { "tracked": 8, "top10Count": 2, "top30Count": 5, "avgRank": 18, "bestKeyword": { "keyword": "learn math", "rank": 3 } }, "metadata": { "titleLength": 25, "titleMaxChars": 30, "titleHasKeyword": true, "descriptionLength": 1250, "descriptionWordCount": 210 } } } ``` *** ## ASO Score Breakdown The overall score is a weighted average of 9 factors: | Factor | Weight | What it measures | | -------------------- | ------ | ---------------------------------------------------- | | titleScore | 15% | Title length, keyword inclusion, formatting | | subtitleScore | 5% | Subtitle presence and keyword usage | | descriptionScore | 10% | Length, formatting (bullets/paragraphs), readability | | ratingScore | 15% | Star rating vs competitive thresholds | | reviewsScore | 15% | Review count relative to category benchmarks | | keywordCoverageScore | 20% | Keywords tracked, top-10 and top-30 positions | | visualAssetsScore | 8% | Screenshot count (target: 8+) | | localizationScore | 5% | Number of supported languages | | updateFrequencyScore | 7% | Days since last update | ## Grade Labels | Score Range | Grade | | ----------- | ---------- | | 90-100 | Excellent | | 75-89 | Good | | 60-74 | Average | | 40-59 | Needs Work | | 0-39 | Poor | ## Recommendation Fields | Field | Type | Description | | ---------- | ------ | ------------------------------------------------------ | | priority | string | `"high"`, `"medium"`, or `"low"` | | category | string | Area of improvement (Title, Description, Rating, etc.) | | issue | string | What's wrong — specific to the app | | suggestion | string | Actionable fix with best practices | *** **Use the ASO audit as a starting point**, then drill deeper with: * [Keyword Opportunities](/docs/aso-opportunities) — find new keywords to target * [Metadata Suggestions](/docs/aso-metadata-suggest) — get optimized title/subtitle/keywords * [Competitor Report](/docs/aso-competitor-report) — benchmark against competitors *** ## Errors | Status | Code | When | | ------ | ----------------- | ----------------------------- | | 400 | INVALID\_APP\_ID | Non-numeric or missing app ID | | 401 | MISSING\_API\_KEY | No API key in the request | | 404 | APP\_NOT\_FOUND | App not found or unavailable | # ASO Decision Brief Source: https://docs.appeeky.com/docs/aso-brief Orchestrated ASO summary — audit, opportunities, storefront readiness, optional intent clusters, multi-country ``` GET /v1/aso/brief/:appId ``` Returns a **decision-oriented package** for ASO: it runs the same work as a full [ASO audit](/docs/aso-audit) and [keyword opportunities](/docs/aso-opportunities) in parallel, adds a **storefront readiness** score (creative + metadata + ASO blend), optionally **semantic intent clusters** on opportunity keywords, and supports **multi-country** briefs. Use the individual endpoints when you need full raw reports. Details on intent clustering: [ASO Intent Clusters](/docs/aso-intent-clusters). *** ## Path parameters | Name | Type | Required | Description | | ----- | ------ | -------- | ---------------------- | | appId | string | Yes | Apple App ID (numeric) | ## Query parameters | Name | Type | Default | Description | | ----------------- | ------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | country | string | `us` | ISO country code (ignored when `countries` is set) | | countries | string | — | Comma-separated ISO codes (e.g. `us,gb,de`) for **per-country** briefs; see multi-country below | | fresh | bool | `false` | If `true` or `1`, forces a fresh re-analysis (slower) | | intentClusters | bool | `false` | If `true` or `1`, group opportunity keywords into **semantic intent clusters** (**+2 credits**) | | semanticExpansion | bool | `false` | If `true` or `1`, expand the brief's strongest opportunity keywords through embedding similarity and surface \~30 fresh keyword candidates the audit/opportunities pipeline didn't already know about (**+2 credits**) | *** ## Response (single country) | Section | Description | | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `summary` | One-line `headline`, ASO score, grade, keyword stats | | `storefrontReadiness` | **0–100** combined score: ASO core, creative (visuals + screenshot count from audit), metadata fit; `gradeLabel` + `summary` | | `intentClusters` | Present when `intentClusters=1`: clusters with `label`, `keywords`, `avgOpportunityScore`, `prioritizeClusterId`, or `skipped` + `reason` if clustering cannot be returned | | `prioritizedActions` | Audit + opportunities + optional **intent** action (“Prioritize this intent group…”) | | `opportunitiesTop` | Up to 10 opportunity rows | | `meta` | `warnings` if clustering skipped or failed | *** ## Multi-country Use **`?countries=us,gb,de`** (same rules as [keyword ranks](/docs/get-keyword-ranks) / `countries` list).\ Response shape: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "913335252", "countries": ["de", "gb", "us"], "results": { "us": { "...": "same shape as single-country brief" }, "gb": { }, "de": { } } } } ``` **Credits:** base **5** × **number of countries** in `countries` (minimum 1). **+2** when `intentClusters=1` (flat add-on for the whole request). *** ## Code examples ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} # Single storefront + optional intent clusters curl "https://api.appeeky.com/v1/aso/brief/913335252?country=us&intentClusters=1" \ -H "X-API-Key: YOUR_API_KEY" # Multi-country curl "https://api.appeeky.com/v1/aso/brief/913335252?countries=us,gb,de" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const params = new URLSearchParams({ countries: "us,gb", intentClusters: "true", }); const res = await fetch( `https://api.appeeky.com/v1/aso/brief/913335252?${params}`, { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); if (data.results) console.log(Object.keys(data.results)); else console.log(data.storefrontReadiness.score); ``` *** ## Credits * **5 × (country multiplier)** — multiplier is the number of valid `countries` codes, or **1** when only `country` is used (same model as [keyword metrics](/docs/keyword-metrics) / ranks). * **+2** when `intentClusters=1` (intent clustering add-on). See [Rate limits](/docs/rate-limits). Response header `X-Credit-Cost` reflects the final charge. *** ## Errors | Status | Code | When | | ------ | ------------------ | -------------------------------------------------- | | 400 | INVALID\_APP\_ID | Missing or non-numeric app ID | | 400 | INVALID\_COUNTRIES | `countries` set but no valid codes | | 404 | APP\_NOT\_FOUND | App unavailable or no data for a requested country | | 401 | — | Missing or invalid API key | | 429 | — | Insufficient monthly credits | *** **MCP:** `aso_brief` — use `countries` and `intent_clusters` arguments. See [MCP Server](/docs/mcp). # Competitor ASO Report Source: https://docs.appeeky.com/docs/aso-competitor-report Deep ASO comparison between two apps with keyword gap analysis ``` GET /v1/aso/competitor/:appId/:competitorId ``` Deep App Store Optimization comparison between two apps. Returns side-by-side profiles (rating, reviews, metadata quality, visuals, localization), keyword overlap analysis, gap opportunities, and a strategic verdict. This is a higher-level analysis than [Keyword Compare](/docs/keyword-compare) — it includes metadata comparison, visual asset analysis, and an overall competitive assessment. *** ## Path Parameters | Name | Type | Required | Description | | ------------ | ------ | -------- | ------------------------- | | appId | string | Yes | Your app's Apple App ID | | competitorId | string | Yes | Competitor's Apple App ID | ## Query Parameters | Name | Type | Default | Description | | ------- | ------ | ------- | ---------------------------------------- | | country | string | `us` | ISO country code (e.g. `us`, `gb`, `de`) | *** ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/aso/competitor/913335252/1157115554?country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/aso/competitor/913335252/1157115554?country=us", { headers: { "X-API-Key": "YOUR_API_KEY" }, } ); const { data } = await response.json(); console.log(`Your app: ${data.yourApp.title} (${data.yourApp.rating}★)`); console.log(`Competitor: ${data.competitorApp.title} (${data.competitorApp.rating}★)`); console.log(`Keyword overlap: ${data.keywordAnalysis.overlapPercent}%`); console.log(`Gaps to target: ${data.keywordAnalysis.gaps}`); console.log(`Verdict: ${data.verdict}`); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/aso/competitor/913335252/1157115554", params={"country": "us"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = response.json()["data"] print(f"Your app: {data['yourApp']['title']} ({data['yourApp']['rating']}★)") print(f"Competitor: {data['competitorApp']['title']} ({data['competitorApp']['rating']}★)") print(f"Keyword overlap: {data['keywordAnalysis']['overlapPercent']}%") print(f"\nTop keyword gaps:") for gap in data["keywordAnalysis"]["topGaps"][:5]: print(f" {gap['keyword']}: rank #{gap['competitorRank']}, volume {gap['volumeScore']}") print(f"\nVerdict: {data['verdict']}") ``` *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "913335252", "competitorId": "1157115554", "country": "us", "yourApp": { "appId": "913335252", "title": "Brilliant: Learn by doing", "developer": "Brilliant.org", "icon": "https://is1-ssl.mzstatic.com/.../512x512bb.jpg", "rating": 4.74, "reviews": 27315, "category": "Education", "titleLength": 25, "descriptionLength": 1250, "screenshotCount": 6, "languageCount": 1, "lastUpdated": "2026-01-26T12:19:36Z" }, "competitorApp": { "appId": "1157115554", "title": "Khan Academy", "developer": "Khan Academy", "icon": "https://is1-ssl.mzstatic.com/.../512x512bb.jpg", "rating": 4.8, "reviews": 158000, "category": "Education", "titleLength": 12, "descriptionLength": 2100, "screenshotCount": 8, "languageCount": 15, "lastUpdated": "2026-02-10T08:00:00Z" }, "comparison": { "ratingDiff": -0.06, "reviewsDiff": -130685, "titleLengthDiff": 13, "descriptionLengthDiff": -850, "screenshotCountDiff": -2, "languageCountDiff": -14 }, "keywordAnalysis": { "shared": 5, "yourUnique": 3, "competitorUnique": 8, "gaps": 2, "overlapPercent": 31, "topGaps": [ { "keyword": "free courses", "competitorRank": 3, "volumeScore": 65 }, { "keyword": "learn coding", "competitorRank": 5, "volumeScore": 58 }, { "keyword": "khan academy", "competitorRank": 1, "volumeScore": 82 }, { "keyword": "online learning", "competitorRank": 8, "volumeScore": 55 }, { "keyword": "study app", "competitorRank": 12, "volumeScore": 48 } ] }, "verdict": "Competitor leads in: more reviews, broader keyword coverage. Your strengths: niche keyword positions. Target their 8 unique keywords for quick wins." } } ``` *** ## App Profile Fields Each app profile (`yourApp` and `competitorApp`) includes: | Field | Type | Description | | ----------------- | ------ | ----------------------------- | | appId | string | Apple App ID | | title | string | App name | | developer | string | Developer name | | icon | string | App icon URL | | rating | number | Star rating (0-5) | | reviews | number | Total review count | | category | string | Primary category | | titleLength | number | Title character count | | descriptionLength | number | Description character count | | screenshotCount | number | Number of screenshots | | languageCount | number | Number of supported languages | | lastUpdated | string | Last update date (ISO 8601) | ## Comparison Fields Difference values: **positive = your app is higher**, negative = competitor is higher. | Field | Type | Description | | --------------------- | ------ | ------------------------------------------------- | | ratingDiff | number | Rating difference (yourRating - competitorRating) | | reviewsDiff | number | Review count difference | | titleLengthDiff | number | Title length difference (chars) | | descriptionLengthDiff | number | Description length difference (chars) | | screenshotCountDiff | number | Screenshot count difference | | languageCountDiff | number | Language count difference | ## Keyword Analysis Fields | Field | Type | Description | | ---------------- | ------ | -------------------------------------------------- | | shared | number | Keywords both apps rank for | | yourUnique | number | Keywords only your app ranks for | | competitorUnique | number | Keywords only the competitor ranks for | | gaps | number | Keywords where competitor outranks you | | overlapPercent | number | Percentage of keywords shared (0-100) | | topGaps | array | Top 10 competitor-unique keywords sorted by volume | ### Top Gap Entry | Field | Type | Description | | -------------- | ------ | ------------------------------------ | | keyword | string | The keyword the competitor ranks for | | competitorRank | number | Competitor's rank position | | volumeScore | number | Search volume score (0-100) | *** **How to use this report:** 1. **Close keyword gaps** — Target `topGaps` keywords with high volume in your next metadata update 2. **Fix weak areas** — If `screenshotCountDiff` or `languageCountDiff` is negative, improve visuals or add localizations 3. **Leverage strengths** — If your `ratingDiff` is positive, mention it in marketing. If `yourUnique` keywords are high-value, protect those positions 4. **Read the verdict** — The automated summary highlights the most important competitive dynamics *** ## Errors | Status | Code | When | | ------ | ----------------- | ----------------------------- | | 400 | INVALID\_APP\_ID | Non-numeric or missing app ID | | 401 | MISSING\_API\_KEY | No API key in the request | | 404 | APP\_NOT\_FOUND | One or both apps not found | # ASO Intent Clusters Source: https://docs.appeeky.com/docs/aso-intent-clusters Group keyword opportunities into semantic intent themes — optional add-on to ASO Brief **Intent clusters** are an optional layer on top of the [ASO Decision Brief](/docs/aso-brief): opportunity keywords are grouped by **meaning** so you can prioritize **themes** (“relaxation / sleep” vs “brain games”) instead of a flat list. *** ## When to use it * You have **many** keyword opportunities and want **fewer, strategy-sized buckets**. * You need a **single “start here” group** (`prioritizeClusterId` in the API response). * You are aligning copy, screenshots, and keyword field around **consistent user intents**. *** ## How it works (short) 1. The brief loads [keyword opportunities](/docs/aso-opportunities) for the app. 2. With `intentClusters=1`, those keywords are analyzed and split into a small number of **intent groups**. 3. Each group gets a **label** (typically the strongest opportunity keyword in that group) and **avgOpportunityScore**. 4. The brief adds an **`intentClusters`** object and usually a **prioritized action** like “Prioritize this intent group: …”. *** ## API Use the same endpoint as the brief: ``` GET /v1/aso/brief/:appId?intentClusters=1&country=us ``` * **`intentClusters=1`** or **`true`** — enable clustering (+2 credits on top of the brief base cost × country multiplier). * **`countries=us,gb,de`** — multi-country brief; clustering runs **per country** (each storefront can have different opportunity mixes). See full parameters and response fields in [ASO Decision Brief](/docs/aso-brief). *** ## MCP Tool: **`aso_brief`** | Argument | Description | | ----------------- | ---------------------------------------------------------------------------- | | `intent_clusters` | `true` to enable semantic intent clustering (maps to `intentClusters` query) | | `countries` | Optional comma-separated ISO codes for multi-country briefs | Auth: same as [MCP Server](/docs/mcp) (`Authorization: Bearer `). *** ## See also * [ASO Decision Brief](/docs/aso-brief) — main orchestration endpoint * [Keyword opportunities](/docs/aso-opportunities) — source keywords for clustering * [MCP Server](/docs/mcp) — `aso_brief` tool reference # Suggest Metadata Source: https://docs.appeeky.com/docs/aso-metadata-suggest Generate optimized app store metadata based on keyword data ``` POST /v1/aso/metadata/suggest ``` Generate optimized metadata suggestions for an app based on target keywords. Returns character-validated suggestions for both Apple App Store (title, subtitle, keyword field) and Google Play Store (title, short description), backed by real keyword volume and difficulty data. *** ## Request Body | Field | Type | Required | Default | Description | | -------- | --------- | -------- | ------- | -------------------------------------------------- | | appId | string | Yes | — | Apple App ID (numeric) | | keywords | string\[] | Yes | — | Target keywords to optimize for (2-10 recommended) | | country | string | No | `us` | ISO country code | *** ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/aso/metadata/suggest" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "913335252", "keywords": ["learn math", "science education", "puzzle solving", "brain training"], "country": "us" }' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/aso/metadata/suggest", { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ appId: "913335252", keywords: ["learn math", "science education", "puzzle solving", "brain training"], country: "us", }), } ); const { data } = await response.json(); console.log("Apple title:", data.apple.title.text); console.log("Apple subtitle:", data.apple.subtitle.text); console.log("Apple keywords:", data.apple.keywords.text); console.log("Google title:", data.google.title.text); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.post( "https://api.appeeky.com/v1/aso/metadata/suggest", headers={"X-API-Key": "YOUR_API_KEY"}, json={ "appId": "913335252", "keywords": ["learn math", "science education", "puzzle solving", "brain training"], "country": "us", }, ) data = response.json()["data"] print(f"Apple Title: {data['apple']['title']['text']} ({data['apple']['title']['charCount']}/{data['apple']['title']['maxChars']})") print(f"Apple Keywords: {data['apple']['keywords']['text']}") ``` *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "apple": { "title": { "text": "Brilliant: Learn Math", "charCount": 21, "maxChars": 30 }, "subtitle": { "text": "Science Education & Puzzles", "charCount": 27, "maxChars": 30 }, "keywords": { "text": "training,brain,solving,puzzle,education,science,interactive,quiz,study", "charCount": 69, "maxChars": 100, "keywordCount": 9 } }, "google": { "title": { "text": "Brilliant - Learn Math & Science Education", "charCount": 43, "maxChars": 50 }, "shortDescription": { "text": "Brilliant helps you with learn math and science education. Download now!", "charCount": 71, "maxChars": 80 } }, "targetKeywords": [ { "keyword": "learn math", "volumeScore": 62, "difficulty": 45 }, { "keyword": "brain training", "volumeScore": 55, "difficulty": 52 }, { "keyword": "science education", "volumeScore": 38, "difficulty": 30 }, { "keyword": "puzzle solving", "volumeScore": 28, "difficulty": 35 } ] } } ``` *** ## Response Fields ### Apple Metadata | Field | Type | Description | | --------------------------- | ------ | --------------------------------------------- | | apple.title.text | string | Suggested title (max 30 chars) | | apple.title.charCount | number | Character count | | apple.subtitle.text | string | Suggested subtitle (max 30 chars) | | apple.keywords.text | string | Comma-separated keyword field (max 100 chars) | | apple.keywords.keywordCount | number | Number of keywords in the field | ### Google Metadata | Field | Type | Description | | ---------------------------- | ------ | ------------------------------------------ | | google.title.text | string | Suggested title (max 50 chars) | | google.shortDescription.text | string | Suggested short description (max 80 chars) | ### Target Keywords | Field | Type | Description | | ----------- | ------ | -------------------------------- | | keyword | string | The target keyword | | volumeScore | number | Search volume score (0-100) | | difficulty | number | Ranking difficulty score (0-100) | *** **How suggestions are generated:** * Keywords are sorted by volume score (highest first) for prioritization * The **top keyword** goes into the title with the brand name * The **2nd-3rd keywords** form the subtitle * The **keyword field** excludes words already in the title (Apple's guideline: don't repeat title words in keywords) * All suggestions are **character-validated** before being returned These are **algorithmic suggestions** based on keyword data. Always review and refine them for natural language and brand voice before submitting to the store. The best metadata reads naturally to humans while incorporating high-value keywords. *** ## Errors | Status | Code | When | | ------ | ----------------- | ---------------------------- | | 400 | INVALID\_APP\_ID | Non-numeric or missing appId | | 400 | INVALID\_KEYWORDS | keywords is missing or empty | | 401 | MISSING\_API\_KEY | No API key in the request | | 404 | APP\_NOT\_FOUND | App not found or unavailable | # Keyword Opportunities Source: https://docs.appeeky.com/docs/aso-opportunities Discover untapped keyword opportunities for an app ``` GET /v1/aso/opportunities/:appId ``` Find high-value keyword opportunities for an app. Discovers keywords the app doesn't rank for yet (high volume + low difficulty), plus existing keywords where rank can be improved. Sorted by opportunity score — the higher the score, the bigger the potential impact. *** ## Path Parameters | Name | Type | Required | Description | | ----- | ------ | -------- | --------------------------------------------------------------------------------------- | | appId | string | Yes | App ID — numeric for Apple (`913335252`), package name for Google (`com.spotify.music`) | ## Query Parameters | Name | Type | Default | Description | | -------- | ------ | ------- | -------------------------------------------------------------------------------- | | platform | string | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | `us` | ISO country code (e.g. `us`, `gb`, `de`) | | lang | string | `en` | Google Play language code (used when `platform=google`) | *** ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/aso/opportunities/913335252?country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/aso/opportunities/913335252?country=us", { headers: { "X-API-Key": "YOUR_API_KEY" }, } ); const { data } = await response.json(); console.log(`Found ${data.summary.totalOpportunities} opportunities`); console.log(`High priority: ${data.summary.highPriority}`); data.opportunities.slice(0, 5).forEach((opp) => { console.log(` "${opp.keyword}" - Score: ${opp.opportunityScore}, Volume: ${opp.volumeScore}, Difficulty: ${opp.difficulty}`); }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/aso/opportunities/913335252", params={"country": "us"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = response.json()["data"] print(f"Total opportunities: {data['summary']['totalOpportunities']}") print(f"High priority: {data['summary']['highPriority']}") for opp in data["opportunities"][:5]: print(f" {opp['keyword']}: score={opp['opportunityScore']}, " f"volume={opp['volumeScore']}, difficulty={opp['difficulty']}, " f"source={opp['source']}") ``` *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "913335252", "country": "us", "opportunities": [ { "keyword": "math learning app", "volumeScore": 58, "difficulty": 32, "opportunityScore": 65, "currentRank": null, "competitorRank": null, "source": "suggestion" }, { "keyword": "physics simulator", "volumeScore": 45, "difficulty": 25, "opportunityScore": 52, "currentRank": null, "competitorRank": null, "source": "suggestion" }, { "keyword": "science quiz", "volumeScore": 40, "difficulty": 38, "opportunityScore": 42, "currentRank": 22, "competitorRank": null, "source": "improvable-rank" } ], "summary": { "totalOpportunities": 18, "highPriority": 4, "mediumPriority": 8, "lowPriority": 6 } } } ``` *** ## Opportunity Fields | Field | Type | Description | | ---------------- | ------------ | -------------------------------------------------------- | | keyword | string | The keyword opportunity | | volumeScore | number | Search volume score (0-100) | | difficulty | number | Ranking difficulty (0-100) | | opportunityScore | number | Composite opportunity score (0-100, higher = better) | | currentRank | number\|null | App's current rank for this keyword (null = not ranking) | | competitorRank | number\|null | Competitor's rank if applicable | | source | string | How the opportunity was found (see below) | ### Source Values | Source | Description | | ----------------- | --------------------------------------------------------------------------------- | | `suggestion` | New keyword from Apple autocomplete suggestions — the app doesn't rank for it yet | | `improvable-rank` | App already ranks but outside top 10 — has room to improve | ## Summary Fields | Field | Type | Description | | ------------------ | ------ | ----------------------------------- | | totalOpportunities | number | Total number of opportunities found | | highPriority | number | Opportunities with score >= 50 | | mediumPriority | number | Opportunities with score 25-49 | | lowPriority | number | Opportunities with score \< 25 | *** **How opportunity score is calculated:** `opportunityScore = volumeScore × easeScore × rankBoost` * **easeScore** = `100 - difficulty` (easier keywords score higher) * **rankBoost** = multiplier based on current rank: * Not ranking (null): 0.5 (moderate upside) * Rank 11-30: 1.0 (sweet spot — close to top 10) * Rank 31+: 0.7 (ranking but far) * Top 3: 0.2 (already optimized, low marginal gain) Focus on **high-priority opportunities** (score >= 50) first — these are keywords with good volume, low competition, and realistic ranking potential. **Best workflow:** 1. Run [ASO Audit](/docs/aso-audit) to understand your baseline 2. Use this endpoint to find opportunities 3. Add target keywords to [Metadata Suggestions](/docs/aso-metadata-suggest) to generate optimized metadata 4. [Validate](/docs/aso-validate-metadata) the new metadata before submitting 5. [Track](/docs/track-keyword) keywords to monitor ranking changes over time *** ## Errors | Status | Code | When | | ------ | ----------------- | ----------------------------- | | 400 | INVALID\_APP\_ID | Non-numeric or missing app ID | | 401 | MISSING\_API\_KEY | No API key in the request | | 404 | APP\_NOT\_FOUND | App not found or unavailable | # Validate Metadata Source: https://docs.appeeky.com/docs/aso-validate-metadata Check app store metadata against Apple and Google character limits ``` POST /v1/aso/metadata/validate ``` Validate your app store metadata against Apple App Store or Google Play Store character limits before submitting. Checks each field for length compliance, detects keyword stuffing, and identifies duplicate keywords. *** ## Request Body | Field | Type | Required | Description | | ---------------- | ------ | -------- | ------------------------------------------------- | | platform | string | Yes | `"apple"` or `"google"` | | title | string | No | App title to validate | | subtitle | string | No | Apple subtitle (Apple only) | | keywords | string | No | Apple keyword field, comma-separated (Apple only) | | shortDescription | string | No | Google short description (Google only) | | fullDescription | string | No | Google full description (Google only) | Include only the fields relevant to your platform. For Apple, send `title`, `subtitle`, and/or `keywords`. For Google, send `title`, `shortDescription`, and/or `fullDescription`. *** ## Character Limits | Platform | Field | Max Characters | | -------- | ---------------- | -------------- | | Apple | title | 30 | | Apple | subtitle | 30 | | Apple | keywords | 100 | | Google | title | 50 | | Google | shortDescription | 80 | | Google | fullDescription | 4,000 | *** ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/aso/metadata/validate" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "platform": "apple", "title": "FitFlow: Fitness Tracker", "subtitle": "Easy Workout Planner & Log", "keywords": "activity,goals,routine,challenge,calories,home,progress,simple,exercise,fitness,beginner" }' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/aso/metadata/validate", { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ platform: "apple", title: "FitFlow: Fitness Tracker", subtitle: "Easy Workout Planner & Log", keywords: "activity,goals,routine,challenge,calories,home,progress,simple,exercise,fitness,beginner", }), } ); const { data } = await response.json(); console.log(`All valid: ${data.allValid}`); data.fields.forEach((f) => console.log(` ${f.field}: ${f.message}`)); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.post( "https://api.appeeky.com/v1/aso/metadata/validate", headers={"X-API-Key": "YOUR_API_KEY"}, json={ "platform": "apple", "title": "FitFlow: Fitness Tracker", "subtitle": "Easy Workout Planner & Log", "keywords": "activity,goals,routine,challenge,calories,home,progress,simple,exercise,fitness,beginner", }, ) data = response.json()["data"] print(f"All valid: {data['allValid']}") print(f"Keyword stuffing: {data['keywordStuffingDetected']}") print(f"Duplicate keywords: {data['duplicateKeywords']}") ``` *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "platform": "apple", "fields": [ { "field": "title", "value": "FitFlow: Fitness Tracker", "charCount": 24, "maxChars": 30, "isValid": true, "message": "24/30 characters (6 remaining)" }, { "field": "subtitle", "value": "Easy Workout Planner & Log", "charCount": 26, "maxChars": 30, "isValid": true, "message": "26/30 characters (4 remaining)" }, { "field": "keywords", "value": "activity,goals,routine,challenge,calories,home,progress,simple,exercise,fitness,beginner", "charCount": 89, "maxChars": 100, "isValid": true, "message": "89/100 characters (11 remaining)" } ], "allValid": true, "keywordStuffingDetected": false, "duplicateKeywords": [] } } ``` ### Example: Invalid Metadata ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "platform": "apple", "fields": [ { "field": "title", "value": "FitFlow: The Best Fitness Tracker App For Everyone", "charCount": 50, "maxChars": 30, "isValid": false, "message": "50/30 characters (20 over limit!)" } ], "allValid": false, "keywordStuffingDetected": true, "duplicateKeywords": [] } } ``` *** ## Response Fields | Field | Type | Description | | ----------------------- | --------- | ---------------------------------------------------------------- | | platform | string | `"apple"` or `"google"` | | fields | array | Validation result for each submitted field | | allValid | boolean | `true` if all fields pass character limits | | keywordStuffingDetected | boolean | `true` if excessive keyword repetition is detected | | duplicateKeywords | string\[] | List of keywords that appear more than once in the keyword field | ### Field Entry | Field | Type | Description | | --------- | ------- | ----------------------------------------------- | | field | string | Field name (e.g. `"title"`, `"keywords"`) | | value | string | The submitted value | | charCount | number | Actual character count | | maxChars | number | Maximum allowed characters | | isValid | boolean | `true` if within character limit | | message | string | Human-readable status with remaining/over count | *** **Apple keyword field tips:** * Use commas without spaces between keywords (saves characters) * Don't repeat words already in your title or subtitle * Don't include your app name or brand * Singular vs plural: Apple matches both, so pick the shorter form * Use all 100 characters — every unused character is a wasted opportunity **Keyword stuffing** will get your app rejected or penalized. Apple and Google both have guidelines against unnatural keyword repetition. The `keywordStuffingDetected` flag warns you when a word appears too frequently (> 5% density in descriptions). *** ## Errors | Status | Code | When | | ------ | ----------------- | ----------------------------------- | | 400 | INVALID\_PLATFORM | platform is not "apple" or "google" | | 401 | MISSING\_API\_KEY | No API key in the request | # Authentication Source: https://docs.appeeky.com/docs/authentication API key management, authentication methods, and security best practices All API endpoints (except `/v1/health`) require authentication. The Appeeky API uses API keys to identify and authorize requests. *** ## Get Your API Key Create a free account on the **Appeeky Dashboard** to get your API key with **100 monthly credits**: Sign up with email or GitHub — your API key is generated instantly. Save your API key immediately. It is displayed **only once** during creation. The key is stored as a SHA-256 hash in our database and cannot be retrieved later. You can regenerate a new key from the dashboard at any time. *** ## Authentication Methods The API supports two ways to pass your API key. ### Method 1: X-API-Key Header (Recommended) Pass the key in the `X-API-Key` header: ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/apps/1617391485?country=us" \ -H "X-API-Key: apk_a1b2c3d4e5f6789012345678abcdef90abcdef1234567890abcdef1234567890ab" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/apps/1617391485?country=us", { headers: { "X-API-Key": "apk_a1b2c3d4e5f6789012345678abcdef90abcdef1234567890abcdef1234567890ab", }, } ); const { data } = await res.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/apps/1617391485", params={"country": "us"}, headers={"X-API-Key": "apk_a1b2c3d4e5f6789012345678abcdef90abcdef1234567890abcdef1234567890ab"}, ) data = res.json()["data"] ``` ### Method 2: Authorization Bearer Header Alternatively, use the standard `Authorization: Bearer` header: ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/apps/1617391485?country=us" \ -H "Authorization: Bearer apk_a1b2c3d4e5f6789012345678abcdef90abcdef1234567890abcdef1234567890ab" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/apps/1617391485?country=us", { headers: { Authorization: "Bearer apk_a1b2c3d4e5f6789012345678abcdef90abcdef1234567890abcdef1234567890ab", }, } ); const { data } = await res.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/apps/1617391485", params={"country": "us"}, headers={"Authorization": "Bearer apk_a1b2c3d4e5f6789012345678abcdef90abcdef1234567890abcdef1234567890ab"}, ) data = res.json()["data"] ``` Both methods are equivalent. Use whichever fits your HTTP client or framework. If both headers are present, `X-API-Key` takes priority. *** ## Check Your Usage Monitor your credit consumption via the API or the [dashboard](https://dashboard.appeeky.com/dashboard/usage): ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/auth/usage" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch("https://api.appeeky.com/v1/auth/usage", { headers: { "X-API-Key": "YOUR_API_KEY" }, }); const { data } = await res.json(); console.log(`${data.used}/${data.monthlyCredits} credits used`); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/auth/usage", headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] print(f'{data["used"]}/{data["monthlyCredits"]} credits used') ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "plan": "free", "monthlyCredits": 100, "used": 47, "remaining": 53, "resetDate": "2026-03-01T00:00:00.000Z", "usageByEndpoint": [ { "endpoint": "GET /apps/:id", "totalCredits": 20, "requestCount": 10 }, { "endpoint": "GET /apps/:id/intelligence", "totalCredits": 25, "requestCount": 5 }, { "endpoint": "GET /search", "totalCredits": 2, "requestCount": 2 } ] } } ``` *** ## Manage Your Account Use the **Appeeky Dashboard** to manage your API key, view usage, and upgrade your plan: | Action | Where | | ---------------------- | ---------------------------------------------------------- | | View & copy API key | [Dashboard](https://dashboard.appeeky.com/dashboard) | | Regenerate API key | [Dashboard](https://dashboard.appeeky.com/dashboard) | | View usage breakdown | [Usage](https://dashboard.appeeky.com/dashboard/usage) | | Upgrade or manage plan | [Billing](https://dashboard.appeeky.com/dashboard/billing) | *** ## Error Responses | Status | Code | When | | ------ | --------------------- | -------------------------------------- | | 401 | `MISSING_API_KEY` | No API key provided in request headers | | 401 | `INVALID_API_KEY` | Key is invalid, inactive, or revoked | | 429 | `RATE_LIMIT_EXCEEDED` | Monthly credit limit reached | **Example error response:** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "error": { "code": "MISSING_API_KEY", "message": "API key is required. Pass it in the X-API-Key header or Authorization: Bearer header." } } ``` # Categories Source: https://docs.appeeky.com/docs/categories List App Store categories and get top apps per category ``` GET /v1/categories ``` Returns all App Store categories with their genre IDs. Use these IDs with the Top Apps endpoint below. Pass `platform=google` to list **Google Play** categories instead (e.g. `GAME`, `MUSIC_AND_AUDIO`). | Name | Type | Default | Description | | -------- | ------ | ------- | -------------------------------------------------------------------------------- | | platform | string | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | ### Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/categories" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch("https://api.appeeky.com/v1/categories", { headers: { "X-API-Key": "YOUR_API_KEY", }, }); const data = await response.json(); console.log(data); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/categories", headers={"X-API-Key": "YOUR_API_KEY"}, ) data = response.json() print(data) ``` ### Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "categories": [ { "id": "6000", "name": "Business" }, { "id": "6001", "name": "Weather" }, { "id": "6002", "name": "Utilities" }, { "id": "6003", "name": "Travel" }, { "id": "6004", "name": "Sports" }, { "id": "6005", "name": "Social Networking" }, { "id": "6006", "name": "Reference" }, { "id": "6007", "name": "Productivity" }, { "id": "6008", "name": "Photo & Video" }, { "id": "6009", "name": "News" }, { "id": "6010", "name": "Navigation" }, { "id": "6011", "name": "Music" }, { "id": "6012", "name": "Lifestyle" }, { "id": "6013", "name": "Health & Fitness" }, { "id": "6014", "name": "Games" }, { "id": "6015", "name": "Finance" }, { "id": "6016", "name": "Entertainment" }, { "id": "6017", "name": "Education" }, { "id": "6018", "name": "Books" }, { "id": "6020", "name": "Medical" }, { "id": "6023", "name": "Food & Drink" }, { "id": "6024", "name": "Shopping" }, { "id": "6025", "name": "Stickers" }, { "id": "6026", "name": "Developer Tools" }, { "id": "6027", "name": "Graphics & Design" } ] } } ``` ### Category Object | Field | Type | Description | | ----- | ------ | -------------------------------------- | | id | string | Apple genre ID (e.g. `6014` for Games) | | name | string | Category display name (e.g. `"Games"`) | *** ## Top Apps in Category ``` GET /v1/categories/:genreId/top ``` Returns top-ranked apps in a specific category, sorted by chart position. Use `all` as the genreId to get the overall chart without filtering by category. ### Path Parameters | Name | Type | Required | Description | | ------- | ------ | -------- | ------------------------------------------------------------------------------------------------------ | | genreId | string | Yes | Genre ID, or `all` for overall chart. Apple: numeric (`6014` for Games); Google: category key (`GAME`) | ### Query Parameters | Name | Type | Default | Description | | -------- | ------ | ---------- | -------------------------------------------------------------------------------- | | platform | string | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | `us` | ISO country code (e.g. `us`, `gb`, `de`) | | chart | string | `top-free` | Chart type: `top-free`, `top-paid`, or `top-grossing` | | lang | string | `en` | Google Play language code (used when `platform=google`) | | limit | number | `25` | Max results (capped at 100) | ### Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/categories/6014/top?country=us&chart=top-free&limit=25" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/categories/6014/top?country=us&chart=top-free&limit=25", { headers: { "X-API-Key": "YOUR_API_KEY", }, } ); const data = await response.json(); console.log(data); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/categories/6014/top", params={"country": "us", "chart": "top-free", "limit": 25}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = response.json() print(data) ``` ### Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "category": { "id": "6014", "name": "Games" }, "chartType": "top-free", "country": "us", "apps": [ { "appId": "1617391485", "rank": 1, "title": "Block Blast!", "developer": "Hungry Studio", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../512x512bb.jpg", "rating": 4.73, "reviewsCount": 1832497, "price": 0, "url": "https://apps.apple.com/us/app/block-blast/id1617391485" }, { "appId": "544007664", "rank": 2, "title": "Candy Crush Saga", "developer": "King", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple221/v4/.../512x512bb.jpg", "rating": 4.69, "reviewsCount": 3254891, "price": 0, "url": "https://apps.apple.com/us/app/candy-crush-saga/id544007664" }, { "appId": "1040639975", "rank": 3, "title": "Hexa Sort", "developer": "IEC Global Pty Ltd", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../512x512bb.jpg", "rating": 4.51, "reviewsCount": 89450, "price": 0, "url": "https://apps.apple.com/us/app/hexa-sort/id1040639975" } ], "total": 25 } } ``` ### App Object | Field | Type | Description | | ------------ | -------------- | ------------------------------- | | appId | string | Apple App ID | | rank | number | Position in the chart (1-based) | | title | string | App name | | developer | string | Developer name | | icon | string | App icon URL (512px) | | rating | number \| null | Average star rating (0–5) | | reviewsCount | number \| null | Total ratings count | | price | number \| null | Price in USD (0 = free) | | url | string | App Store URL | ### Chart Types | Chart | Description | | -------------- | ---------------------------------- | | `top-free` | Most downloaded free apps | | `top-paid` | Most downloaded paid apps | | `top-grossing` | Highest revenue apps (free + paid) | **Use `genreId=all`** to get the overall App Store chart without filtering by category. This returns the raw Apple RSS chart (top-free, top-paid, or top-grossing) enriched with full metadata from iTunes Lookup. Great for seeing what's trending across the entire store. **Per-category filtering**: When using a specific genre ID, the API fetches the overall Apple chart (top 100), filters by genre, and enriches each app with full metadata via iTunes Lookup. If the chart doesn't have enough results for the category, it supplements with iTunes Search results sorted by popularity. ### Errors | Status | Code | When | | ------ | -------------------- | --------------------------------------------------------------------------- | | 400 | INVALID\_GENRE\_ID | genreId not provided or not in the valid list (use `all` for overall chart) | | 400 | INVALID\_CHART\_TYPE | Chart not one of `top-free`, `top-paid`, `top-grossing` | | 401 | MISSING\_API\_KEY | No API key in the request | | 401 | INVALID\_API\_KEY | Invalid or expired API key | # Category Screenshots Source: https://docs.appeeky.com/docs/category-screenshots Get screenshots for top-ranked apps in an App Store category ``` GET /v1/categories/:genreId/top/screenshots ``` Fetch screenshots for the top-ranked apps in a specific App Store category. Acts as a creative explorer for ASO — see what screenshot styles, layouts, and messaging the top apps in any category are using. ## Path Parameters | Name | Type | Required | Description | | ------- | ------ | -------- | ------------------------------------------------------------------------------------ | | genreId | string | Yes | Genre ID, or `all`. Apple: numeric (`6014` for Games); Google: category key (`GAME`) | ## Query Parameters | Name | Type | Default | Description | | -------- | ------ | ---------- | -------------------------------------------------------------------------------- | | platform | string | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | `us` | ISO country code (e.g. `us`, `gb`, `de`, `jp`) | | chart | string | `top-free` | Chart type: `top-free`, `top-paid`, or `top-grossing` | | lang | string | `en` | Google Play language code (used when `platform=google`) | | limit | number | `10` | Number of apps to include (1-25) | ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/categories/6014/top/screenshots?country=us&chart=top-free&limit=10" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/categories/6014/top/screenshots?country=us&chart=top-free&limit=10", { headers: { "X-API-Key": "YOUR_API_KEY", }, } ); const data = await response.json(); console.log(data); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/categories/6014/top/screenshots", params={"country": "us", "chart": "top-free", "limit": 10}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = response.json() print(data) ``` ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "genreId": "6014", "chart": "top-free", "country": "us", "apps": [ { "rank": 1, "appId": "1617391485", "title": "Block Blast!", "developer": "Hungry Studio", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../512x512bb.jpg", "screenshots": { "iphone": [ "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen1.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen2.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen3.jpg" ], "ipad": [ "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../ipad1.jpg" ] }, "screenshotCount": 4 }, { "rank": 2, "appId": "1594703498", "title": "Woodoku", "developer": "Tripledot Studios", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../512x512bb.jpg", "screenshots": { "iphone": [ "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen1.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen2.jpg" ], "ipad": [] }, "screenshotCount": 2 }, { "rank": 3, "appId": "1614645498", "title": "Cube Block - Puzzle Games", "developer": "Easybrain", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../512x512bb.jpg", "screenshots": { "iphone": [ "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen1.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen2.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen3.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen4.jpg" ], "ipad": [ "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../ipad1.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../ipad2.jpg" ] }, "screenshotCount": 6 } ] } } ``` ## Response Fields | Field | Type | Description | | ------- | ------ | ------------------------------------- | | genreId | string | The genre ID that was queried | | chart | string | Chart type used | | country | string | Country code used | | apps | array | Array of ranked apps with screenshots | ### App Entry | Field | Type | Description | | --------------- | ------ | --------------------------------- | | rank | number | Position in the chart (1-based) | | appId | string | Apple App ID | | title | string | App name | | developer | string | Developer / publisher name | | icon | string | App icon URL (512px) | | screenshots | object | Screenshot URLs grouped by device | | screenshotCount | number | Total screenshots (iPhone + iPad) | ### Screenshots Object | Field | Type | Description | | ------ | --------- | ---------------------- | | iphone | string\[] | iPhone screenshot URLs | | ipad | string\[] | iPad screenshot URLs | ## Chart Types | Chart | Description | | -------------- | ---------------------------------- | | `top-free` | Most downloaded free apps | | `top-paid` | Most downloaded paid apps | | `top-grossing` | Highest revenue apps (free + paid) | ## Genre IDs Use `GET /v1/categories` to get the full list. Common genre IDs: | Genre ID | Category | Genre ID | Category | | -------- | ---------------- | -------- | ----------------- | | `6014` | Games | `6015` | Finance | | `6016` | Entertainment | `6017` | Education | | `6018` | Books | `6000` | Business | | `6013` | Health & Fitness | `6005` | Social Networking | | `6007` | Productivity | `all` | All categories | Use `genreId=all` to get screenshots for the overall top apps across all categories — great for spotting cross-category screenshot trends. This endpoint combines two data sources: Apple's RSS charts for ranking data and the iTunes Lookup API for screenshot URLs. If an app appears in the chart but its iTunes lookup fails, it will be included with empty screenshot arrays. ## Errors | Status | Code | When | | ------ | ----------------- | -------------------------------------------------------- | | 400 | INVALID\_GENRE | Genre ID is missing | | 400 | INVALID\_CHART | Chart type not `top-free`, `top-paid`, or `top-grossing` | | 401 | MISSING\_API\_KEY | No API key in the request | | 401 | INVALID\_API\_KEY | Invalid or expired API key | # Competitor Screenshots Source: https://docs.appeeky.com/docs/competitor-screenshots Compare screenshots between an app and its competitors side by side ``` GET /v1/apps/:id/screenshots/competitors ``` Compare App Store screenshots between an app and its automatically detected competitors. Returns screenshot URLs for the target app and up to 10 similar apps, making it easy to analyze creative strategies, screenshot styles, and visual trends in a category. ## Path Parameters | Name | Type | Required | Description | | ---- | ------ | -------- | ---------------------------------------------------------------------------------------- | | id | string | Yes | App ID — numeric for Apple (`1617391485`), package name for Google (`com.spotify.music`) | ## Query Parameters | Name | Type | Default | Description | | -------- | ------ | ------- | -------------------------------------------------------------------------------- | | platform | string | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | `us` | ISO country code (e.g. `us`, `gb`, `de`, `jp`) | | limit | number | `5` | Number of competitor apps to include (1-10) | | lang | string | `en` | Google Play language code (used when `platform=google`) | ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/apps/1617391485/screenshots/competitors?country=us&limit=5" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/apps/1617391485/screenshots/competitors?country=us&limit=5", { headers: { "X-API-Key": "YOUR_API_KEY", }, } ); const data = await response.json(); console.log(data); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/apps/1617391485/screenshots/competitors", params={"country": "us", "limit": 5}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = response.json() print(data) ``` ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "1617391485", "apps": [ { "appId": "1617391485", "title": "Block Blast!", "developer": "Hungry Studio", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../512x512bb.jpg", "screenshots": { "iphone": [ "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen1.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen2.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen3.jpg" ], "ipad": [ "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../ipad1.jpg" ] }, "screenshotCount": 4 }, { "appId": "1594703498", "title": "Woodoku", "developer": "Tripledot Studios", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../512x512bb.jpg", "screenshots": { "iphone": [ "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen1.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen2.jpg" ], "ipad": [] }, "screenshotCount": 2 }, { "appId": "1614645498", "title": "Cube Block - Puzzle Games", "developer": "Easybrain", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../512x512bb.jpg", "screenshots": { "iphone": [ "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen1.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen2.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen3.jpg" ], "ipad": [ "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../ipad1.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../ipad2.jpg" ] }, "screenshotCount": 5 } ] } } ``` ## Response Fields | Field | Type | Description | | ----- | ------ | -------------------------------------------------------------------- | | appId | string | The target app's Apple App ID | | apps | array | Array of app screenshot entries (target app first, then competitors) | ### App Screenshot Entry | Field | Type | Description | | --------------- | ------ | --------------------------------- | | appId | string | Apple App ID | | title | string | App name | | developer | string | Developer / publisher name | | icon | string | App icon URL (512px) | | screenshots | object | Screenshot URLs grouped by device | | screenshotCount | number | Total screenshots (iPhone + iPad) | ### Screenshots Object | Field | Type | Description | | ------ | --------- | ---------------------- | | iphone | string\[] | iPhone screenshot URLs | | ipad | string\[] | iPad screenshot URLs | The **first entry** in the `apps` array is always the target app itself. This makes it easy to compare — just iterate the array and the first element is your baseline. Competitors are automatically detected using a 3-layer matching algorithm: keyword overlap from the database, title search with genre filtering, and same-developer apps. The quality of competitor detection improves when the app has keyword ranking data in the system. ## Errors | Status | Code | When | | ------ | ----------------- | ----------------------------------------------------- | | 400 | INVALID\_APP\_ID | Non-numeric or missing app ID | | 401 | MISSING\_API\_KEY | No API key in the request | | 401 | INVALID\_API\_KEY | Invalid or expired API key | | 404 | APP\_NOT\_FOUND | App not found or unavailable in the specified country | # Country Rankings Source: https://docs.appeeky.com/docs/country-rankings Get an app's chart rankings across all supported countries ``` GET /v1/apps/:id/country-rankings ``` Retrieve an app's chart rankings across all supported countries. Returns the app's position in each country's Top 100 chart where it currently appears. ## Path Parameters | Name | Type | Required | Description | | ---- | ------ | -------- | ---------------------------------------------------------------------------------------- | | id | string | Yes | App ID — numeric for Apple (`1617391485`), package name for Google (`com.spotify.music`) | ## Query Parameters | Name | Type | Default | Description | | -------------- | ------ | ---------- | -------------------------------------------------------------------------------- | | platform | string | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | chart | string | `top-free` | Chart type: `top-free`, `top-paid`, or `top-grossing` | | includeReviews | number | `0` | Set to `1` to include per-country review counts | | lang | string | `en` | Google Play language code (used when `platform=google`) | ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/apps/1617391485/country-rankings?chart=top-free&includeReviews=0" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/apps/1617391485/country-rankings?chart=top-free&includeReviews=0", { headers: { "X-API-Key": "YOUR_API_KEY", }, } ); const data = await response.json(); console.log(data); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/apps/1617391485/country-rankings", params={"chart": "top-free", "includeReviews": 0}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = response.json() print(data) ``` ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "1617391485", "chart": "top-free", "rankings": [ { "rank": 3, "rankChange1D": null, "country": "United States", "countryCode": "US", "reviewsCount": null }, { "rank": 1, "rankChange1D": null, "country": "United Kingdom", "countryCode": "GB", "reviewsCount": null }, { "rank": 7, "rankChange1D": null, "country": "Germany", "countryCode": "DE", "reviewsCount": null }, { "rank": 2, "rankChange1D": null, "country": "France", "countryCode": "FR", "reviewsCount": null }, { "rank": 12, "rankChange1D": null, "country": "Japan", "countryCode": "JP", "reviewsCount": null }, { "rank": 5, "rankChange1D": null, "country": "Canada", "countryCode": "CA", "reviewsCount": null }, { "rank": 9, "rankChange1D": null, "country": "Australia", "countryCode": "AU", "reviewsCount": null }, { "rank": 4, "rankChange1D": null, "country": "Brazil", "countryCode": "BR", "reviewsCount": null }, { "rank": 18, "rankChange1D": null, "country": "India", "countryCode": "IN", "reviewsCount": null }, { "rank": 6, "rankChange1D": null, "country": "Mexico", "countryCode": "MX", "reviewsCount": null } ] } } ``` ### Response with `includeReviews=1` ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "1617391485", "chart": "top-free", "rankings": [ { "rank": 3, "rankChange1D": null, "country": "United States", "countryCode": "US", "reviewsCount": 1832497 }, { "rank": 1, "rankChange1D": null, "country": "United Kingdom", "countryCode": "GB", "reviewsCount": 284510 } ] } } ``` ## Ranking Object | Field | Type | Description | | ------------ | -------------- | ----------------------------------------------------------------------- | | rank | number | Position in the country's Top 100 chart (1–100) | | rankChange1D | number \| null | Rank change in the last 24 hours. Always `null` without historical data | | country | string | Full country name (e.g. `"United States"`) | | countryCode | string | ISO 3166-1 alpha-2 country code (e.g. `"US"`) | | reviewsCount | number \| null | Total review count in that country. `null` unless `includeReviews=1` | ## Chart Types | Chart | Description | | -------------- | ---------------------------------- | | `top-free` | Most downloaded free apps | | `top-paid` | Most downloaded paid apps | | `top-grossing` | Highest revenue apps (free + paid) | ## Supported Countries The API checks the following **26 countries** for chart presence: | Code | Country | Code | Country | Code | Country | | ---- | ------------- | ---- | -------------- | ---- | --------- | | US | United States | GB | United Kingdom | DE | Germany | | FR | France | JP | Japan | CN | China | | KR | South Korea | IN | India | BR | Brazil | | RU | Russia | NG | Nigeria | AR | Argentina | | PK | Pakistan | CA | Canada | AU | Australia | | ES | Spain | IT | Italy | MX | Mexico | | IQ | Iraq | AF | Afghanistan | AO | Angola | | AZ | Azerbaijan | BH | Bahrain | BJ | Benin | | BO | Bolivia | BF | Burkina Faso | | | **Only apps in a country's Top 100 chart will appear.** If an app isn't charting in any country's Top 100 for the specified chart type, the `rankings` array will be empty. A popular app may still not appear if it ranks outside the top 100 in every tracked country. Setting `includeReviews=1` adds per-country review counts to each ranking, but this makes the request **significantly slower** (\~25 extra API calls, one per country). Only enable this when you specifically need per-country review data. An empty `rankings` array doesn't mean the app has zero downloads — it means the app is **not currently in any tracked country's Top 100** for the specified chart type. Try different chart types (`top-free`, `top-paid`, `top-grossing`) for broader results. ## Errors | Status | Code | When | | ------ | -------------------- | -------------------------------------------------------- | | 400 | INVALID\_APP\_ID | Non-numeric or missing app ID | | 400 | INVALID\_CHART\_TYPE | Chart type not `top-free`, `top-paid`, or `top-grossing` | | 401 | MISSING\_API\_KEY | No API key in the request | | 401 | INVALID\_API\_KEY | Invalid or expired API key | | 404 | APP\_NOT\_FOUND | App not found or unavailable | # Discover Source: https://docs.appeeky.com/docs/discover Aggregated discovery data - new releases, category leaders, and trending apps ``` GET /v1/discover ``` Returns aggregated discovery data combining multiple sources into a single response. Use this as a convenience endpoint to power discovery dashboards. ## Query Parameters | Name | Type | Default | Description | | ------- | ------ | ------- | ---------------------------------------- | | country | string | `us` | ISO country code (e.g. `us`, `gb`, `de`) | | limit | number | `25` | Max apps per section (capped at 50) | ## Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/discover?country=us&limit=10" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/discover?country=us&limit=10", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); console.log(data.newReleases); console.log(data.newNumber1); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/discover", params={"country": "us", "limit": 10}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] print(data["newReleases"]) print(data["newNumber1"]) ``` ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "newReleases": [ { "appId": "6758588325", "title": "SocialLife Diary", "developer": "Jelena Popovic", "icon": "https://is1-ssl.mzstatic.com/.../512x512bb.jpg", "category": "Lifestyle", "releasedAt": "2026-02-16T08:00:00Z", "releasedAgo": "today", "url": "https://apps.apple.com/us/app/sociallife-diary/id6758588325" }, { "appId": "6756836995", "title": "Finnet Finance", "developer": "RICHINNOVATIONS TECHNOLOGIES", "icon": "https://is1-ssl.mzstatic.com/.../512x512bb.jpg", "category": "Finance", "releasedAt": "2026-02-16T08:00:00Z", "releasedAgo": "today", "url": "https://apps.apple.com/us/app/finnet-finance/id6756836995" } ], "newNumber1": [ { "category": "Games", "appId": "1617391485", "title": "Block Blast", "icon": "https://is1-ssl.mzstatic.com/.../512x512bb.jpg", "reachedAt": null }, { "category": "Social Networking", "appId": "284882215", "title": "Facebook", "icon": "https://is1-ssl.mzstatic.com/.../512x512bb.jpg", "reachedAt": null } ] } } ``` ## Sections | Section | Description | Source | | ------------- | ---------------------------------- | ---------------------------------------- | | `newReleases` | Apps released in the last 30 days | iTunes Search across multiple categories | | `newNumber1` | Apps currently #1 in each category | Apple RSS Top Free chart | For more granular control, use the individual endpoints directly: * [`GET /v1/new-releases`](/docs/new-releases) for new releases with `maxDays` filter * [`GET /v1/discover/new-number-1`](/docs/new-number-1) for category leaders ## Errors | Status | Code | When | | ------ | ------------- | ----------------------------- | | 500 | FETCH\_FAILED | Failed to fetch upstream data | # Downloads to Top Source: https://docs.appeeky.com/docs/downloads-to-top Estimate daily downloads needed to reach specific chart positions in any App Store category ## Endpoint ``` GET /v1/categories/:genreId/downloads-to-top ``` ## Authentication Requires `X-API-Key` header. ## Path Parameters | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------------------------------------------------------------------------ | | genreId | string | Yes | Genre ID, or `all`. Apple: numeric (`6014` for Games); Google: category key (`GAME`) | ## Query Parameters | Parameter | Type | Default | Description | | --------- | ------ | ---------- | -------------------------------------------------------------------------------- | | platform | string | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | `us` | Two-letter ISO country code | | chart | string | `top-free` | Chart type: `top-free`, `top-paid`, or `top-grossing` | | lang | string | `en` | Google Play language code (used when `platform=google`) | ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "category": { "id": "all", "name": "All" }, "country": "us", "chartType": "top-free", "tiers": [ { "rank": 1, "estimatedDailyDownloads": 699297, "estimatedMonthlyDownloads": 20978910, "app": { "appId": "6448311069", "name": "ChatGPT", "developer": "OpenAI OpCo, LLC", "icon": "https://is1-ssl.mzstatic.com/...", "rating": 4.8, "reviews": 5818679 } }, { "rank": 5, "estimatedDailyDownloads": 223626, "estimatedMonthlyDownloads": 6708780, "app": { "..." : "..." } }, { "rank": 10, "estimatedDailyDownloads": 77183, "estimatedMonthlyDownloads": 2315490, "app": { "..." : "..." } }, { "rank": 25, "estimatedDailyDownloads": 53666, "estimatedMonthlyDownloads": 1609980, "app": { "..." : "..." } }, { "rank": 50, "estimatedDailyDownloads": 31910, "estimatedMonthlyDownloads": 957300, "app": { "..." : "..." } }, { "rank": 100, "estimatedDailyDownloads": 18974, "estimatedMonthlyDownloads": 569220, "app": null } ], "disclaimer": "Download estimates use power-law models calibrated per category. Actual downloads may vary significantly based on seasonality, marketing spend, and viral effects." } } ``` ## Tier Ranks The endpoint returns download estimates for these chart positions: | Rank | Description | | ---- | --------------------------- | | #1 | Top of the chart | | #5 | Top 5 | | #10 | Top 10 | | #25 | Top 25 | | #50 | Top 50 | | #100 | Bottom of the visible chart | ## Fields ### Tier Object | Field | Type | Description | | ------------------------- | ------ | -------------------------------------------- | | rank | number | Chart position | | estimatedDailyDownloads | number | Estimated daily downloads to reach this rank | | estimatedMonthlyDownloads | number | Estimated monthly downloads (daily × 30) | | app | object | The app currently at this rank, or `null` | ### App Object | Field | Type | Description | | --------- | -------------- | ------------------ | | appId | string | App Store ID | | name | string | App name | | developer | string | Developer name | | icon | string | Icon URL | | rating | number \| null | Average rating | | reviews | number \| null | Total review count | ## Methodology Download estimates use a **power-law model** calibrated per category: ``` Downloads(rank) = base / rank^exponent ``` * Parameters are tuned per genre (Games, Social Networking, Finance, etc.) * Paid apps receive \~30× fewer downloads than free apps at the same rank * When actual chart data is available, estimates are blended with review-based signals for improved accuracy ## Examples ### Top Free — All Categories (US) ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -H "X-API-Key: YOUR_KEY" \ "https://api.appeeky.com/v1/categories/all/downloads-to-top?country=us&chart=top-free" ``` ### Top Free — Games (US) ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -H "X-API-Key: YOUR_KEY" \ "https://api.appeeky.com/v1/categories/6014/downloads-to-top?country=us&chart=top-free" ``` ### Top Paid — All Categories (GB) ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -H "X-API-Key: YOUR_KEY" \ "https://api.appeeky.com/v1/categories/all/downloads-to-top?country=gb&chart=top-paid" ``` ## Credit Cost **2 credits** per request. # Featured Apps Source: https://docs.appeeky.com/docs/featured Apps featured on the Apple App Store Today tab, including App of the Day, Game of the Day, and curated editorial collections ``` GET /v1/featured ``` Returns apps currently featured on the Apple App Store **Today** tab. Includes App of the Day, Game of the Day, and editorially curated collections like "Hot This Week", "Indie Games We Love", etc. ## Query Parameters | Name | Type | Default | Description | | ------- | ------ | ------- | ---------------------------------------- | | country | string | `us` | ISO country code (e.g. `us`, `gb`, `de`) | ## Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/featured?country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/featured?country=us", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); console.log("App of the Day:", data.appOfTheDay); console.log("Game of the Day:", data.gameOfTheDay); console.log("Featured groups:", data.groups.length); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/featured", params={"country": "us"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] print("App of the Day:", data["appOfTheDay"]["name"]) print("Game of the Day:", data["gameOfTheDay"]["name"]) ``` ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "country": "us", "date": "2026-02-24", "appOfTheDay": { "appId": "1583614988", "name": "Oko - Cross streets and Maps", "developer": "Polara Engineering, Inc.", "icon": "https://is1-ssl.mzstatic.com/.../256x256bb.png", "genre": "Navigation", "url": "https://apps.apple.com/us/app/oko-cross-streets-and-maps/id1583614988" }, "gameOfTheDay": { "appId": "6742443980", "name": "Blade & Soul Heroes", "developer": "NCSOFT", "icon": "https://is1-ssl.mzstatic.com/.../256x256bb.png", "genre": "Roleplaying", "url": "https://apps.apple.com/us/app/blade-soul-heroes/id6742443980" }, "groups": [ { "id": "1694921970", "name": "Turn-Based Games", "tagline": null, "type": "GameOfTheDay", "apps": [ { "appId": "6742443980", "name": "Blade & Soul Heroes", "developer": "NCSOFT", "icon": "https://is1-ssl.mzstatic.com/.../256x256bb.png", "genre": "Roleplaying", "url": "https://apps.apple.com/us/app/blade-soul-heroes/id6742443980" } ] }, { "id": "1698371075", "name": "Hot This Week", "tagline": "Play these new launches and updates", "type": "ShortImage", "apps": [ { "appId": "1174078549", "name": "Apple TV", "developer": "Apple", "icon": "https://is1-ssl.mzstatic.com/.../256x256bb.png", "genre": "Entertainment", "url": "https://apps.apple.com/us/app/apple-tv/id1174078549" } ] } ] } } ``` ## Response Fields ### Top Level | Field | Type | Description | | -------------- | ---------------- | ------------------------------------------------ | | `country` | string | ISO country code | | `date` | string | Date of the featured content (YYYY-MM-DD) | | `appOfTheDay` | FeaturedApp | Today's App of the Day (null if not available) | | `gameOfTheDay` | FeaturedApp | Today's Game of the Day (null if not available) | | `groups` | FeaturedGroup\[] | Curated editorial collections with featured apps | ### FeaturedApp | Field | Type | Description | | ----------- | ------ | ---------------------- | | `appId` | string | Apple App ID | | `name` | string | App name | | `developer` | string | Developer name | | `icon` | string | App icon URL (256x256) | | `genre` | string | Primary genre | | `url` | string | App Store URL | ### FeaturedGroup | Field | Type | Description | | --------- | -------------- | -------------------------------------------------------------- | | `id` | string | Apple editorial group ID | | `name` | string | Collection name (e.g. "Hot This Week") | | `tagline` | string\|null | Short description (e.g. "Play these new launches") | | `type` | string | Card display type: `AppOfTheDay`, `GameOfTheDay`, `ShortImage` | | `apps` | FeaturedApp\[] | Apps in this collection | ## Group Types | Type | Description | | -------------- | ----------------------------------------------- | | `AppOfTheDay` | Featured apps with large editorial cards | | `GameOfTheDay` | Featured games with large editorial cards | | `ShortImage` | Curated collections with smaller app cards | | `AppEventCard` | In-app event promotions (typically no app data) | ## Errors | Status | Code | When | | ------ | --------------- | ------------------------------------ | | 500 | INTERNAL\_ERROR | Failed to fetch Apple editorial data | # Get App Source: https://docs.appeeky.com/docs/get-app Fetch full app metadata from iTunes Lookup API ``` GET /v1/apps/:id ``` Fetch complete app metadata from the iTunes Lookup API. ## Path Parameters | Name | Type | Required | Description | | ---- | ------ | -------- | ---------------------------------------------------------------------------------------- | | id | string | Yes | App ID — numeric for Apple (`1617391485`), package name for Google (`com.spotify.music`) | ## Query Parameters | Name | Type | Default | Description | | -------- | ------ | -------- | -------------------------------------------------------------------------------- | | platform | string | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | `us` | ISO country code (e.g. `us`, `gb`, `de`, `jp`) | | device | string | `iphone` | Device type: `iphone` or `ipad` (Apple only) | | lang | string | `en` | Google Play language code (used when `platform=google`) | ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/apps/1617391485?country=us&device=iphone" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/apps/1617391485?country=us&device=iphone", { headers: { "X-API-Key": "YOUR_API_KEY", }, } ); const data = await response.json(); console.log(data); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/apps/1617391485", params={"country": "us", "device": "iphone"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = response.json() print(data) ``` ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "1617391485", "title": "Block Blast!", "developer": "Hungry Studio", "iconUrl": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/07/07/46/070746fc-c20c-72d6-c067-f2e405e0c29e/AppIcon-0-0-1x_U007emarketing-0-7-0-85-220.png/512x512bb.jpg", "metadata": { "trackId": 1617391485, "trackName": "Block Blast!", "artistName": "Hungry Studio", "bundleId": "com.tsgames.blockblast", "artworkUrl100": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/07/07/46/070746fc-c20c-72d6-c067-f2e405e0c29e/AppIcon-0-0-1x_U007emarketing-0-7-0-85-220.png/100x100bb.jpg", "artworkUrl512": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/07/07/46/070746fc-c20c-72d6-c067-f2e405e0c29e/AppIcon-0-0-1x_U007emarketing-0-7-0-85-220.png/512x512bb.jpg", "description": "Block Blast is a classic yet addictive block puzzle game...", "price": 0, "primaryGenreName": "Games", "genres": ["Games", "Puzzle", "Casual"], "averageUserRating": 4.73321, "userRatingCount": 1832497, "version": "4.1.2", "releaseNotes": "Bug fixes and performance improvements.", "releaseDate": "2022-10-06T07:00:00Z", "currentVersionReleaseDate": "2026-01-28T15:42:00Z", "trackViewUrl": "https://apps.apple.com/us/app/block-blast/id1617391485", "fileSizeBytes": "227541504", "minimumOsVersion": "16.0", "contentAdvisoryRating": "4+", "languageCodesISO2A": ["EN", "FR", "DE", "ES", "IT", "JA", "KO", "PT", "ZH"], "screenshotUrls": [ "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen1.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen2.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../screen3.jpg" ], "ipadScreenshotUrls": [ "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../ipad-screen1.jpg" ] }, "averageUserRating": 4.73321, "userRatingCount": 1832497 } } ``` ## Top-Level Fields | Field | Type | Description | | ----------------- | -------------- | --------------------------------------- | | appId | string | Apple App ID | | title | string | App name | | developer | string | Developer / publisher name | | iconUrl | string \| null | App icon URL (512px) | | averageUserRating | number \| null | Average star rating (0–5) | | userRatingCount | number \| null | Total number of ratings | | metadata | object | Full iTunes Lookup response (see below) | ## Metadata Object (iTunes Lookup) | Field | Type | Description | | ------------------------- | --------- | ------------------------------------------------- | | trackId | number | Apple App ID (numeric) | | trackName | string | App name | | artistName | string | Developer name | | bundleId | string | Bundle identifier (e.g. `com.tsgames.blockblast`) | | artworkUrl100 | string | Icon URL 100px | | artworkUrl512 | string | Icon URL 512px | | description | string | Full app description | | price | number | App price (0 = free) | | primaryGenreName | string | Primary category (e.g. `Games`) | | genres | string\[] | All categories | | averageUserRating | number | Star rating (0–5) | | userRatingCount | number | Total ratings count | | version | string | Current version | | releaseNotes | string | Latest version release notes | | releaseDate | string | Original release date (ISO 8601) | | currentVersionReleaseDate | string | Current version release date (ISO 8601) | | trackViewUrl | string | App Store URL | | fileSizeBytes | string | App size in bytes | | minimumOsVersion | string | Minimum iOS version (e.g. `16.0`) | | contentAdvisoryRating | string | Age rating (e.g. `4+`, `12+`) | | languageCodesISO2A | string\[] | Supported language codes (e.g. `["EN","FR"]`) | | screenshotUrls | string\[] | iPhone screenshot URLs | | ipadScreenshotUrls | string\[] | iPad screenshot URLs | The `metadata` object contains the **raw iTunes Lookup API response**. Additional fields beyond those listed above may be present depending on the app. The structure mirrors Apple's iTunes Search API output exactly. Use this endpoint for **basic app lookups** (metadata, ratings, screenshots). For a full intelligence report including revenue estimates, download estimates, similar apps, and in-app purchases, use the [Intelligence endpoint](/docs/get-app-intelligence) instead. ## Errors | Status | Code | When | | ------ | ----------------- | ----------------------------------------------------- | | 400 | INVALID\_APP\_ID | Non-numeric or missing app ID | | 401 | MISSING\_API\_KEY | No API key in the request | | 401 | INVALID\_API\_KEY | Invalid or expired API key | | 404 | APP\_NOT\_FOUND | App not found or unavailable in the specified country | # Get App Intelligence Source: https://docs.appeeky.com/docs/get-app-intelligence Full intelligence report - metadata, revenue, downloads, IAPs, sentiment ``` GET /v1/apps/:id/intelligence ``` Full app intelligence report including metadata, market estimates (downloads and revenue), in-app purchases, and sentiment analysis. This is the most comprehensive endpoint for analyzing any iOS app. **Similar apps have moved to a dedicated endpoint.** Use [`GET /v1/apps/:id/similar`](/docs/get-similar-apps) to fetch similar apps separately. The intelligence endpoint now returns `similarApps: []` by default. ## Path Parameters | Name | Type | Required | Description | | ---- | ------ | -------- | --------------------------------------------------------------------------------------- | | id | string | Yes | App ID — numeric for Apple (`913335252`), package name for Google (`com.spotify.music`) | ## Query Parameters | Name | Type | Default | Description | | -------- | ------ | ------- | -------------------------------------------------------------------------------- | | platform | string | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | `us` | ISO country code (e.g. `us`, `gb`, `de`) | | lang | string | `en` | Google Play language code (used when `platform=google`) | ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/apps/913335252/intelligence?country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/apps/913335252/intelligence?country=us", { headers: { "X-API-Key": "YOUR_API_KEY", }, } ); const data = await response.json(); console.log(data); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/apps/913335252/intelligence", params={"country": "us"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = response.json() print(data) ``` ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "app": { "appId": "913335252", "title": "Brilliant: Learn by doing", "url": "https://apps.apple.com/us/app/brilliant-learn-by-doing/id913335252", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple221/v4/c8/d1/72/c8d172e7-1a89-cc7d-2a2e-4e8b3e6a1a2e/AppIcon-0-0-1x_U007emarketing-0-7-0-85-220.png/512x512bb.jpg", "description": "Master concepts in math, data science, and computer science through fun, bite-sized lessons. Brilliant makes learning interactive — you won't just watch, you'll solve puzzles, build intuition, and develop real skills.", "rating": 4.74, "ratingMax": 5, "reviewsCount": 27315, "isFree": true, "price": 0, "formattedPrice": "Free", "mainCategory": "Education", "categories": ["Education", "Reference"], "developer": "Brilliant.org", "lastUpdateDate": "2026-01-26T12:19:36Z", "screenshots": [ "https://is1-ssl.mzstatic.com/image/thumb/Purple221/v4/.../screen1.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple221/v4/.../screen2.jpg", "https://is1-ssl.mzstatic.com/image/thumb/Purple221/v4/.../screen3.jpg" ], "provider": "Brilliant.org", "fileSizeBytes": "88879104", "minimumOsVersion": "17.0", "contentAdvisoryRating": "4+", "languageCodes": ["EN"], "inAppPurchases": [ { "name": "Brilliant Premium", "formattedPrice": "$149.99" }, { "name": "Brilliant Premium", "formattedPrice": "$127.99" }, { "name": "Brilliant Premium: Fun, challenging problems in math and science by experts", "formattedPrice": "$24.99" }, { "name": "Brilliant Premium", "formattedPrice": "$24.99" }, { "name": "Brilliant Premium - Personal stats & exclusive problems", "formattedPrice": "$19.99" } ] }, "market": { "estimatedDownloads": 2458350, "downloadsRange": { "low": 1229175, "high": 4916700 }, "downloadsSource": "reviews-heuristic", "downloadsDisclaimer": "Estimated total lifetime downloads · reviewsCount × 90 (Education benchmark)", "downloadsConfidence": "low", "topFreeRank": null, "topPaidRank": null, "downloadsTrend": null, "estimatedRevenue": 14045, "revenueRange": { "low": 4214, "high": 42135 }, "revenueSource": "iap-heuristic", "revenueDisclaimer": "Estimated daily revenue (USD) · ARPU × engagement model (Education)", "revenueConfidence": "medium", "revenueTrend": null, "topGrossingRank": null, "topGrossingCategory": "Education", "topCountries": [ { "code": "US", "name": "United States", "share": 100 } ], "sentiment": { "positive": 95, "negative": 5, "basedOnRatings": 27315 }, "appPower": null }, "similarApps": [] } } ``` *** ## App Object | Field | Type | Description | | --------------------- | --------- | ------------------------------------------------- | | appId | string | Apple App ID | | title | string | App name | | url | string | App Store URL | | icon | string | App icon URL (512px) | | description | string | Full app description | | rating | number | Average star rating (0–5) | | ratingMax | number | Always `5` | | reviewsCount | number | Total number of ratings | | isFree | boolean | `true` if price is 0 | | price | number | App price in USD (0 = free) | | formattedPrice | string | Localized price string (e.g. `"Free"`, `"$4.99"`) | | mainCategory | string | Primary category (e.g. `Education`) | | categories | string\[] | All categories | | developer | string | Developer name | | lastUpdateDate | string | Last update date (ISO 8601) | | screenshots | string\[] | Screenshot URLs (max 6) | | provider | string | Publisher / provider name | | fileSizeBytes | string | App size in bytes | | minimumOsVersion | string | Minimum iOS version (e.g. `17.0`) | | contentAdvisoryRating | string | Age rating (e.g. `4+`, `12+`, `17+`) | | languageCodes | string\[] | Supported language codes (e.g. `["EN","FR"]`) | | inAppPurchases | array | In-app purchase / subscription list (see below) | ### In-App Purchases | Field | Type | Description | | -------------- | ------ | ---------------------------------------------- | | name | string | Purchase name (e.g. `"Premium Annual Plan"`) | | formattedPrice | string | Localized price (e.g. `"$149.99"`, `"$24.99"`) | In-app purchases are sourced from the official App Store web page. Apple embeds this data in structured JSON on the product page. The list includes all subscriptions, consumables, and non-consumables visible on the App Store listing. *** ## Market Object ### Downloads | Field | Type | Description | | ------------------- | -------------- | ----------------------------------------------------------------- | | estimatedDownloads | number | Estimated downloads (daily if chart-based, lifetime if heuristic) | | downloadsRange | object | `{ low, high }` confidence interval | | downloadsSource | string | `"top-free-chart"`, `"top-paid-chart"`, or `"reviews-heuristic"` | | downloadsDisclaimer | string | Human-readable methodology note | | downloadsConfidence | string | `"high"`, `"medium"`, or `"low"` | | topFreeRank | number \| null | Rank on Top Free chart (`null` if not charting) | | topPaidRank | number \| null | Rank on Top Paid chart (`null` if not charting) | | downloadsTrend | string \| null | `"up"`, `"down"`, or `null` | **How downloads are estimated** (similar to Sensor Tower, AppTweak): 1. **Top Free/Paid chart** (high confidence): Power-law model calibrated per category. Top 1 free app ≈ 500K–1.5M downloads/day, dropping off steeply by rank. 2. **Reviews heuristic** (low confidence): `reviewsCount × category ratio` (industry benchmark: 1 review per 60–200 downloads depending on category). The `downloadsSource` field tells you which method was used, and `downloadsConfidence` indicates reliability. ### Revenue | Field | Type | Description | | ------------------- | -------------- | ------------------------------------------------------------------ | | estimatedRevenue | number | Estimated daily revenue (USD) | | revenueRange | object | `{ low, high }` confidence interval | | revenueSource | string | `"top-grossing-chart"`, `"paid-price-model"`, or `"iap-heuristic"` | | revenueDisclaimer | string | Human-readable methodology note | | revenueConfidence | string | `"high"`, `"medium"`, or `"low"` | | revenueTrend | string \| null | `"up"`, `"down"`, or `null` | | topGrossingRank | number \| null | Rank on Top Grossing chart (`null` if not charting) | | topGrossingCategory | string \| null | Category for Top Grossing rank | **How revenue is estimated** (modeled after industry leaders): 1. **Top Grossing chart** (high confidence): Power-law model with category-specific parameters. Games top 1 ≈ $2.5M/day, Finance top 1 ≈ $1.4M/day. 2. **Paid price model** (medium confidence): `price × estimated daily sales × 0.70 net` (Apple takes a 30% cut). 3. **IAP/subscription heuristic** (low confidence): `estimated MAU × monthly ARPU / 30` where ARPU varies by category (Games $0.80, Music $2.50, Finance $1.80, Education $1.20). ### Sentiment | Field | Type | Description | | -------------- | ------ | ------------------------------------------------ | | positive | number | Positive sentiment percentage (0–100) | | negative | number | Negative sentiment percentage (0–100) | | basedOnRatings | number | Number of ratings used for sentiment calculation | ### Other Market Fields | Field | Type | Description | | ------------ | -------------- | --------------------------------------------------- | | topCountries | array | Countries with `code`, `name`, `share` (percentage) | | appPower | number \| null | Composite score (reserved for future use) | The `topCountries` array is derived from [country rankings](/docs/country-rankings). The share percentage indicates the relative distribution of the app's chart presence across countries. If the app only charts in one country, that country will show `100` share. *** ## Similar Apps (Deprecated) The `similarApps` field in the intelligence response is now always an empty array. Use the dedicated [`GET /v1/apps/:id/similar`](/docs/get-similar-apps) endpoint instead. This separation allows the intelligence report to return much faster (\~4-6s vs 30+s). | Field | Type | Description | | ----------- | ----- | ------------------------------------------------ | | similarApps | array | Always `[]` — use `/v1/apps/:id/similar` instead | *** ## Errors | Status | Code | When | | ------ | ----------------- | ----------------------------- | | 400 | INVALID\_APP\_ID | Non-numeric or missing app ID | | 401 | MISSING\_API\_KEY | No API key in the request | | 401 | INVALID\_API\_KEY | Invalid or expired API key | | 404 | APP\_NOT\_FOUND | App not found or unavailable | # Get App Keywords Source: https://docs.appeeky.com/docs/get-app-keywords Enriched organic keyword rankings for an app with auto-discovery ``` GET /v1/apps/:id/keywords ``` Returns enriched organic keyword rankings for the specified app. When no keyword data exists, the API **automatically discovers keywords** — no special flags needed. Each keyword includes volume, difficulty, opportunity scores, rank changes, and trend direction. *** ## Path Parameters | Name | Type | Description | | ---- | ------ | ---------------------------------------------------------------------------------------- | | id | string | App ID — numeric for Apple (`1617391485`), package name for Google (`com.spotify.music`) | ## Query Parameters | Name | Type | Default | Description | | -------- | ------ | -------- | ------------------------------------------------------------------------------------------------- | | platform | string | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | `us` | ISO country code (e.g. `us`, `gb`, `de`) | | device | string | `iphone` | `iphone` or `ipad` (Apple only) | | lang | string | `en` | Google Play language code (used when `platform=google`) | | discover | string | — | **Deprecated.** Auto-discovery is now the default behavior. Passing `1` has no additional effect. | *** ## Code Examples ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/apps/1617391485/keywords?country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/apps/1617391485/keywords?country=us", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); data.keywords.forEach((kw) => { console.log(`${kw.keyword}: rank #${kw.rank}, opportunity ${kw.opportunity}`); }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/apps/1617391485/keywords", params={"country": "us"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] for kw in data["keywords"]: print(f"{kw['keyword']}: rank #{kw['rank']}, opportunity {kw['opportunity']}") ``` *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "1617391485", "keywords": [ { "keyword": "puzzle game", "rank": 5, "appsCount": 215, "popularity": 86, "competitiveness": 97, "volumeScore": 72, "difficulty": 85, "opportunity": 34, "rankChange7d": 3, "trend": "improving" }, { "keyword": "block puzzle", "rank": 2, "appsCount": 180, "popularity": 72, "competitiveness": 82, "volumeScore": 65, "difficulty": 68, "opportunity": 58, "rankChange7d": -1, "trend": "declining" }, { "keyword": "brain teaser", "rank": 18, "appsCount": 120, "popularity": 48, "competitiveness": 55, "volumeScore": 44, "difficulty": 42, "opportunity": 71, "rankChange7d": 0, "trend": "stable" } ] } } ``` *** ## Keyword Object | Field | Type | Description | | --------------- | ------ | ------------------------------------------------------------------------------------------------------ | | keyword | string | The search keyword | | rank | number | App's organic rank for this keyword (1 = top result) | | appsCount | number | Total number of apps ranking for this keyword | | popularity | number | **Deprecated.** Legacy popularity score (0–100). Use `volumeScore` instead. | | competitiveness | number | **Deprecated.** Legacy competition score (0–100). Use `difficulty` instead. | | volumeScore | number | Estimated search volume score (0–100). Higher = more searches. | | difficulty | number | Ranking difficulty score (0–100). Higher = harder to rank. | | opportunity | number | Composite opportunity score (0–100). Balances volume, difficulty, and current rank — higher is better. | | rankChange7d | number | Rank change over the last 7 days. Positive = improved (rank number decreased). | | trend | string | Trend direction: `"improving"`, `"declining"`, or `"stable"` | *** ## Metric Details ### volumeScore (0–100) Estimated relative search volume for the keyword. Derived from iTunes result counts, top app review volumes, and autocomplete position. A score of **80+** indicates a high-traffic keyword. ### difficulty (0–100) How hard it is to rank in the top results for this keyword. Factors in average rating of top apps, review count distribution, developer diversity, and market dominance. A score of **70+** means the keyword is dominated by strong incumbents. ### opportunity (0–100) A composite score that combines volume, difficulty, and your current rank into a single prioritization metric. **High opportunity** means the keyword has good search volume, manageable difficulty, and room for your app to climb. ### trend | Value | Meaning | | ----------- | ------------------------------------------------------- | | `improving` | Rank has been getting better (lower number) over 7 days | | `declining` | Rank has been getting worse (higher number) over 7 days | | `stable` | Rank has not changed significantly | *** ## Auto-Discovery When no keyword data exists for an app, the API automatically runs keyword discovery: 1. **Extracts keywords** from the app's title, subtitle, and description 2. **Adds category-relevant keywords** based on the app's primary category 3. **Expands with Apple Search Suggestions** to find related long-tail keywords 4. **Queries iTunes Search** for up to **30 keywords** to determine rankings 5. **Returns enriched results** immediately with all scoring metrics Auto-discovery runs transparently on the first request for any app. Subsequent requests return stored data instantly. There is no need to pass `discover=1` — this parameter is deprecated and kept only for backward compatibility. **Prioritize with opportunity score**: Sort keywords by `opportunity` descending to find the best keywords to optimize for. High opportunity keywords have strong search volume, achievable difficulty, and room for rank improvement — these are your quick wins for ASO. *** ## Errors | Status | Code | When | | ------ | ---------------- | ----------------------------- | | 400 | INVALID\_APP\_ID | Non-numeric or missing app ID | | 401 | UNAUTHORIZED | Missing or invalid API key | | 404 | APP\_NOT\_FOUND | App does not exist in iTunes | | 429 | RATE\_LIMITED | Too many requests — slow down | | 500 | INTERNAL\_ERROR | Server error during discovery | # Get App Reviews Source: https://docs.appeeky.com/docs/get-app-reviews User reviews from Apple RSS feed ``` GET /v1/apps/:id/reviews ``` Fetches user reviews from Apple's public RSS feed. Returns up to 50 reviews per page, with a maximum of 10 pages (500 reviews total per country). ## Path Parameters | Name | Type | Required | Description | | ---- | ------ | -------- | ---------------------------------------------------------------------------------------- | | id | string | Yes | App ID — numeric for Apple (`1617391485`), package name for Google (`com.spotify.music`) | ## Query Parameters | Name | Type | Default | Description | | -------- | ------ | ------------ | -------------------------------------------------------------------------------- | | platform | string | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | `us` | ISO country code (e.g. `us`, `gb`, `jp`) | | page | number | `1` | Page number (1–10) | | sortBy | string | `mostRecent` | Sort order: `mostRecent` or `mostHelpful` | | lang | string | `en` | Google Play language code (used when `platform=google`) | ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/apps/1617391485/reviews?country=us&page=1&sortBy=mostRecent" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/apps/1617391485/reviews?country=us&page=1&sortBy=mostRecent", { headers: { "X-API-Key": "YOUR_API_KEY", }, } ); const data = await response.json(); console.log(data); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/apps/1617391485/reviews", params={"country": "us", "page": 1, "sortBy": "mostRecent"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = response.json() print(data) ``` ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "1617391485", "reviews": [ { "author": "PuzzleFan92", "rating": 5, "title": "Best puzzle game ever!", "content": "I've been playing this for months. The levels are challenging but fair, and the design is beautiful. Highly recommended for anyone who loves block puzzles.", "date": "2026-02-10T12:00:00Z" }, { "author": "CasualGamer_Jake", "rating": 4, "title": "Fun but too many ads", "content": "Great gameplay and very addictive, but the ads between levels are a bit much. Would happily pay to remove them if that was an option.", "date": "2026-02-09T08:30:00Z" }, { "author": "iPhoneUser2024", "rating": 2, "title": "Crashes after latest update", "content": "The app keeps crashing after the latest update on my iPhone 14. Was working perfectly before. Please fix this ASAP.", "date": "2026-02-08T15:45:00Z" } ], "hasMore": true, "page": 1 } } ``` ## Review Object | Field | Type | Description | | ------- | ------ | ---------------------------------------------------- | | author | string | Reviewer display name | | rating | number | Star rating (1–5) | | title | string | Review title / headline | | content | string | Review body text (only reviews with written content) | | date | string | Review date (ISO 8601) | ## Pagination | Field | Type | Description | | ------- | ------- | ---------------------------------- | | hasMore | boolean | `true` if more pages are available | | page | number | Current page number | Apple's RSS feed provides a maximum of **10 pages** with up to **50 reviews per page**, giving a ceiling of **500 reviews** per country. **Sort options:** * `mostRecent` — Returns the newest reviews first. Best for monitoring recent user sentiment and catching new issues. * `mostHelpful` — Returns reviews that other users found most helpful. Best for understanding the most impactful feedback. Reviews without written content are automatically filtered out. The first entry in Apple's RSS feed (app summary) is also excluded. **Filtering strategies:** To analyze reviews effectively, consider: * Paginate through all 10 pages to get a comprehensive view (up to 500 reviews) * Use `mostRecent` to track sentiment after an app update * Use `mostHelpful` to surface the most impactful user feedback * Combine with different `country` codes to compare regional sentiment * Filter by `rating` on the client side to focus on negative reviews (1–2 stars) or positive ones (4–5 stars) ## Errors | Status | Code | When | | ------ | ----------------- | ------------------------------ | | 400 | INVALID\_APP\_ID | Non-numeric or missing app ID | | 400 | INVALID\_PAGE | Page number outside 1–10 range | | 401 | MISSING\_API\_KEY | No API key in the request | | 401 | INVALID\_API\_KEY | Invalid or expired API key | # Get Keyword Ranks Source: https://docs.appeeky.com/docs/get-keyword-ranks Get all apps ranking for a given keyword with live iTunes Search fallback ``` GET /v1/keywords/ranks ``` Returns all apps that rank for a specific keyword, ordered by rank. This endpoint **never returns empty results** if iTunes has data — it checks for stored rankings first, and automatically falls back to a **live iTunes Search** when no stored data exists. *** ## Query Parameters | Name | Type | Required | Default | Description | | --------- | ------ | -------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------- | | keyword | string | Yes | — | Search keyword (min 2 characters) | | platform | string | No | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | No | `us` | ISO country code — used when **`countries` is omitted** (legacy single-store) | | countries | string | No | — | Comma-separated ISO codes (e.g. `us,gb,de`) for **multi-country** results in one request (max 25). When set, **`country` is ignored**. | | device | string | No | `iphone` | `iphone` or `ipad` (Apple only) | | lang | string | No | `en` | Google Play language code (used when `platform=google`) | | date | string | No | today | Date in `YYYY-MM-DD` format | Without `countries`, the response shape is unchanged (single `country` + `ranks`). With `countries`, the response includes `countries` and a `results` object keyed by ISO code — each value matches the single-country payload for that storefront. *** ## Code Examples ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/keywords/ranks?keyword=puzzle+game&country=us&device=iphone" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/keywords/ranks?keyword=puzzle+game&country=us&device=iphone", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); console.log(`Found ${data.ranks.length} apps for "${data.keyword}"`); console.log(`Volume Score: ${data.volumeScore}, Difficulty: ${data.difficulty}`); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/keywords/ranks", params={"keyword": "puzzle game", "country": "us", "device": "iphone"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] print(f"Found {len(data['ranks'])} apps for \"{data['keyword']}\"") print(f"Volume Score: {data['volumeScore']}, Difficulty: {data['difficulty']}") ``` *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "keyword": "puzzle game", "country": "us", "volumeScore": 72, "difficulty": 85, "resultCount": 3450, "ranks": [ { "appId": "1617391485", "rank": 1, "title": "Block Blast!", "developer": "Hungry Studio", "iconUrl": "https://is1-ssl.mzstatic.com/.../512x512bb.jpg" }, { "appId": "544007664", "rank": 2, "title": "Candy Crush Saga", "developer": "King", "iconUrl": "https://is1-ssl.mzstatic.com/.../512x512bb.jpg" }, { "appId": "1040639975", "rank": 3, "title": "Hexa Sort", "developer": "IEC Global", "iconUrl": "https://is1-ssl.mzstatic.com/.../512x512bb.jpg" } ] } } ``` **Multi-country** (`?countries=us,gb`): ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "keyword": "puzzle game", "countries": ["us", "gb"], "results": { "us": { "keyword": "puzzle game", "country": "us", "volumeScore": 72, "difficulty": 85, "resultCount": 3450, "ranks": [] }, "gb": { "keyword": "puzzle game", "country": "gb", "volumeScore": 70, "difficulty": 80, "resultCount": 3200, "ranks": [] } } } } ``` *** ## Response Fields | Field | Type | Description | | ------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------- | | keyword | string | The queried keyword | | country | string | ISO country code (single-country response only) | | countries | array | List of ISO codes (multi-country response only) | | results | object | Per-country payloads keyed by ISO code (multi-country only) | | volumeScore | number | Estimated search volume score (0–100) | | difficulty | number | Ranking difficulty score (0–100) | | resultCount | number | Total results in the iTunes Search index for this keyword | | lastScrapedAt | string \| null | ISO timestamp of the last historical observation for this keyword + storefront. `null` if there is no historical data yet. | | source | string | `"scrape"` when ranks come from historical data, `"live"` when freshly fetched from iTunes Search. | | ranks | array | Ordered list of ranking apps (see Rank Object below) | ## Rank Object | Field | Type | Description | | --------- | ------ | --------------------------------------- | | appId | string | Apple App ID | | rank | number | Organic rank for this keyword (1 = top) | | title | string | App name | | developer | string | Developer / seller name | | iconUrl | string | App icon URL (512×512) | *** ## How It Works This endpoint uses a **two-tier data strategy** to ensure you always get results: 1. **Stored data (fast)** — Checks for ranking data matching the keyword, country, device, and date. If found, returns results instantly. 2. **Live iTunes Search fallback (real-time)** — If no stored data exists, the API performs a live search against Apple's iTunes Search API and returns real-time results. This guarantees you get data for any keyword that has results in the App Store. Results from live iTunes Search are automatically saved for historical tracking, so subsequent requests for the same keyword return faster. **Real-time keyword research**: Use this endpoint to explore any keyword on-demand without needing to set up tracking first. Simply query any keyword and get instant results — the API handles everything behind the scenes including data persistence and future monitoring. *** ## Errors | Status | Code | When | | ------ | ---------------- | --------------------------------- | | 400 | INVALID\_KEYWORD | Keyword shorter than 2 characters | | 401 | UNAUTHORIZED | Missing or invalid API key | | 429 | RATE\_LIMITED | Too many requests — slow down | | 500 | INTERNAL\_ERROR | Server error during live search | # Analytics Source: https://docs.appeeky.com/docs/google-play-analytics Import Google Play report exports and read App Store Connect-style analytics rollups Google Play analytics uses a synced dataset. Appeeky imports Play Console CSV reports and Android vitals first, then dashboard endpoints read from the imported data instead of calling Google live on every page load. This avoids slow live GCS reads on every dashboard request and gives the UI the same shape as App Store Connect analytics. *** ## Data Sources | Metric family | Source | | ------------------------------------------------------------- | -------------------------------------------------------- | | Installs, device acquisitions, first opens, MAU, install base | `stats/installs/` CSV exports | | Ratings count and average rating | `stats/ratings/` CSV exports | | Crashes and ANRs | `stats/crashes/` CSV exports and Developer Reporting API | | Crash rate and ANR rate | Developer Reporting API | | Store listing visitors, acquisitions, conversion | `stats/store_performance/` CSV exports | | Search terms and traffic sources | `stats/store_performance/` traffic source report | Google captures daily rows, but report exports can lag Play Console by several days. Monthly CSV files are updated as Google publishes new data. *** ## Import Report Families ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/google-play/analytics/import-reports" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "prefixes": [ "stats/installs/", "stats/crashes/", "stats/ratings/", "stats/store_performance/" ], "maxObjects": 100 }' ``` | Field | Description | | ------------ | ------------------------------------------------------------------- | | `prefixes` | Report folders to import. Defaults to all supported stats prefixes. | | `maxObjects` | Safety cap for how many objects to import in one request. | | `bucket` | Optional bucket override. Usually use saved `reportsBucket`. | Use this endpoint for initial backfill and manual sync testing. Scheduled jobs can run the same import flow in the background. *** ## Import One Report ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/google-play/analytics/import-report" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "objectName": "stats/ratings/ratings_com.example.app_202601_overview.csv" }' ``` This is useful when testing one CSV object or replaying a failed import. *** ## Sync Android Vitals ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/analytics/vitals-sync" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "from": "2026-05-01", "to": "2026-06-01" }' ``` Vitals sync queries the Google Play Developer Reporting API and stores daily crash/ANR metrics for dashboard reads. *** ## Read App Analytics ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/analytics?from=2026-05-01&to=2026-06-01" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` Response shape: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "platform": "google", "packageName": "com.example.app", "period": { "from": "2026-05-01", "to": "2026-06-01" }, "totals": { "device_acquisitions": 107, "first_opens": 15, "mau": 242, "store_listing_visitors": 920, "store_listing_acquisitions": 107, "store_listing_conversion_rate": 0.1163, "ratings_count": 12, "average_rating": 4.5, "crash_rate": 0.0012, "anr_rate": 0.0004 }, "rows": [] } } ``` *** ## Read Account Analytics ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/analytics?from=2026-05-01&to=2026-06-01" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` Use this to show an account-level overview across imported Play apps. *** ## Sources and Countries ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/analytics/sources?from=2026-05-01&to=2026-06-01" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` This endpoint returns traffic source, country, and UTM breakdowns where Google includes them in the store performance report. *** ## Search Terms ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/analytics/search-terms?from=2026-05-01&to=2026-06-01" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` Search-term rows come from the `stats/store_performance/` traffic source export. Google may suppress low-volume rows, and some monthly exports may not include the `Search term` column. Expected row fields: | Field | Description | | ------------------------------- | -------------------------------- | | `search_term` | Play Store search term | | `store_listing_visitors` | Visitors from that term | | `store_listing_acquisitions` | Acquisitions from that term | | `store_listing_conversion_rate` | Acquisitions divided by visitors | | `country` | Country breakdown when available | *** ## Dashboard Mapping | Dashboard card | Google Play source | | ----------------------------- | ------------------------------------------------ | | Device acquisitions | `stats/installs/` | | First opens | `stats/installs/` | | MAU | `stats/installs/` | | Install base | `stats/installs/` | | Crash rate | Developer Reporting API and `stats/crashes/` | | ANR rate | Developer Reporting API and `stats/crashes/` | | Average rating | `stats/ratings/` | | Store listing conversion rate | `stats/store_performance/` | | Search terms | `stats/store_performance/` traffic source report | Revenue and buyer metrics require financial report access. They are separate from the four stats folders above. *** ## Why Not Query GCS Live? Live GCS requests are acceptable for testing, but not for every dashboard view: * Report files can be large. * CSV parsing is slower than reading already-synced analytics data. * Google exports are monthly files with daily rows inside. * The same report may be needed by multiple users, charts, and filters. * Background imports let the UI stay fast and consistent. The recommended model is: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Scheduled import -> synced analytics data -> dashboard API -> web UI ``` # Google Play Connect Overview Source: https://docs.appeeky.com/docs/google-play-connect Connect owned Google Play apps and access reviews, vitals, report exports, analytics, releases, listings, and monetization data Google Play Connect is for **apps you own in Google Play Console**. It is the Google Play counterpart to App Store Connect: customers connect their own Play Console account, then Appeeky can read private owner data and power dashboards, reviews workflows, vitals monitoring, and publishing tools. Public ASO endpoints do not need this setup. Use `platform=google` on the public endpoints when you want public app metadata, screenshots, keyword ranks, and competitor intelligence for any Play Store app. Google Play Connect uses a **Google Cloud service account JSON key**. It does not use a normal Google API key. The service account must also be invited in **Play Console > Users and permissions**. *** ## How It Works ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Customer Google Cloud project -> service account JSON -> Play Console user invite + permissions -> Appeeky /v1/connect/google-play/credentials -> Appeeky stores encrypted credentials -> API and scheduled sync jobs read Play Console data ``` Appeeky securely stores the service account credential encrypted at rest. Non-sensitive connection details such as the service account email, default package name, reports bucket, and sync status are used to show connection state and run scheduled syncs. *** ## What You Can Access | Area | What it covers | Docs | | ---------------- | -------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | | Credentials | Service account JSON, Play Console permissions, saved connection status | [Credentials & Setup](/docs/google-play-credentials) | | Report exports | GCS bucket, monthly CSV reports, object listing and downloads | [Report Exports](/docs/google-play-reports) | | Analytics | Dashboard rollups for installs, first opens, MAU, ratings, crashes, store conversion, search terms | [Analytics](/docs/google-play-analytics) | | Reviews & Vitals | List reviews, reply to reviews, query Android vitals, anomalies | [Reviews & Vitals](/docs/google-play-reviews-vitals) | | Publishing | Tracks, releases, store listing metadata, subscriptions, one-time products | [Publishing & Monetization](/docs/google-play-publishing) | | API Reference | Full endpoint list and request surface | [API Reference](/docs/google-play-console) | *** ## Customer Setup Flow 1. Create or choose a Google Cloud project. 2. Enable the Google Play APIs. 3. Create a service account JSON key. 4. Invite the service account email in Play Console. 5. Grant the permissions needed for the features the customer wants. 6. Find the Play Console reports bucket if analytics imports are needed. 7. Connect credentials through Appeeky. 8. Verify app access, report access, and analytics import. Start with [Credentials & Setup](/docs/google-play-credentials). If the customer only wants dashboard analytics, then continue with [Report Exports](/docs/google-play-reports) and [Analytics](/docs/google-play-analytics). *** ## Recommended Minimum Permissions | Use case | Minimum permission set | | --------------------------- | ----------------------------------------------------------------------------------- | | Analytics dashboard | Global `View app information and download bulk reports`, plus Android vitals access | | Reviews dashboard | Review read permission | | Reply to reviews | Review reply permission | | Release visibility | Release/track read permission | | Release updates | Release management permission | | Store listing updates | Store listing management permission | | IAP/subscription visibility | Monetization product read permission | | Financial reports | Global `View financial data` | For bulk report exports, Google requires account-level/global permissions. App-scoped access can be enough for some Play APIs, but it is usually not enough for the private GCS report bucket. *** ## Typical Workflow ### 1. Connect credentials ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} POST /v1/connect/google-play/credentials ``` See [Credentials & Setup](/docs/google-play-credentials#connect-credentials). ### 2. List owned apps ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/google-play/apps ``` Use this to confirm that the service account can see the customer's Play Console apps. ### 3. Verify report access ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/google-play/reports/objects?prefix=stats/installs/ ``` If this fails with `storage.objects.list denied`, the service account is authenticated but does not have bulk report access. See [Report Exports](/docs/google-play-reports#storageobjectslist-denied). ### 4. Import analytics ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} POST /v1/connect/google-play/analytics/import-reports POST /v1/connect/google-play/apps/:packageName/analytics/vitals-sync ``` Dashboard reads should use the synced analytics endpoints: ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/google-play/apps/:packageName/analytics GET /v1/connect/google-play/apps/:packageName/analytics/search-terms GET /v1/connect/google-play/apps/:packageName/analytics/sources ``` *** ## Endpoint Families | Family | Endpoints | | ------------ | --------------------------------------------------------------------------------------------------------------------------------- | | Credentials | `POST /credentials`, `GET /credentials/status`, `DELETE /credentials` | | Apps | `GET /apps` | | Reviews | `GET /apps/:packageName/reviews`, `POST /apps/:packageName/reviews/:reviewId/reply` | | Vitals | `GET /apps/:packageName/vitals/:metricSet`, `POST /apps/:packageName/vitals/:metricSet/query`, `GET /apps/:packageName/anomalies` | | Reports | `GET /reports/objects`, `GET /reports/object`, `GET /reports/download` | | Analytics | `POST /analytics/import-report`, `POST /analytics/import-reports`, `GET /analytics`, `GET /apps/:packageName/analytics` | | Publishing | Tracks, releases, localized listings | | Monetization | Subscriptions and one-time products | For the complete endpoint list, see [Google Play Console API Reference](/docs/google-play-console). # Google Play Console API Reference Source: https://docs.appeeky.com/docs/google-play-console Full endpoint reference for owned-app Google Play reviews, vitals, releases, listings, monetization products, and report exports Google Play Console endpoints are for **apps you own**. They complement the public Google Play ASO endpoints (`platform=google`) that work for any public app. For setup and usage guides, start with [Google Play Connect Overview](/docs/google-play-connect). ## Authentication Google Play Console uses a **Google Cloud service account JSON key**, not a regular browser/API key. 1. In Google Cloud Console, create or choose a project. 2. Enable **Google Play Android Developer API** and **Google Play Developer Reporting API**. 3. Go to **IAM & Admin -> Service Accounts**, create a service account, then create a **JSON** key. 4. In Play Console, open **Users and permissions**, invite the service account email, and grant the app/global permissions needed for reviews, vitals, report exports, products, releases, and store listing metadata. Connect once: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/google-play/credentials" \ -H "X-API-Key: YOUR_APEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "serviceAccountJson": { "...": "..." }, "defaultPackageName": "com.example.app", "reportsBucket": "pubsite_prod_rev_0123456789" }' ``` Or pass credentials per request: | Header | Description | | --------------------------- | ------------------------------- | | `X-GP-Service-Account-Json` | Full service account JSON | | `X-GP-Client-Email` | Service account `client_email` | | `X-GP-Private-Key` | Service account private key PEM | | `X-GP-Private-Key-B64` | Base64 private key alternative | | `X-GP-Project-Id` | Optional project ID | | `X-GP-Client-Id` | Optional client ID | The service account must be invited in Play Console with the permissions needed for the data you want to read or mutate. ## Owned App Data ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/google-play/apps GET /v1/connect/google-play/apps/:packageName/reviews GET /v1/connect/google-play/apps/:packageName/reviews/:reviewId POST /v1/connect/google-play/apps/:packageName/reviews/:reviewId/reply GET /v1/connect/google-play/apps/:packageName/vitals/crashRate POST /v1/connect/google-play/apps/:packageName/vitals/crashRate/query GET /v1/connect/google-play/apps/:packageName/anomalies ``` Review replies use Google's official `reviews.reply` endpoint and must be 350 characters or fewer. Supported vitals metric sets: `anrRate`, `crashRate`, `errorCount`, `excessiveWakeupRate`, `lmkRate`, `slowRenderingRate`, `slowStartRate`, `stuckBackgroundWakelockRate`. ## Publishing Metadata ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/google-play/apps/:packageName/tracks GET /v1/connect/google-play/apps/:packageName/tracks/:track/releases PATCH /v1/connect/google-play/apps/:packageName/tracks/:track?validateOnly=true GET /v1/connect/google-play/apps/:packageName/listings GET /v1/connect/google-play/apps/:packageName/listings/:language PATCH /v1/connect/google-play/apps/:packageName/listings/:language?validateOnly=true ``` Listing patches accept `title`, `shortDescription`, `fullDescription`, and `video`. Track patches accept Google Play Track schema fields, usually `releases`. ## Monetization ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/google-play/apps/:packageName/subscriptions GET /v1/connect/google-play/apps/:packageName/one-time-products ``` ## Report Exports Google Play sales, earnings, reviews, statistics, acquisition/search-term, and store listing conversion exports live in a private Google Cloud Storage bucket whose name usually starts with `pubsite_prod_`. ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/google-play/reports/objects?prefix=stats/store_performance/ GET /v1/connect/google-play/reports/object?objectName=stats/store_performance/store_performance_com.example.app_202601_country.csv GET /v1/connect/google-play/reports/download?objectName=sales/salesreport_202601.zip ``` Use `reportsBucket` in saved credentials, or pass `bucket=pubsite_prod_...` on each report request. Reports are captured daily, published into monthly CSV files, and can lag Play Console by several days. The service account must be added in Play Console with account-level access to download bulk reports; otherwise GCS object listing will return `storage.objects.list` permission errors. ## Store Analytics Import monthly statistics CSVs from the Play Console GCS export: ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} POST /v1/connect/google-play/analytics/import-report POST /v1/connect/google-play/analytics/import-reports POST /v1/connect/google-play/apps/:packageName/analytics/vitals-sync ``` ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "objectName": "stats/store_performance/store_performance_com.example.app_202601_traffic_source.csv" } ``` Supported import prefixes: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} stats/installs/ stats/crashes/ stats/ratings/ stats/store_performance/ ``` After import, read Apple-analytics-style rollups: ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/google-play/analytics?from=2026-01-01&to=2026-01-31 GET /v1/connect/google-play/apps/:packageName/analytics?from=2026-01-01&to=2026-01-31 GET /v1/connect/google-play/apps/:packageName/analytics/sources?from=2026-01-01&to=2026-01-31 GET /v1/connect/google-play/apps/:packageName/analytics/search-terms?from=2026-01-01&to=2026-01-31 ``` The analytics response includes installs/device acquisitions, first opens, MAU, ratings, crash/ANR rates, `store_listing_visitors`, `store_listing_acquisitions`, and `store_listing_conversion_rate`. Search-term rows are populated from the `traffic_source` store performance report when Google includes the `Search term` column. Dashboard reads should use these synced analytics endpoints; GCS imports run as background sync jobs. # Credentials & Setup Source: https://docs.appeeky.com/docs/google-play-credentials Create a Google Play service account, grant Play Console permissions, and save credentials in Appeeky Google Play Connect uses a **service account JSON key** from Google Cloud. The same service account must be invited as a user in Play Console before it can access owned-app data. Do not create or share a normal Google API key for this integration. The credential is the service account JSON file. *** ## Required Google APIs Enable these APIs in the Google Cloud project that will own the service account: | API | Required for | | ----------------------------------- | ----------------------------------------------------------------------------- | | Google Play Android Developer API | Reviews, review replies, releases, listings, subscriptions, one-time products | | Google Play Developer Reporting API | Android vitals, crash rate, ANR rate, app quality metrics | | Cloud Storage API | Play Console bulk CSV reports in the `gs://pubsite_prod_...` bucket | Official references: Service account setup for Google Play Developer API. Android vitals and quality reporting setup. *** ## Create the Service Account JSON 1. Open **Google Cloud Console > IAM & Admin > Service Accounts**. 2. Choose the Google Cloud project connected to Play Console, or create a new project. 3. Create a service account. 4. Open the service account. 5. Go to **Keys**. 6. Create a new key. 7. Select **JSON**. 8. Download the JSON file. You do not need to grant broad IAM roles inside Google Cloud for Play Console access. The important access control happens in Play Console after the service account is invited there. If Google Cloud asks for a project IAM role during service account creation, a minimal viewer-style role is enough for creation flow. Play Console permissions decide which app data the API can access. *** ## Invite the Service Account in Play Console Open **Play Console > Users and permissions**, then invite the service account email from the JSON file. The email looks like: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} appeeky@your-project.iam.gserviceaccount.com ``` Grant permissions based on the features the customer wants: | Feature | Play Console permission guidance | | ----------------------------------------------------- | ------------------------------------------------------- | | App list | App information read access | | Bulk reports | Global `View app information and download bulk reports` | | Store performance, installs, ratings, crashes reports | Same global bulk report permission | | Financial reports | Global `View financial data` | | Review listing | Review read access | | Review replies | Review reply/manage access | | Android vitals | App quality / Android vitals access | | Release tracks | Release read or release management access | | Release updates | Release management access | | Store listing metadata | Store listing management access | | Subscriptions and one-time products | Monetization product access | For analytics dashboards, start with: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} Global: View app information and download bulk reports App: Android vitals / app quality access ``` Add write permissions only when the customer wants Appeeky to reply to reviews or update releases/listings. *** ## Connect Credentials ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/google-play/credentials" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "serviceAccountJson": { "type": "service_account", "project_id": "your-google-project", "private_key_id": "...", "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n", "client_email": "appeeky@your-project.iam.gserviceaccount.com", "client_id": "..." }, "defaultPackageName": "com.example.app", "reportsBucket": "pubsite_prod_8701287375724464057" }' ``` | Field | Required | Description | | -------------------- | -------- | ---------------------------------------------------- | | `serviceAccountJson` | Yes | Full JSON key downloaded from Google Cloud | | `defaultPackageName` | No | Default app package for dashboards and tools | | `reportsBucket` | No | Required for report imports and analytics dashboards | The API verifies credentials before saving. A successful response means Google accepted the service account and the account can access at least basic Play Console app data. *** ## Per-Request Credentials Saved credentials are recommended. For testing, you can also pass credentials per request: | Header | Description | | --------------------------- | ------------------------------- | | `X-GP-Service-Account-Json` | Full service account JSON | | `X-GP-Client-Email` | Service account `client_email` | | `X-GP-Private-Key` | Service account private key PEM | | `X-GP-Private-Key-B64` | Base64 private key alternative | | `X-GP-Project-Id` | Optional project ID | | `X-GP-Client-Id` | Optional client ID | Use saved credentials for production customers so background sync jobs can run without asking for headers on every request. *** ## Check Status ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/credentials/status" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` Example response: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "connected": true, "serviceAccountEmail": "appee...@your-project.iam.gserviceaccount.com", "projectId": "your-google-project", "defaultPackageName": "com.example.app", "reportsBucket": "pubsite_prod_8701287375724464057", "analyticsSyncStatus": "pending", "lastSyncedAnalyticsDate": null } } ``` *** ## Delete Credentials ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X DELETE "https://api.appeeky.com/v1/connect/google-play/credentials" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` This removes the saved connection and deletes the encrypted credential from Appeeky. *** ## Troubleshooting | Error | Meaning | Fix | | ---------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------- | | `GOOGLE_PLAY_CREDENTIALS_REQUIRED` | Missing service account JSON or private key fields | Send `serviceAccountJson`, or `clientEmail` + private key | | Google OAuth error | The JSON key is malformed, revoked, or from the wrong project | Create a new JSON key and retry | | App list is empty | Service account is authenticated but has no Play Console app access | Invite the service account in Play Console and grant app permissions | | Report listing fails | Basic Play API access works, but GCS bulk report access is missing | See [Report Exports](/docs/google-play-reports#storageobjectslist-denied) | Permission changes in Play Console can take time to propagate. If setup looks correct, wait a few minutes and retry. # Publishing & Monetization Source: https://docs.appeeky.com/docs/google-play-publishing Manage Google Play tracks, releases, store listings, subscriptions, and one-time products Publishing endpoints expose the owned-app surfaces customers normally manage in Play Console. Read endpoints are useful for dashboards and release audits. Write endpoints should be used carefully and can be validated before committing. Release and listing updates write to Google Play. Grant write permissions only to customers who explicitly want automation for publishing workflows. *** ## Release Tracks ### List Tracks ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/tracks" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` ### Get a Track ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/tracks/production" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` ### List Releases on a Track ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/tracks/production/releases" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` *** ## Patch a Track Use `validateOnly=true` to ask Google to validate the edit without committing it. ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X PATCH "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/tracks/production?validateOnly=true" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "releases": [ { "name": "3.1.0", "versionCodes": ["123"], "status": "completed" } ] }' ``` Track patches accept Google's Android Publisher track schema. *** ## Store Listings ### List Listing Localizations ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/listings" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` ### Get One Localization ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/listings/en-US" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` ### Patch Listing Metadata ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X PATCH "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/listings/en-US?validateOnly=true" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Example App", "shortDescription": "Create better screenshots faster.", "fullDescription": "Example App helps you create and localize app screenshots for Google Play.", "video": "https://www.youtube.com/watch?v=example" }' ``` Supported listing fields: | Field | Description | | ------------------ | ---------------------- | | `title` | App title | | `shortDescription` | Short description | | `fullDescription` | Full store description | | `video` | Promo video URL | *** ## Monetization Products ### Subscriptions ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/subscriptions" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` ### One-Time Products ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/one-time-products" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` These endpoints are read-only in the current API surface. *** ## Permission Guidance | Action | Permission guidance | | --------------------------- | -------------------------------- | | Read releases/tracks | Release read access | | Patch releases/tracks | Release management access | | Read listings | Store listing read access | | Patch listings | Store listing management access | | Read subscriptions/products | Monetization product read access | For production automations, prefer a narrow service account permission set and use `validateOnly=true` in development flows. # Report Exports Source: https://docs.appeeky.com/docs/google-play-reports Access Google Play Console bulk reports from the private Cloud Storage bucket Google Play Console exports owner reports to a private Google Cloud Storage bucket. These reports are the source for many dashboard metrics, including installs, ratings, crashes, store listing conversion, search terms, sales, and earnings. The reports bucket is account-level. It is not a normal customer-created GCS bucket and it is not stored inside the customer's Google Cloud project. *** ## Reports Bucket Common report paths look like this: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} gs://pubsite_prod_8701287375724464057/stats/installs/ gs://pubsite_prod_8701287375724464057/stats/crashes/ gs://pubsite_prod_8701287375724464057/stats/ratings/ gs://pubsite_prod_8701287375724464057/stats/store_performance/ ``` When saving credentials, pass only the bucket root: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} pubsite_prod_8701287375724464057 ``` If a customer pastes a full `gs://.../stats/...` path, Appeeky normalizes it to the bucket root automatically. *** ## Required Permission For report exports, the service account needs account-level/global access in Play Console: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} View app information and download bulk reports ``` For financial reports, it also needs: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} View financial data ``` Google documents this requirement in [Download and export monthly reports](https://support.google.com/googleplay/android-developer/answer/6135870). *** ## Supported Prefixes The analytics importer currently supports these report families: | Prefix | Used for | | -------------------------- | ----------------------------------------------------------------------- | | `stats/installs/` | Device acquisitions, installs, first opens, MAU, install base | | `stats/crashes/` | Crash and ANR counts/rates from CSV exports | | `stats/ratings/` | Ratings count and average rating | | `stats/store_performance/` | Store listing visitors, acquisitions, conversion, sources, search terms | Other report families can still be listed and downloaded through the report endpoints when the service account has access. *** ## List Objects ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/reports/objects?prefix=stats/store_performance/" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` Optional query parameters: | Name | Description | | -------- | --------------------------------------------------- | | `prefix` | GCS object prefix, such as `stats/installs/` | | `bucket` | Override the saved `reportsBucket` for this request | | `limit` | Max objects to return | *** ## Get Object Metadata ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/reports/object?objectName=stats/store_performance/store_performance_com.example.app_202601_country.csv" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` Use this when you need metadata for one report object before downloading or importing it. *** ## Download a Report ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -o report.csv "https://api.appeeky.com/v1/connect/google-play/reports/download?objectName=stats/installs/installs_com.example.app_202601_overview.csv" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` Google report files may be CSV, ZIP, or gzip-compressed CSV depending on the report family. The analytics importer handles the supported stats CSV formats automatically. *** ## Import One Report ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/google-play/analytics/import-report" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "objectName": "stats/store_performance/store_performance_com.example.app_202601_traffic_source.csv" }' ``` Use this for manual testing or targeted backfills. For normal analytics sync, use the bulk import endpoint in [Analytics](/docs/google-play-analytics#import-report-families). *** ## `storage.objects.list denied` This error means the service account can authenticate, but Google Cloud Storage will not allow it to list the Play Console reports bucket. Most common causes: 1. The service account was not invited in Play Console. 2. The service account has app-scoped permissions but not global bulk report permission. 3. The wrong service account email was invited. 4. The reports bucket belongs to a different Play Console developer account. 5. Permission propagation has not completed yet. Fix: 1. Open **Play Console > Users and permissions**. 2. Select the service account user. 3. Grant global `View app information and download bulk reports`. 4. Save changes. 5. Retry `GET /reports/objects?prefix=stats/installs/`. If financial reports are needed, also grant global `View financial data`. # Reviews & Vitals Source: https://docs.appeeky.com/docs/google-play-reviews-vitals List Google Play reviews, reply to users, query Android vitals, and inspect anomalies Reviews and Android vitals use Google's official APIs. They are live owner-data endpoints, not public Play Store scraping endpoints. *** ## Reviews ### List Reviews ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/reviews" \ -H "X-API-Key: YOUR_APEEKY_KEY" ``` Optional query parameters: | Name | Description | | --------------------- | ---------------------------------------- | | `maxResults` | Page size supported by Google | | `token` | Pagination token from Google | | `translationLanguage` | Language code for translated review text | ### Get One Review ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/reviews/REVIEW_ID" \ -H "X-API-Key: YOUR_APEEKY_KEY" ``` ### Reply to a Review ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/reviews/REVIEW_ID/reply" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "replyText": "Thanks for the feedback. We fixed this in the latest update." }' ``` Google's review reply endpoint accepts a reply up to 350 characters. Replying to a review writes to Google Play. Use read-only permissions for customers who only want review analytics or triage. *** ## Review Permissions | Action | Permission guidance | | --------------- | -------------------------- | | List reviews | Review read access | | Get one review | Review read access | | Reply to review | Review reply/manage access | If review listing works but replies fail, the service account likely has read access but not reply permission. *** ## Android Vitals Android vitals are queried through the Google Play Developer Reporting API. ### Metric Metadata ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/vitals/crashRate" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` Supported metric sets: ```text theme={"theme":{"light":"github-light","dark":"github-dark"}} anrRate crashRate errorCount excessiveWakeupRate lmkRate slowRenderingRate slowStartRate stuckBackgroundWakelockRate ``` ### Query Metric Data ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/vitals/crashRate/query" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "Content-Type: application/json" \ -d '{ "timelineSpec": { "aggregationPeriod": "DAILY", "startTime": { "year": 2026, "month": 5, "day": 1 }, "endTime": { "year": 2026, "month": 6, "day": 1 } } }' ``` The body follows Google's metric query schema. Use the analytics vitals sync endpoint when you want Appeeky to store crash and ANR rates for dashboards. *** ## Anomalies ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/google-play/apps/com.example.app/anomalies" \ -H "X-API-Key: YOUR_APPEEKY_KEY" ``` Anomalies help identify unusual spikes in technical quality metrics. *** ## Empty Vitals Rows Vitals can return no rows even when credentials are valid. Common reasons: 1. The selected date range has no data. 2. Google suppresses data below thresholds. 3. The service account lacks app quality / vitals access. 4. The app has too little traffic for the metric. Try a wider date range and verify the same app has vitals data in Play Console. # Overview Source: https://docs.appeeky.com/docs/growth-channels Multi-channel social lead generation — scan communities for high-intent posts, score with AI, and approve replies before publishing. **Growth Channels** is Appeeky's growth layer for organic distribution — community lead generation and visual social posting. It watches communities where your users hang out, surfaces posts worth engaging with, drafts replies you review, and publishes through connected accounts — starting with **Reddit**. **Pinterest** covers screenshot-to-pin marketing for your apps via the Social Media API. *** ## Channels | Channel | Status | Docs | | ----------------- | --------- | --------------------------------------------------- | | **Reddit** | Available | [Reddit Growth](/docs/growth-channels-reddit) | | **Pinterest** | Available | [Pinterest Growth](/docs/growth-channels-pinterest) | | More integrations | Planned | — | Use `GET /v1/growth/channels` to discover which channels are enabled on a deployment and their config schemas. Each project is bound to one `channel` (`reddit` today). *** ## Shared concepts Every channel shares the same core model: ### Projects A **project** is your product's Growth configuration: target communities, intent thresholds, scan cadence, LLM budget, and draft rules. Create via bootstrap from an App Store app or manually. ### Opportunities (inbox) An **opportunity** is a scored post worth replying to. The pipeline assigns an intent score, urgency (`must_reply` / `browse`), buyer stage, and an LLM-drafted reply. You **approve** or **dismiss** before anything is published. ### Scheduled posts **Scheduled posts** are standalone channel posts (not thread replies) queued for a future time. A background worker dispatches due posts; you can also publish immediately. ### Review-first publishing Inbox drafts are never auto-posted. You stay in control of tone, timing, and disclosure — especially important on communities with strict promo rules. *** ## How it works (all channels) ``` scan (cron or manual) → pre-filter → LLM intent score → draft reply → inbox (pending) │ approve / dismiss │ post or schedule ``` * **Hard dedupe** — each source post is processed once per project. * **Per-project LLM budget** — auto-pauses scanning when the daily cap is hit. * **Reply linter** — flags missing disclosure, generic openers, links, etc. Drafts are surfaced with warnings, not silently dropped. *** ## REST API surface (shared) These endpoints are channel-agnostic; filter by `channel` or `projectId` where needed: | Area | Base path | | ---------------- | ---------------------------- | | Projects | `/v1/growth/projects` | | Inbox | `/v1/growth/opportunities` | | Scheduled posts | `/v1/growth/scheduled-posts` | | Channel registry | `/v1/growth/channels` | Channel-specific connect flows live under `/v1/growth/connect/{channel}/…` (Reddit today). *** ## MCP tools Growth is exposed as `growth_*` tools on the Appeeky MCP server. See [MCP → Growth Channels](/docs/mcp#growth-channels-reddit-lead-generation). *** ## Channel guides Subreddit scanning, Social Media Reddit OAuth, inbox replies, scheduled posts, and REST reference. Build account trust before API posting so Reddit's spam filters don't remove your content. Pins from app screenshots, board picker, scheduling, and screenshot automations. # Pinterest Source: https://docs.appeeky.com/docs/growth-channels-pinterest Turn App Store screenshots into Pinterest pins, schedule posts, and run screenshot automations that draft copy for you. **Pinterest Growth** helps you turn App Store screenshots and marketing images into discoverable pins that drive traffic to your app. Connect your Pinterest account, compose or automate pins, and schedule them from the Appeeky dashboard. Requires a signed-in Appeeky account. Connect Pinterest before posting or scheduling. *** ## How it works ``` Connect Pinterest → pick board → add screenshot + copy → publish now or schedule │ pin goes live on Pinterest ``` * **Manual posts** — compose in the dashboard with an image, title/description, board, and destination link (App Store, Play Store, or App Gallery URL). * **Automations** — rotate through your tracked app's App Store screenshots; each run drafts one pin with AI-generated copy for you to review. * **Scheduled posts** — queue pins for a future time; Appeeky publishes them automatically when due. * **Analytics** — after publish, see impressions, saves, and clicks in the dashboard or via the API. For TikTok slideshows, X, and LinkedIn, use the same **Social Media** dashboard. *** ## Connect Pinterest In the dashboard: **Settings → Social Media** or **Growth → Social Media → Accounts**, then connect Pinterest. | Method | Endpoint | Description | | ------ | -------------------------------------------------- | ----------------------------------------------- | | GET | `/v1/connect/social/platforms` | List supported platforms | | GET | `/v1/connect/social/pinterest/oauth/start` | Returns `authorizeUrl` (`?return_to=` optional) | | GET | `/v1/connect/social/accounts` | List connected accounts | | GET | `/v1/connect/social/accounts/:id/pinterest/boards` | List boards for the board picker | | DELETE | `/v1/connect/social/accounts/:id` | Disconnect | On first connect, if your account has no boards yet, Appeeky creates a default **Appeeky** board and uses it when you don't pick one. *** ## Create a pin | Method | Endpoint | Description | | ------ | -------------------------------- | --------------------------------------------- | | POST | `/v1/social/posts` | Create post (publish now or schedule) | | POST | `/v1/social/posts/:id/publish` | Publish a draft or scheduled post immediately | | GET | `/v1/social/posts/:id` | Get post and platform status | | GET | `/v1/social/posts/:id/analytics` | Pin metrics after publish | | DELETE | `/v1/social/posts/:id` | Cancel draft or scheduled post | | POST | `/v1/social/media/upload` | Upload image to your content library | ### Request body ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} POST /v1/social/posts { "content": "Dibble — habit tracker for ADHD\nBuild tiny routines without overwhelm. Tap to download.", "account_ids": ["social-media-account-uuid"], "media_ids": ["asset-uuid"], "scheduled_for": "2026-07-22T09:00:00.000Z", "timezone": "Europe/Istanbul", "extra": { "boardId": "pinterest-board-id", "link": "https://apps.apple.com/app/id123456789" } } ``` | Field | Required | Notes | | --------------------------- | ----------- | ------------------------------------------------------------ | | `content` | Yes | First line → pin **title**; rest → **description** | | `account_ids` | Yes | Pinterest account id from `/connect/social/accounts` | | `media_ids` or `media_urls` | Yes | At least one image (or video for video pins) | | `scheduled_for` | No | Omit to publish immediately | | `extra.boardId` | No | Board id; uses your default board if omitted | | `extra.link` | Recommended | Destination URL (App Store, Play Store, or App Gallery page) | ### Pin media * **Image pins** — one image from your library or upload. * **Video pins** — video plus a cover image (`extra.coverImageUrl`). *** ## Automations (screenshot → pin) | Method | Endpoint | Description | | ------ | ------------------------------------- | --------------------------------- | | GET | `/v1/social/automations` | List automations | | POST | `/v1/social/automations` | Create automation | | PATCH | `/v1/social/automations/:id` | Update schedule, pause, or resume | | DELETE | `/v1/social/automations/:id` | Archive | | POST | `/v1/social/automations/:id/generate` | Generate a draft pin now (202) | ### Create Pinterest automation ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} POST /v1/social/automations { "platform": "pinterest", "user_app_id": "tracked-app-uuid", "account_id": "pinterest-social-account-uuid", "name": "Dibble Pinterest pins", "schedule_crons": ["0 9 * * 1,3,5"], "timezone": "Europe/Istanbul", "review_mode": true, "draft_mode": true, "publish_config": { "boardId": "optional-board-id", "link": "https://apps.apple.com/app/id123456789" } } ``` Each run: 1. Pulls the next iPhone screenshot from your tracked app. 2. Writes pin title and description with AI. 3. Saves a **draft** in the dashboard for you to review before publishing. *** ## Dashboard Route: **`/dashboard/social-media`** | Tab | Purpose | | --------------- | ------------------------------------------ | | **Calendar** | Scheduled and published posts | | **All posts** | Status, errors, publish now | | **Accounts** | Connect Pinterest | | **Automations** | Pinterest pin series and TikTok slideshows | | **Library** | Reuse uploaded screenshots | *** ## Related * [Growth Channels overview](/docs/growth-channels) * [Reddit Growth](/docs/growth-channels-reddit) — community scanning and inbox replies # Reddit Source: https://docs.appeeky.com/docs/growth-channels-reddit Scan subreddits for high-intent posts, score buying intent with AI, draft replies, and publish scheduled posts via Social Media. Reddit is the first **Growth Channels** integration. The scanner watches your configured subreddits, scores posts with an LLM, drafts replies for your inbox, and publishes standalone posts through the same Social Media Reddit connection. **Scan transport:** listing and search go through [Monid](https://monid.ai) → TikHub (`MONID_API_KEY`) so we get scores and comment counts without Reddit's public `.json` lock. RSS is the fallback. Connect + scheduled posts still use Zernio. Requires a signed-in Appeeky account. Connect Reddit before posting or scheduling. New or low-karma accounts that post through the API are often removed by Reddit's sitewide spam filters. Read **[Reddit account warmup](/docs/reddit-account-warmup)** before your first scheduled post. *** ## How it works ``` every 15 min (per project) → scan subreddits → score intent → draft reply → inbox (pending) │ you approve / dismiss │ post reply or schedule ``` * **No auto-posting of inbox drafts** — you review each opportunity first. * **Scheduled posts** are dispatched by a Trigger.dev cron (`growth-reddit-scheduled-dispatch`, every 5 minutes) or manually via `POST …/scheduled-posts/:id/dispatch`. * **Hard dedupe** — each Reddit post is processed once per project. For shared concepts (projects, opportunities, linter), see [Growth Channels overview](/docs/growth-channels). *** ## Connect Reddit | Method | Endpoint | Description | | ------ | ------------------------------------------ | ---------------------------------------------------- | | GET | `/v1/growth/connect/reddit/start` | Returns `authorizeUrl` for Social Media Reddit OAuth | | GET | `/v1/growth/connect/reddit/status` | Connection status | | GET | `/v1/growth/connect/reddit/account-health` | Karma, age, warmup readiness, API usage today | | GET | `/v1/growth/connect/reddit/subscriptions` | Subreddits the account is subscribed to | | DELETE | `/v1/growth/connect/reddit` | Disconnect | *** ## Projects | Method | Endpoint | Description | Credits | | ------ | ------------------------------- | ------------------------------------------------- | ------- | | POST | `/v1/growth/projects/bootstrap` | Suggest subreddits + config from an App Store app | 3 | | POST | `/v1/growth/projects` | Create project (`channel: "reddit"`) | 0 | | GET | `/v1/growth/projects` | List projects (`?channel=reddit`) | 0 | | GET | `/v1/growth/projects/:id` | Get one | 0 | | PATCH | `/v1/growth/projects/:id` | Update subs, threshold, budget, enabled | 0 | | DELETE | `/v1/growth/projects/:id` | Delete (cascades opportunities) | 0 | | POST | `/v1/growth/projects/:id/scan` | Queue manual scan | 0 | | GET | `/v1/growth/projects/:id/stats` | Funnel + budget (`?days=30`) | 0 | ### Reddit `channelConfig` ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "subreddits": ["iosdev", "AppStoreOptimization"], "competitorKeywords": ["competitor app name"], "intentPatterns": ["looking for", "alternative to", "recommend"] } ``` *** ## Opportunities (inbox) | Method | Endpoint | Description | Credits | | ------ | ----------------------------------------------- | --------------------------------------------------------------------------------- | ------- | | GET | `/v1/growth/opportunities` | List (`?status=pending&urgency=must_reply&projectId=&limit=&offset=`) | 0 | | GET | `/v1/growth/opportunities/:id` | Get one (full draft + score breakdown) | 0 | | PATCH | `/v1/growth/opportunities/:id/review` | `{ "decision": "approved" \| "dismissed", "editedDraft"? }` | 0 | | POST | `/v1/growth/opportunities/:id/regenerate-draft` | Regenerate LLM draft | dynamic | | POST | `/v1/growth/opportunities/:id/post-reply` | Schedule a reply on the calendar (`scheduledFor`). Inbox replies stay copy-paste. | 0 | | POST | `/v1/growth/opportunities/:id/mark-replied` | Mark manually posted `{ "replyUrl" }` | 0 | **Urgency** values: `must_reply`, `browse`. **Status**: `pending`, `approved`, `dismissed`, `replied`, `expired`. List responses include `meta.total` for pagination. ### Inbox cleanup When the inbox grows large, bulk-dismiss low-value rows or let the nightly cron expire untouched items. | Method | Endpoint | Description | Credits | | ------ | ---------------------------------- | -------------------------------------------------- | ------- | | GET | `/v1/growth/inbox/cleanup-preview` | Counts by category (`?projectId=` optional) | 0 | | POST | `/v1/growth/inbox/cleanup` | Bulk dismiss selected categories (up to 5,000/run) | 0 | **POST body** (at least one flag required): ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "projectId": "uuid", "dismissLowUrgency": true, "dismissBelowMinScore": true, "dismissStalePosts": true, "dismissUntouchedOld": false } ``` Categories: * **low urgency** — `urgency = low` * **below min score** — `intent_score` under the project threshold (or global default) * **stale posts** — Reddit post older than `maxPostAgeDays` * **untouched old** — pending in inbox 60+ days (also auto-expired nightly by `growth-inbox-expire` cron) **Account health** (`GET …/connect/reddit/account-health`) returns `readiness`: `cold` | `warming` | `ready`, a checklist (account age, comment karma, daily API cap), and today's API post/reply counts. *** ## Scheduled posts | Method | Endpoint | Description | Credits | | ------ | ----------------------------------------- | ----------------------------------------------------- | ------- | | GET | `/v1/growth/scheduled-posts` | List (`?status=pending` or `?from=&to=` for calendar) | 0 | | POST | `/v1/growth/scheduled-posts` | Create text/link/image/video post | 0 | | DELETE | `/v1/growth/scheduled-posts/:id` | Cancel pending | 0 | | POST | `/v1/growth/scheduled-posts/:id/retry` | Retry failed | 0 | | POST | `/v1/growth/scheduled-posts/:id/dispatch` | Publish now (pending or failed) | 0 | *** ## Channel registry ``` GET /v1/growth/channels ``` Returns available channels, config JSON schemas, and whether Reddit posting is configured for the deployment. *** ## MCP tools Reddit Growth is exposed as `growth_*` MCP tools — see [MCP Server → Growth Channels](/docs/mcp#growth-channels-reddit-lead-generation). *** ## Related * [Growth Channels overview](/docs/growth-channels) — shared model and future channels * [Reddit account warmup](/docs/reddit-account-warmup) — avoid spam-filter removals on new accounts # Health Check Source: https://docs.appeeky.com/docs/health Service status and uptime monitoring ``` GET /v1/health ``` Returns the current service status. Use this endpoint to verify the API is running and reachable. This is the **only endpoint** that does not require authentication. No `X-API-Key` header is needed. ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/health" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch("https://api.appeeky.com/v1/health"); const data = await response.json(); console.log(data.status); // "ok" ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get("https://api.appeeky.com/v1/health") data = response.json() print(data["status"]) # "ok" ``` ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "status": "ok", "service": "app-store-api", "timestamp": "2026-02-17T14:30:00.000Z" } ``` ## Fields | Field | Type | Description | | --------- | ------ | ------------------------------------------------ | | status | string | Always `"ok"` when the service is up and healthy | | service | string | Service identifier — always `"app-store-api"` | | timestamp | string | Current server time in ISO 8601 format | Use this endpoint for **uptime monitoring** and automated health checks. Point your monitoring tool (UptimeRobot, Pingdom, Datadog, etc.) at `https://api.appeeky.com/v1/health` and alert on non-200 responses or missing `"status": "ok"`. ## Errors | Status | Code | When | | ------ | -------------------- | ----------------------------------- | | 503 | SERVICE\_UNAVAILABLE | Service is starting up or unhealthy | # Idea Validation Source: https://docs.appeeky.com/docs/idea-validation Validate a plain-language app idea against the live App Store — real competitors, keyword demand, review pain points, an AI verdict, and a go-to-market starter pack ``` POST /v1/validate-idea GET /v1/validate-idea/jobs/:jobId ``` Turn a one-line app idea into **real App Store evidence**. Instead of guessing, this endpoint searches the live store for direct competitors (with revenue and download estimates), measures keyword demand vs ranking difficulty, mines recurring complaints from competitor reviews, then synthesizes a **verdict** (0–100) and a **go-to-market / ASO starter pack** with an LLM. The work runs asynchronously on a background job, so `POST /v1/validate-idea` returns a `jobId` immediately (HTTP `202`). Poll `GET /v1/validate-idea/jobs/:jobId` for the full result. Every number in the result is grounded in live App Store data — competitor revenue, keyword difficulty, and verbatim user complaints — not the model's prior. The LLM is instructed to reason from the gathered evidence. *** ## Create a run ``` POST /v1/validate-idea ``` ### Request Body | Field | Type | Required | Default | Description | | --------- | ------ | -------- | ------- | ----------------------------------------------------- | | `idea` | string | Yes | — | The app idea in plain language (8–600 characters). | | `country` | string | No | `us` | ISO 3166-1 alpha-2 storefront to research, e.g. `gb`. | Cost is metered at a flat **12 credits per run** (one idea-parse call, several App Store lookups, and two synthesis LLM calls). The poll endpoint is free. ### Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/validate-idea" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "idea": "An AI-powered budgeting app that auto-categorizes spending and warns you before you overspend", "country": "us" }' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} // 1. Start the run const start = await fetch("https://api.appeeky.com/v1/validate-idea", { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ idea: "An AI-powered budgeting app that warns you before you overspend", country: "us", }), }); const { data } = await start.json(); const jobId = data.jobId; // 2. Poll for the result async function poll() { const res = await fetch( `https://api.appeeky.com/v1/validate-idea/jobs/${jobId}`, { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); if (data.status === "completed" || data.status === "failed") return data; await new Promise((r) => setTimeout(r, 4000)); return poll(); } console.log(await poll()); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import time, requests headers = {"X-API-Key": "YOUR_API_KEY"} start = requests.post( "https://api.appeeky.com/v1/validate-idea", headers=headers, json={ "idea": "An AI-powered budgeting app that warns you before you overspend", "country": "us", }, ) job_id = start.json()["data"]["jobId"] while True: res = requests.get( f"https://api.appeeky.com/v1/validate-idea/jobs/{job_id}", headers=headers, ).json()["data"] if res["status"] in ("completed", "failed"): break time.sleep(4) print(res) ``` ### Response (`202 Accepted`) ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "jobId": "f3c1a2b4-...", "triggerRunId": "run_...", "status": "queued", "idea": "An AI-powered budgeting app that warns you before you overspend", "country": "us" } } ``` *** ## Poll for the result ``` GET /v1/validate-idea/jobs/:jobId ``` ### Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "jobId": "f3c1a2b4-...", "status": "completed", "idea": "An AI-powered budgeting app that warns you before you overspend", "country": "us", "result": { "plan": { "summary": "An AI budgeting app that proactively prevents overspending.", "category": "Finance", "searchQueries": ["budget app", "spending tracker", "money manager"], "seedKeywords": ["budget", "budgeting app", "expense tracker", "spending", "savings"] }, "competitors": [ { "appId": "1602117440", "title": "Example Budget", "developer": "Example Inc.", "icon": "https://...", "rating": 4.7, "reviewsCount": 48211, "isFree": true, "estimatedDownloads": 1200000, "estimatedRevenue": 240000, "humanizedDownloads": "~1.2M", "humanizedRevenue": "~$240k" } ], "keywords": [ { "keyword": "budget", "volumeScore": 78, "difficulty": 64 }, { "keyword": "expense tracker", "volumeScore": 55, "difficulty": 41 } ], "painPoints": [ { "theme": "Sync reliability", "detail": "Users report bank sync breaking and transactions not importing.", "quote": "It keeps disconnecting from my bank every week", "frequency": "high" } ], "verdictScore": 72, "verdict": "validate", "marketSummary": "A large, actively monetizing market with several apps above $100k/mo, but dominated by sync reliability complaints you can exploit.", "demandSignal": "High search demand on core terms (volume 78) with mid difficulty.", "competitionLevel": "Crowded but monetizing — leaders earn six figures monthly.", "differentiationAngle": "Win on rock-solid bank sync plus proactive overspend alerts.", "risks": ["Bank-sync infrastructure cost", "Incumbent brand trust"], "marketing": { "suggestedName": "Spendwise: Budget Guard", "suggestedSubtitle": "Stop overspending today", "suggestedKeywords": "budget,spending,expense,money,savings,finance,tracker,bills", "positioning": "The budgeting app that warns you before you overspend, not after.", "targetAudience": "Younger professionals who overspend and want guardrails.", "goToMarket": ["Launch on Product Hunt", "Target 'budget app' ASA keywords", "Partner with finance creators"] } }, "error": null, "createdAt": "2026-06-21T09:00:00.000Z", "updatedAt": "2026-06-21T09:00:48.000Z" } } ``` ### Job Fields | Field | Type | Description | | -------- | ------ | ---------------------------------------------------------- | | `status` | string | `queued` \| `processing` \| `completed` \| `failed` | | `result` | object | The full validation result (see below); `null` until done. | | `error` | string | Set only if the whole job failed. | ### Result Fields | Field | Type | Description | | ---------------------- | --------- | ------------------------------------------------------------------------------- | | `plan` | object | Refined idea summary, category, search queries, and seed keywords. | | `competitors` | array | Matching live apps, with revenue/download estimates where available. | | `keywords` | array | Seed keywords with `volumeScore` (0–100) and `difficulty` (0–100). | | `painPoints` | array | Recurring complaints mined from competitor reviews (`frequency`: low/med/high). | | `verdictScore` | number | 0–100 — how validated the idea is by the evidence. | | `verdict` | string | `build` \| `validate` \| `pivot` \| `avoid`. | | `marketSummary` | string | 2–3 sentences on whether there's a real market. | | `demandSignal` | string | One line on search demand. | | `competitionLevel` | string | One line on how crowded / monetized the space is. | | `differentiationAngle` | string | The sharpest wedge, grounded in pain points. | | `risks` | string\[] | Up to four concrete risks. | | `marketing` | object | Suggested name, subtitle, keywords, positioning, audience, go-to-market. | *** ## MCP Tools | Tool | Description | | ------------------------- | ---------------------------------------------------------- | | `validate_idea` | Start a validation run for an app idea; returns a `jobId`. | | `get_idea_validation_job` | Poll status and the full result for a run. | *** ## Errors | Status | Code | When | | ------ | --------------- | --------------------------------------------- | | 400 | INVALID\_IDEA | `idea` missing or shorter than 8 characters | | 400 | IDEA\_TOO\_LONG | `idea` longer than 600 characters | | 401 | UNAUTHORIZED | Missing or invalid credentials | | 403 | FORBIDDEN | Polling a job that belongs to another account | | 404 | JOB\_NOT\_FOUND | Unknown `jobId` | # Keyword Compare Source: https://docs.appeeky.com/docs/keyword-compare Competitor keyword overlap and gap analysis between two apps ``` GET /v1/keywords/compare ``` Compares keyword rankings between two apps to reveal shared keywords, unique strengths, competitor opportunities, and gap threats. Essential for competitive ASO (App Store Optimization) strategy. *** ## Query Parameters | Name | Type | Required | Default | Description | | ------------ | ------ | -------- | ------- | -------------------------------------------------------------------------------- | | appId | string | Yes | — | Your app's ID — numeric for Apple, package name for Google | | competitorId | string | Yes | — | Competitor's ID — numeric for Apple, package name for Google | | platform | string | No | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | No | `us` | ISO country code (e.g. `us`, `gb`, `de`, `jp`) | *** ## Code Examples ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/keywords/compare?appId=1617391485&competitorId=544007664&country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/keywords/compare?appId=1617391485&competitorId=544007664&country=us", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); console.log(`Shared keywords: ${data.summary.totalShared}`); console.log(`Your unique: ${data.summary.totalYourUnique}`); console.log(`Competitor unique: ${data.summary.totalCompetitorUnique}`); console.log(`Gaps to close: ${data.summary.totalGaps}`); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/keywords/compare", params={ "appId": "1617391485", "competitorId": "544007664", "country": "us", }, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] summary = data["summary"] print(f"Shared keywords: {summary['totalShared']}") print(f"Your unique: {summary['totalYourUnique']}") print(f"Competitor unique: {summary['totalCompetitorUnique']}") print(f"Gaps to close: {summary['totalGaps']}") ``` *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "shared": [ { "keyword": "puzzle game", "yourRank": 5, "competitorRank": 3, "rankDiff": -2, "volumeScore": 72, "difficulty": 85 }, { "keyword": "block puzzle", "yourRank": 2, "competitorRank": 8, "rankDiff": 6, "volumeScore": 65, "difficulty": 68 } ], "yourUnique": [ { "keyword": "blast game", "yourRank": 4, "competitorRank": null, "rankDiff": null, "volumeScore": 38, "difficulty": 45 } ], "competitorUnique": [ { "keyword": "candy crush", "yourRank": null, "competitorRank": 1, "rankDiff": null, "volumeScore": 88, "difficulty": 95 }, { "keyword": "match 3 game", "yourRank": null, "competitorRank": 6, "rankDiff": null, "volumeScore": 55, "difficulty": 62 } ], "gapAnalysis": [ { "keyword": "puzzle game", "yourRank": 5, "competitorRank": 3, "rankDiff": -2, "volumeScore": 72, "difficulty": 85 } ], "summary": { "totalShared": 2, "totalYourUnique": 1, "totalCompetitorUnique": 2, "totalGaps": 1, "overlapPercent": 40 } } } ``` *** ## Response Sections ### `shared` — Keywords Both Apps Rank For Keywords where both your app and the competitor appear in search results. Use `rankDiff` to see who ranks higher. ### `yourUnique` — Your Strengths Keywords that **only your app** ranks for. These are your competitive advantages — keywords where you have visibility and the competitor does not. ### `competitorUnique` — Opportunities to Target Keywords that **only the competitor** ranks for. These represent **opportunities** — keywords you could target to gain visibility in spaces where the competitor already has traction. ### `gapAnalysis` — Threats Keywords where the competitor **significantly outranks** you. These are **threats** — high-value keywords where you're losing ground and should consider optimizing. *** ## Keyword Entry Fields | Field | Type | Description | | -------------- | ------------ | -------------------------------------------------------------------------------------- | | keyword | string | The search keyword | | yourRank | number\|null | Your app's rank (null if your app doesn't rank for this keyword) | | competitorRank | number\|null | Competitor's rank (null if competitor doesn't rank for this keyword) | | rankDiff | number\|null | `yourRank - competitorRank`. Positive = you rank better. Null if one app doesn't rank. | | volumeScore | number | Estimated search volume score (0–100) | | difficulty | number | Ranking difficulty score (0–100) | ## Summary Fields | Field | Type | Description | | --------------------- | ------ | -------------------------------------------------------------- | | totalShared | number | Number of keywords both apps rank for | | totalYourUnique | number | Number of keywords only your app ranks for | | totalCompetitorUnique | number | Number of keywords only the competitor ranks for | | totalGaps | number | Number of keywords where competitor significantly outranks you | | overlapPercent | number | Percentage of keywords shared between both apps (0–100) | *** **Both apps must have keyword data.** Before using this endpoint, call `GET /v1/apps/:id/keywords` for both your app and the competitor to trigger keyword discovery. If either app has no keyword data, the comparison will be incomplete or empty. **Competitive ASO strategy**: Focus on `competitorUnique` keywords with high `volumeScore` and low `difficulty` — these are the easiest wins for expanding your keyword footprint. Then address `gapAnalysis` keywords where you're being outranked on important terms. *** ## Errors | Status | Code | When | | ------ | ---------------- | --------------------------------- | | 400 | INVALID\_APP\_ID | Non-numeric or missing app ID | | 400 | MISSING\_PARAMS | Missing `appId` or `competitorId` | | 401 | UNAUTHORIZED | Missing or invalid API key | | 429 | RATE\_LIMITED | Too many requests — slow down | | 500 | INTERNAL\_ERROR | Server error during comparison | # Keyword Compare Cluster Source: https://docs.appeeky.com/docs/keyword-compare-cluster Multi-competitor keyword overlap and lifecycle buckets for your app plus up to five rivals ``` GET /v1/keywords/compare-cluster ``` Builds a **keyword lifecycle view** for your app and **1–5 competitor** Apple app IDs: which terms appear everywhere in the cluster, which are partial overlaps, which are only yours, which appear only on the competitor side (including strict per-competitor exclusives). Uses the same latest keyword snapshot per app as [`GET /v1/keywords/compare`](/docs/keyword-compare) (two-way compare is unchanged). *** ## Query parameters | Name | Type | Required | Default | Description | | ----------- | ------ | -------- | ------- | -------------------------------------------------------------------------------- | | appId | string | Yes | — | Your app’s ID — numeric for Apple, package name for Google | | competitors | string | Yes | — | Comma-separated competitor app IDs (1–5), e.g. `544007664,284882215` | | platform | string | No | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | No | `us` | ISO country code (e.g. `us`, `gb`, `de`, `jp`) | Duplicate IDs and `appId` itself in the list are ignored. Competitors are sorted numerically in the response. *** ## Code examples ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/keywords/compare-cluster?appId=1617391485&competitors=544007664,284882215&country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const params = new URLSearchParams({ appId: "1617391485", competitors: "544007664,284882215", country: "us", }); const res = await fetch(`https://api.appeeky.com/v1/keywords/compare-cluster?${params}`, { headers: { "X-API-Key": "YOUR_API_KEY" }, }); const { data } = await res.json(); console.log(data.summary.withoutPrimaryCount, data.summary.inAllApps.count); ``` *** ## Response shape Top-level fields: | Field | Description | | ---------------------- | -------------------------------------------------------------------------- | | appId | Your app ID | | competitorIds | Sorted competitor IDs | | country | Storefront used | | appIds | Cluster order: your app first, then competitors | | summary | Counts and volume/difficulty averages per bucket | | inAllApps | Sample rows (up to 80): keyword in **every** app in the cluster | | partialOverlap | Sample rows: keyword in **2..n−1** apps (not universal) | | primaryOnly | Sample rows: only **your** app ranks | | competitorExclusive | Sample rows: exactly **one** competitor ranks; you do not | | withoutPrimary | Sample rows: you have **no** rank; at least one competitor does (gap view) | | perCompetitorExclusive | Same as competitor-exclusive terms, split by competitor app ID | Each row includes `keyword`, `ranks` (map of app ID → rank or `null`), `appsWithKeyword`, `volumeScore`, and `difficulty`. ### Summary buckets | Field | Meaning | | ------------------- | ----------------------------------------------------------------------------------- | | totalUniqueKeywords | Distinct keywords across the union of all apps’ tracked lists | | withoutPrimaryCount | Number of keywords where your app does not rank (subset view; see `withoutPrimary`) | | inAllApps | `count`, `avgVolumeScore`, `avgDifficulty` for universal keywords | | partialOverlap | Same for **partial** overlap (2..n−1 apps) | | primaryOnly | Same for **only-you** singletons | | competitorExclusive | Same for **single-competitor** terms (you absent) | Buckets are **disjoint**: every keyword falls into exactly one of `inAllApps`, `partialOverlap`, `primaryOnly`, or `competitorExclusive`. The `withoutPrimary` list is a separate convenience slice (all keywords where you do not rank). *** ## Credits This endpoint costs **4 credits** per successful call (see [Rate limits](/docs/rate-limits)). *** ## Errors | Status | Code | When | | ------ | ------------------------------------- | ---------------------------------------------------------------------------------------------- | | 400 | INVALID\_APP\_ID | Missing or non-numeric `appId` | | 400 | INVALID\_COMPETITORS | Missing `competitors`, no valid IDs, only duplicates of `appId`, or more than five competitors | | 401 | MISSING\_API\_KEY / INVALID\_API\_KEY | Auth | | 429 | RATE\_LIMIT\_EXCEEDED | Insufficient monthly credits | *** # Keyword Demand Trend Source: https://docs.appeeky.com/docs/keyword-demand-trend Market-heat snapshot for one keyword — how result count and top-app review velocity are changing. ``` GET /v1/keywords/demand-trend ``` Compares the *current* state of a keyword's SERP against the closest snapshot on or before today − N days: * **`resultCountDelta`** — change in the number of apps Apple returns for the keyword. Rising = more competition entering the niche. * **`reviewVelocity`** — average daily growth in the top app's review count over the window. A leader pulling away fast means a maturing market with a clear winner. * **`trend`** — coarse bucket so you can sort dashboards by momentum without comparing raw deltas across keywords of different sizes. *** ## Query Parameters | Name | Type | Required | Default | Description | | ------- | ------ | -------- | -------- | ------------------------------- | | keyword | string | Yes | — | The keyword to analyse | | country | string | No | `us` | ISO 3166-1 alpha-2 storefront | | device | string | No | `iphone` | `iphone` or `ipad` | | days | number | No | `14` | Comparison window in days, 3–90 | *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "keyword": "ai chat", "country": "us", "device": "iphone", "windowDays": 14, "current": { "date": "2026-04-21", "resultCount": 248, "topAppReviews": 1840221 }, "previous": { "date": "2026-04-07", "resultCount": 211, "topAppReviews": 1610115 }, "resultCountDelta": 37, "resultCountChangePct": 17.5, "reviewVelocity": 16436, "reviewVelocityPct": 14.3, "trend": "rising", "hasFullWindow": true, "interpretation": "Result count +17.5%, leader reviews +14.3% — demand is expanding faster than the leader is consolidating." }, "meta": { "lastScrapedAt": "2026-04-21T00:00:00Z", "dataAgeHours": 3, "source": "stored", "freshness": "fresh" } } ``` `trend` values: `surging` · `rising` · `steady` · `cooling` · `declining`. Buckets are deliberately coarse so noise on small keywords doesn't flip the verdict day to day. `hasFullWindow` is `false` when one of the snapshots is missing — usually for very new keywords with less than `days` of history. `interpretation` is a short, plain-English summary of the bucket and the underlying deltas — safe to surface directly in a UI and useful for LLM agents that don't want to combine raw numbers. The response-level `meta` envelope describes data freshness — see [Keyword Metrics → meta envelope](/docs/keyword-metrics#meta-envelope-response-level) for the full schema. ## Credit Cost 2 credits per request. ## Use Cases * Filter keyword tracking dashboards by `trend = surging` to surface emerging niches. * Pair with `/keywords/metrics` for "high opportunity + rising demand" lists. * Trigger alerts when a keyword you rank for moves to `cooling`. # Emerging Suggestions Source: https://docs.appeeky.com/docs/keyword-emerging-suggestions Apple autocomplete suggestions that just started surfacing — early-warning trend signal. ``` GET /v1/keywords/suggestions/emerging ``` Returns suggestions that appeared in Apple's autocomplete during the last `recentDays` window but were absent in the prior `baselineDays` window. These are the rawest possible early-trend signal — Apple has decided users are searching for these terms often enough to recommend them, but they're new enough that no one has built tracking around them yet. *** ## Query Parameters | Name | Type | Required | Default | Description | | ------------ | ------ | -------- | ------- | ------------------------------------ | | country | string | No | `us` | ISO 3166-1 alpha-2 storefront | | recentDays | number | No | `7` | Recent window, 1–30 | | baselineDays | number | No | `30` | Baseline window before recent, 7–180 | | limit | number | No | `50` | Max suggestions returned, 1–200 | `recentDays` must be smaller than `baselineDays`. *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "country": "us", "recentDays": 7, "baselineDays": 30, "asOf": "2026-04-21", "total": 17, "suggestions": [ { "suggestion": "claude code", "firstSeenAt": "2026-04-16", "bestRank": 2, "prefixes": ["c", "cl", "claude"], "appearanceCount": 12 }, { "suggestion": "ai vibes", "firstSeenAt": "2026-04-18", "bestRank": 6, "prefixes": ["a", "ai"], "appearanceCount": 6 } ], "interpretation": "17 new autocomplete suggestions surfaced in US in the last 7d (none in the prior 30d). Top mover: 'claude code' (best rank 2, 12 impressions)." }, "meta": { "lastScrapedAt": "2026-04-21T00:00:00Z", "dataAgeHours": 4, "source": "stored", "freshness": "fresh" } } ``` `interpretation` is a one-line plain-English summary that names the strongest emerging mover — handy for daily digest UIs and LLM agents. The response-level `meta` envelope describes data freshness — see [Keyword Metrics → meta envelope](/docs/keyword-metrics#meta-envelope-response-level) for the schema. Suggestions are sorted by `appearanceCount` desc → `bestRank` asc → `firstSeenAt` desc, so the top of the list is the strongest combination of "many times observed" + "high in Apple's list" + "still surfacing recently". ## Credit Cost 3 credits per request. ## Use Cases * Daily "trend radar" feed for ASO teams. * Auto-track newly-emerging suggestions before competitors catch on. * Pair with `/keywords/metrics` to filter for emerging + low-difficulty opportunities. # Expand Keyword Source: https://docs.appeeky.com/docs/keyword-expand Embedding-based semantic neighbourhood for one keyword. ``` GET /v1/keywords/expand ``` Returns the closest known keywords to the seed by semantic similarity. Unlike Apple's autocomplete, semantic expansion catches keywords that share *meaning* but not *letters* — `"to-do"` finds `"task list"`, `"focus timer"` finds `"pomodoro"`. Each result also carries the keyword's `volumeScore` and `rankVolatility` so you can rank by "high volume + similar to seed" without a second roundtrip. *** ## Query Parameters | Name | Type | Required | Default | Description | | ------- | ------ | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | keyword | string | Yes | — | The seed keyword to expand | | country | string | No | `us` | ISO 3166-1 alpha-2 storefront | | limit | number | No | `25` | Max related keywords returned, 1–100 | | appId | string | No | — | Numeric Apple App ID. When supplied, each neighbour is also annotated with `tracked` and `yourRank` so you can immediately see which expansions are actionable opportunities for the app. | *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "seed": "ai chat", "country": "us", "appId": "284882215", "total": 8, "cachedSeed": true, "related": [ { "keyword": "ai assistant", "similarity": 0.871, "volumeScore": 64, "rankVolatility": 21.3, "difficulty": 58, "tracked": true, "yourRank": 14 }, { "keyword": "chatbot", "similarity": 0.842, "volumeScore": 58, "rankVolatility": 14.8, "difficulty": 51, "tracked": true, "yourRank": null }, { "keyword": "ai conversation", "similarity": 0.811, "volumeScore": 41, "rankVolatility": 9.0, "difficulty": 33, "tracked": false, "yourRank": null } ] }, "meta": { "lastScrapedAt": "2026-04-21T07:30:00Z", "dataAgeHours": 0, "source": "stored", "freshness": "fresh" } } ``` `cachedSeed` is `true` when an embedding for the seed was already on file (the request stays fast). `false` means the seed was embedded fresh for this call — subsequent calls within \~30 days will be cached. `appId` echoes the optional caller param so MCP/agent clients can correlate batched calls. `null` when not supplied. ### Per-neighbour fields | Field | Type | Notes | | -------------- | -------------- | ------------------------------------------------------------------------------------------------------------ | | keyword | string | The semantically-adjacent keyword. | | similarity | number | Semantic similarity score to the seed (0.55–1.0; 1 = identical meaning). | | volumeScore | number \| null | Persisted volume score; `null` if not yet enriched. | | rankVolatility | number \| null | Persisted volatility; `null` if not yet enriched. | | difficulty | number \| null | Persisted difficulty score (0–100); `null` if not yet enriched. | | tracked | boolean | `true` when the keyword is in the actively tracked set for this country. | | yourRank | number \| null | Owner app's latest rank (only populated when `appId` is supplied). `null` = the app does not currently rank. | The `difficulty + tracked + yourRank` triple turns "interesting neighbour" into "actionable opportunity": neighbours with `tracked: false` are completely undiscovered, neighbours with `tracked: true` and `yourRank: null` are pre-vetted easy wins. The response-level `meta` envelope describes data freshness — see [Keyword Metrics → meta envelope](/docs/keyword-metrics#meta-envelope-response-level) for the schema. `source` will be `hybrid` when the seed had to be embedded fresh. ## Credit Cost 4 credits per request. ## Use Cases * "More like this" suggestions on a keyword detail page. * Brainstorm support: type a candidate, see semantically-adjacent search terms you would have missed. * Build a personalised tracking list by expanding a small set of seed keywords. # Competitor Keyword Gap Source: https://docs.appeeky.com/docs/keyword-gap Top-N keywords competitors rank for that the primary app doesn't (or ranks > 50). ``` GET /v1/keywords/gap ``` Surfaces the keyword opportunities you're leaving on the table. For up to five competitors, returns the top keywords where: 1. At least one competitor ranks, AND 2. Your app either doesn't rank or ranks worse than 50. Each opportunity carries an `opportunityScore`: ``` opportunityScore = volumeScore // bigger traffic = bigger upside × (1 − difficulty/100) // prefer attainable × (1 − bestCompetitorRank/100) // competitor in top 10 = real signal × (yourRank IS NULL ? 1.0 : yourRank/100) // bigger gap to close = bigger upside ``` Results are sorted by `opportunityScore` descending. *** ## Query Parameters | Name | Type | Required | Default | Description | | ------------- | ------ | -------- | -------- | ------------------------------------------- | | appId | string | Yes | — | Numeric Apple app ID for your app | | competitorIds | string | Yes | — | 1–5 numeric competitor IDs, comma separated | | country | string | No | `us` | ISO 3166-1 alpha-2 storefront | | device | string | No | `iphone` | `iphone` or `ipad` | | limit | number | No | `25` | Max opportunities returned, 1–100 | *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "ok": true, "data": { "appId": "123456789", "competitorIds": ["456", "789"], "country": "us", "device": "iphone", "opportunities": [ { "keyword": "school", "bestCompetitor": "456", "bestCompetitorRank": 18, "yourRank": null, "volumeScore": 82, "difficulty": 43, "opportunityScore": 38.33 }, { "keyword": "productivity", "bestCompetitor": "456", "bestCompetitorRank": 18, "yourRank": 71, "volumeScore": 68, "difficulty": 43, "opportunityScore": 22.59 } ], "summary": { "totalGaps": 25, "avgVolumeScore": 54.7, "avgDifficulty": 41.2 } } } ``` `yourRank: null` means you don't rank for the keyword at all. Otherwise it's your current rank (always > 50 by definition of "gap"). ## Credit Cost 4 credits per request. ## Relationship to other endpoints * [`/v1/keywords/compare`](./keyword-compare.md) — full pairwise comparison (shared / unique / gap). * [`/v1/keywords/compare-cluster`](./keyword-compare-cluster.md) — multi-app overlap buckets. * This endpoint is the **action-oriented** view: ranked, capped, decision-ready. ## Use Cases * Mobile **Insights** tab "Opportunities" section. * One-tap → `POST /v1/keywords/track` to start tracking the suggested keyword. * Daily / weekly digest emails: "5 keywords you should target this week." # Keyword Metrics Source: https://docs.appeeky.com/docs/keyword-metrics Search volume and ranking difficulty scores for any keyword ``` GET /v1/keywords/metrics ``` Returns detailed search volume and ranking difficulty scores for a keyword. The scoring algorithms analyse the store's search results, top app review volumes, autocomplete position, rating distributions, and developer diversity to produce actionable scores. Works for both the App Store and Google Play (`platform=google`) using store-native signals. *** ## Query Parameters | Name | Type | Required | Default | Description | | --------- | ------ | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- | | keyword | string | Yes | — | Search keyword (min 2 characters) | | platform | string | No | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | No | `us` | ISO country code — when **`countries` is omitted** (single storefront) | | countries | string | No | — | Comma-separated ISO codes (e.g. `us,gb,de`) for multiple storefronts in one request (max 25). When set, **`country` is ignored**. | | lang | string | No | `en` | Google Play language code (used when `platform=google`) | *** ## Code Examples ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/keywords/metrics?keyword=puzzle+game&country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/keywords/metrics?keyword=puzzle+game&country=us", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); console.log(`Volume: ${data.volumeScore}/100`); console.log(`Difficulty: ${data.difficulty}/100`); console.log(`Results: ${data.resultCount}`); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/keywords/metrics", params={"keyword": "puzzle game", "country": "us"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] print(f"Volume: {data['volumeScore']}/100") print(f"Difficulty: {data['difficulty']}/100") print(f"Results: {data['resultCount']}") ``` ```bash Google Play theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/keywords/metrics?platform=google&keyword=puzzle+game&country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` On **Google Play**, `volumeScore` and `difficulty` are computed from store-native signals (search results, ratings, review counts, autocomplete). The enrichment fields `rankVolatility`, `marketDominance`, and `historicalDays` are currently Apple-only — on Google they return `null` and `interpretation.trustworthy` is `false`. *** ## Response Single storefront (default — same as before): ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "keyword": "puzzle game", "country": "us", "volumeScore": 72, "difficulty": 85, "resultCount": 3450, "topAppsAvgRating": 4.6, "topAppsAvgReviews": 284500, "developerDiversity": 0.82, "rankVolatility": 12.4, "marketDominance": 0.31, "historicalDays": 90, "updatedAt": "2026-04-21T03:14:00Z", "interpretation": { "stability": "volatile", "competition": "competitive", "trustworthy": true } }, "meta": { "lastScrapedAt": "2026-04-21T03:14:00Z", "dataAgeHours": 7, "source": "stored", "freshness": "fresh" } } ``` Multi-country (`?countries=us,gb`): ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "keyword": "puzzle game", "countries": ["us", "gb"], "results": { "us": { "keyword": "puzzle game", "country": "us", "volumeScore": 72, "difficulty": 85, "resultCount": 3450, "topAppsAvgRating": 4.6, "topAppsAvgReviews": 284500, "developerDiversity": 0.82 }, "gb": { "keyword": "puzzle game", "country": "gb", "volumeScore": 68, "difficulty": 80, "resultCount": 3100, "topAppsAvgRating": 4.5, "topAppsAvgReviews": 200000, "developerDiversity": 0.78 } } } } ``` *** ## Response Fields | Field | Type | Description | | ------------------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | keyword | string | The queried keyword | | country | string | ISO country code (single-storefront response only) | | countries | array | List of ISO codes (multi-storefront response only) | | results | object | Per-country metric objects keyed by ISO code (multi-storefront only) | | volumeScore | number | Estimated search volume score (0–100) | | difficulty | number | Ranking difficulty score (0–100) | | resultCount | number | Total apps in the App Store search results for this keyword | | topAppsAvgRating | number | Average star rating of the top-ranking apps (1.0–5.0) | | topAppsAvgReviews | number | Average review count of the top-ranking apps | | developerDiversity | number | Ratio of unique developers in the top results (0.0–1.0). Higher = more diverse. | | rankVolatility | number \| null | Average daily rank change observed across the top results — higher = more chaotic SERP, lower = stable incumbents. `null` if no historical data yet. | | marketDominance | number \| null | Share of total review weight held by the #1 app on this keyword (0.0–1.0). High dominance = one app owns the keyword. `null` if no historical data yet. | | historicalDays | number \| null | Number of days of historical observations the enriched signals are based on. `null` if not yet enriched. | | updatedAt | string \| null | ISO timestamp of the last enrichment refresh. `null` if not yet enriched. | | interpretation | object | Plain-English bucketing of the volatility / dominance / history signals — see "Interpretation" below. | ### `interpretation` | Field | Type | Values | Meaning | | ----------- | ------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | | stability | string | `stable` (volatility \< 3), `moderate` (3–10), `volatile` (> 10), `unknown` | How shaky the SERP has been over the trailing window. | | competition | string | `fragmented` (dominance \< 0.3), `competitive` (0.3–0.6), `dominated` (> 0.6), `unknown` | How concentrated review-share is at the top of the SERP. | | trustworthy | boolean | `true` when `historicalDays >= 7` | False means the volatility/dominance numbers exist but the trend they describe is too short to act on. | Use `interpretation` for at-a-glance UI labels and LLM summaries — the underlying numerical fields stay the source of truth for filtering / sorting. ### `meta` envelope (response-level) Every keyword response includes a `meta` block describing data freshness: | Field | Type | Description | | ------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | lastScrapedAt | string \| null | ISO timestamp of the most recent data point that backs this response. | | dataAgeHours | number \| null | Whole-hour age relative to `lastScrapedAt`. | | source | string | `stored` (served from a precomputed snapshot), `live` (fetched from the App Store in real-time for this request), or `hybrid` (a snapshot enriched with a live lookup). | | freshness | string | `fresh` (\< 24h), `stale` (24h–7d), `very-stale` (> 7d), or `unknown`. | *** ## Scoring Algorithms ### Volume Score (0–100) The volume score estimates relative search popularity using three weighted signals: | Signal | Weight | Description | | ------------------ | ------ | ----------------------------------------------------------------------- | | iTunes resultCount | 40% | Number of apps in search results, normalized to a max of 200 | | Top app reviews | 40% | Average review count of top apps, normalized to a max of 500K | | Autocomplete bonus | 20% | Bonus points if the keyword appears in Apple's autocomplete suggestions | ### Difficulty Score (0–100) The difficulty score measures how hard it is to rank in the top results: | Signal | Weight | Description | | -------------------- | ------ | ------------------------------------------------------------------ | | Average rating | 20% | Higher avg rating of top apps = harder to compete | | Average review count | 35% | More reviews on top apps = stronger incumbents | | Developer diversity | 25% | Low diversity = dominated by a few publishers = harder to break in | | Dominance ratio | 20% | How much of the market the #1 app captures relative to others | *** ## Score Ranges | Range | Label | Volume Meaning | Difficulty Meaning | | ------ | --------- | ------------------------------------ | -------------------------------------------- | | 0–20 | Very Low | Minimal search traffic | Very easy to rank — few strong competitors | | 21–40 | Low | Some searches, niche keyword | Relatively easy — moderate competition | | 41–60 | Medium | Moderate search volume | Competitive — established apps present | | 61–80 | High | Popular keyword, significant traffic | Hard — strong incumbents dominate | | 81–100 | Very High | Top-tier keyword, massive volume | Extremely hard — dominated by top publishers | *** The scoring algorithms analyze the **top search results** from iTunes at query time. Scores may fluctuate slightly over time as the App Store rankings change. **Combine with opportunity score**: Use this endpoint to evaluate individual keywords, then cross-reference with the `opportunity` score from `GET /v1/apps/:id/keywords` for a complete prioritization framework. High volume + low difficulty + high opportunity = your best ASO targets. *** ## Errors | Status | Code | When | | ------ | ---------------- | --------------------------------- | | 400 | INVALID\_KEYWORD | Keyword shorter than 2 characters | | 401 | UNAUTHORIZED | Missing or invalid API key | | 429 | RATE\_LIMITED | Too many requests — slow down | | 500 | INTERNAL\_ERROR | Error calculating metrics | # Keyword Movers (per app) Source: https://docs.appeeky.com/docs/keyword-movers Per-keyword rank changes for one app — top gainers, top losers, new entries, dropped out. ``` GET /v1/keywords/movers ``` Compares the latest snapshot of one app's keyword ranks vs the closest snapshot on or before `today − days`, then buckets every keyword into: * **Top gainers** — rank improved (lower rank number) * **Top losers** — rank dropped * **New entries** — newly ranking keywords * **Dropped out** — keywords the app no longer ranks for Both `topGainers` and `topLosers` are sorted by `|rankChange| × volumeScore` so high-traffic moves bubble to the top. *** ## Query Parameters | Name | Type | Required | Default | Description | | ------- | ------ | -------- | -------- | ------------------------------- | | appId | string | Yes | — | Numeric Apple app ID | | country | string | No | `us` | ISO 3166-1 alpha-2 storefront | | device | string | No | `iphone` | `iphone` or `ipad` | | days | number | No | `7` | Comparison window in days, 1–90 | | limit | number | No | `20` | Max entries per bucket, 1–50 | *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "ok": true, "data": { "appId": "123456789", "country": "us", "device": "iphone", "days": 7, "period": { "current": "2026-04-21", "previous": "2026-04-14" }, "summary": { "totalKeywords": 87, "gainers": 22, "losers": 14, "newEntries": 9, "droppedOut": 4 }, "topGainers": [ { "keyword": "ai writer", "currentRank": 8, "previousRank": 41, "rankChange": 33, "volumeScore": 71, "difficulty": 58 } ], "topLosers": [ { "keyword": "notes", "currentRank": 64, "previousRank": 22, "rankChange": -42, "volumeScore": 88, "difficulty": 71 } ], "newEntries": [ { "keyword": "claude pro", "currentRank": 3, "previousRank": null, "rankChange": null, "volumeScore": 62, "difficulty": 35 } ], "droppedOut": [ { "keyword": "diary", "currentRank": null, "previousRank": 87, "rankChange": null, "volumeScore": 41, "difficulty": 28 } ] } } ``` ## Rank Change Interpretation | Field | Meaning | | ------------------------ | ------------------------------------- | | `rankChange > 0` | Improved (e.g. went from rank 41 → 8) | | `rankChange < 0` | Dropped | | `previousRank` is `null` | New keyword (newly ranking) | | `currentRank` is `null` | No longer ranking | ## Credit Cost 3 credits per request. ## Use Cases * "Monday morning ASO" digest. * Smart Alerts trigger source ("a tracked keyword fell out of top 10"). * Correlating metadata changes with ranking impact (pair with the upcoming ASO Timeline endpoint). # Keyword Suggestions Source: https://docs.appeeky.com/docs/keyword-suggestions Autocomplete suggestions from Apple's App Store search ``` GET /v1/keywords/suggestions ``` Returns autocomplete suggestions from the store's search for a given term. These are the same suggestions users see when they type into the App Store (or Google Play, with `platform=google`) search bar — making them a powerful source for discovering real search queries. *** ## Query Parameters | Name | Type | Required | Default | Description | | -------- | ------ | -------- | ------- | ------------------------------------------------------------------------------------- | | term | string | Yes | — | Search prefix to get suggestions for (min 2 characters) | | platform | string | No | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | No | `us` | ISO country code (e.g. `us`, `gb`, `de`, `jp`) | | lang | string | No | `en` | Google Play language code (used when `platform=google`) | | expand | number | No | `0` | Set to `1` to query prefix variants (`term a`, `term b`, ...) for long-tail discovery | | metrics | number | No | `0` | Set to `1` to enrich the top 10 suggestions with volume and difficulty scores | *** ## Code Examples ### Basic Suggestions ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/keywords/suggestions?term=puzzle&country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/keywords/suggestions?term=puzzle&country=us", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); data.suggestions.forEach((s) => { console.log(`#${s.suggestRank}: ${s.term}`); }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/keywords/suggestions", params={"term": "puzzle", "country": "us"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] for s in data["suggestions"]: print(f"#{s['suggestRank']}: {s['term']}") ``` ### With Expand (Long-Tail Discovery) ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/keywords/suggestions?term=puzzle&country=us&expand=1" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/keywords/suggestions?term=puzzle&country=us&expand=1", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); console.log(`${data.suggestions.length} long-tail suggestions found`); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/keywords/suggestions", params={"term": "puzzle", "country": "us", "expand": 1}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] print(f"{len(data['suggestions'])} long-tail suggestions found") ``` ### With Metrics (Volume & Difficulty) ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/keywords/suggestions?term=puzzle&country=us&metrics=1" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/keywords/suggestions?term=puzzle&country=us&metrics=1", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); data.suggestions.forEach((s) => { console.log(`${s.term}: volume ${s.volumeScore ?? "—"}, difficulty ${s.difficulty ?? "—"}`); }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/keywords/suggestions", params={"term": "puzzle", "country": "us", "metrics": 1}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] for s in data["suggestions"]: vol = s.get("volumeScore", "—") diff = s.get("difficulty", "—") print(f"{s['term']}: volume {vol}, difficulty {diff}") ``` *** ## Response ### Basic (default) ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "term": "puzzle", "country": "us", "suggestions": [ { "term": "puzzle game", "suggestRank": 1 }, { "term": "puzzle games free", "suggestRank": 2 }, { "term": "puzzle & dragons", "suggestRank": 3 }, { "term": "puzzle bobble", "suggestRank": 4 }, { "term": "puzzle page", "suggestRank": 5 } ] } } ``` ### With `metrics=1` ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "term": "puzzle", "country": "us", "suggestions": [ { "term": "puzzle game", "suggestRank": 1, "volumeScore": 72, "difficulty": 85 }, { "term": "puzzle games free", "suggestRank": 2, "volumeScore": 58, "difficulty": 71 }, { "term": "puzzle & dragons", "suggestRank": 3, "volumeScore": 45, "difficulty": 32 }, { "term": "puzzle bobble", "suggestRank": 4 }, { "term": "puzzle page", "suggestRank": 5 } ] } } ``` When `metrics=1` is set, only the **top 10 suggestions** are enriched with `volumeScore` and `difficulty`. Suggestions beyond position 10 will not include these fields. This keeps response times reasonable since metric calculation requires additional API calls. *** ## Response Fields | Field | Type | Description | | ----------- | ------ | --------------------------------------- | | term | string | The original search prefix | | country | string | ISO country code used | | suggestions | array | List of autocomplete suggestion objects | ### Suggestion Object | Field | Type | Always Present | Description | | ----------- | ------ | -------------- | ---------------------------------------------------------------- | | term | string | Yes | The suggested search keyword | | suggestRank | number | Yes | Position in Apple's autocomplete results (1 = most popular) | | volumeScore | number | No | Search volume score (0–100). Only present when `metrics=1`. | | difficulty | number | No | Ranking difficulty score (0–100). Only present when `metrics=1`. | *** ## How suggestRank Works The `suggestRank` value represents the position of this suggestion in Apple's autocomplete dropdown. A rank of **1** means this is the most popular completion that Apple shows first when a user types the given prefix. Lower `suggestRank` values generally correlate with higher search volume, making this a useful proxy for keyword popularity even without the `metrics` flag. *** ## Expand Mode When `expand=1` is set, the API queries **prefix variants** by appending each letter of the alphabet to your term: * `puzzle a` → puzzle adventure, puzzle action, ... * `puzzle b` → puzzle bobble, puzzle blocks, ... * `puzzle c` → puzzle craft, puzzle cats, ... * ... and so on through `puzzle z` This dramatically increases the number of suggestions returned and is ideal for **long-tail keyword discovery**. You may receive 50–200+ suggestions in a single request depending on the term. *** **Discover long-tail keywords**: Use suggestions to find keywords that real users are actively searching for in the App Store. Combine `expand=1` with `metrics=1` to get a comprehensive list of long-tail keywords ranked by search volume — these often have lower competition and are easier to rank for. *** ## Errors | Status | Code | When | | ------ | --------------- | ------------------------------------- | | 400 | INVALID\_TERM | Term shorter than 2 characters | | 401 | UNAUTHORIZED | Missing or invalid API key | | 429 | RATE\_LIMITED | Too many requests — slow down | | 500 | INTERNAL\_ERROR | Error fetching suggestions from Apple | # Keyword Suggestion History Source: https://docs.appeeky.com/docs/keyword-suggestions-history Per-day appearance log for one Apple autocomplete suggestion. ``` GET /v1/keywords/suggestions/history ``` For one suggestion (e.g. `"ai chat"`), returns the per-day record of every prefix that surfaced it in Apple's autocomplete, plus the best rank Apple placed it in. Use this to confirm whether a suggestion is sticky or a one-day flicker, and to understand which prefix paths users follow to discover it. *** ## Query Parameters | Name | Type | Required | Default | Description | | ---------- | ------ | -------- | ------- | ------------------------------ | | suggestion | string | Yes | — | Exact autocomplete suggestion | | country | string | No | `us` | ISO 3166-1 alpha-2 storefront | | days | number | No | `30` | Lookback window in days, 3–180 | *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "suggestion": "ai chat", "country": "us", "windowDays": 30, "firstSeen": "2026-04-04", "lastSeen": "2026-04-21", "isCurrentlyVisible": true, "series": [ { "date": "2026-04-04", "bestRank": 9, "prefixes": ["a", "ai"], "appearances": 2 }, { "date": "2026-04-05", "bestRank": 7, "prefixes": ["a", "ai", "ai "], "appearances": 3 }, { "date": "2026-04-21", "bestRank": 3, "prefixes": ["a", "ai", "ai c"], "appearances": 4 } ], "trend": { "direction": "promoted", "startRank": 9, "endRank": 3, "rankImprovement": 6, "newPrefixes": ["ai c"] }, "interpretation": "Apple promoted 'ai chat' from rank 9 to rank 3 — strong demand signal. New prefix coverage: ai c." }, "meta": { "lastScrapedAt": "2026-04-21T00:00:00Z", "dataAgeHours": 4, "source": "stored", "freshness": "fresh" } } ``` `isCurrentlyVisible` is `true` when the suggestion appeared in today's snapshot — a quick way to confirm whether the trend is still live before acting on it. ### `trend` Window-over-window read of how Apple's perception of the suggestion is moving: | Field | Type | Meaning | | --------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | direction | string | `promoted` (rank improved by ≥ 2), `demoted` (worsened by ≥ 2), `stable`, `fresh` (only one day of data), `unknown` (no observations). | | startRank | number \| null | Best rank on the first day of the window. | | endRank | number \| null | Best rank on the last observed day. | | rankImprovement | number \| null | `startRank − endRank` — positive = improved (lower rank number = higher in dropdown). | | newPrefixes | string\[] | Prefixes seen in the most recent 7 days that weren't seen earlier in the window — Apple expanding the suggestion into new prefix neighbourhoods is the strongest possible "demand rising" signal we can see. | `interpretation` is a one-line plain-English summary of the trend — safe to surface directly in a UI and useful for LLM agents. The response-level `meta` envelope describes data freshness — see [Keyword Metrics → meta envelope](/docs/keyword-metrics#meta-envelope-response-level) for the schema. ### Data freshness The suggestion archive is refreshed daily per storefront, so the response reflects the most recent overnight snapshot. Use the response-level `meta` envelope above to read the exact `lastScrapedAt` and `freshness` band for each call. ## Credit Cost 2 credits per request. ## Use Cases * Decide whether to invest in ranking for a suggestion based on its history (sticky vs one-off). * Detect when a previously-strong suggestion stops being surfaced (signal of changing search behaviour). * Build a "discovery path" view: which prefixes users type to reach the suggestion. # Keyword Trends Source: https://docs.appeeky.com/docs/keyword-trends Historical keyword rank trends for an app over time ``` GET /v1/apps/:id/keywords/trends ``` Returns historical rank data for a specific keyword and app over a configurable time window. Use this to track how your keyword rankings change over time and measure the impact of ASO optimizations. *** ## Path Parameters | Name | Type | Description | | ---- | ------ | ---------------------------------------------------------------------------------------- | | id | string | App ID — numeric for Apple (`1617391485`), package name for Google (`com.spotify.music`) | ## Query Parameters | Name | Type | Required | Default | Description | | -------- | ------ | -------- | -------- | -------------------------------------------------------------------------------- | | keyword | string | Yes | — | Search keyword (min 2 characters) | | platform | string | No | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | No | `us` | ISO country code (e.g. `us`, `gb`, `de`, `jp`) | | device | string | No | `iphone` | `iphone` or `ipad` (Apple only) | | days | number | No | `30` | Number of days of history to return (7–90) | *** ## Code Examples ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/apps/1617391485/keywords/trends?keyword=puzzle+game&country=us&days=30" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/apps/1617391485/keywords/trends?keyword=puzzle+game&country=us&days=30", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); console.log(`Keyword: ${data.keyword}`); console.log(`7-day change: ${data.rankChange7d}`); console.log(`30-day change: ${data.rankChange30d}`); console.log(`Trend: ${data.trend}`); data.history.forEach((point) => { console.log(` ${point.date}: rank #${point.rank}`); }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/apps/1617391485/keywords/trends", params={"keyword": "puzzle game", "country": "us", "days": 30}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] print(f"Keyword: {data['keyword']}") print(f"7-day change: {data['rankChange7d']}") print(f"30-day change: {data['rankChange30d']}") print(f"Trend: {data['trend']}") for point in data["history"]: print(f" {point['date']}: rank #{point['rank']}") ``` *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "keyword": "puzzle game", "appId": "1617391485", "country": "us", "history": [ { "date": "2026-01-18", "rank": 12 }, { "date": "2026-01-19", "rank": 11 }, { "date": "2026-01-20", "rank": 11 }, { "date": "2026-01-21", "rank": 9 }, { "date": "2026-01-22", "rank": 8 }, { "date": "2026-01-23", "rank": 8 }, { "date": "2026-01-24", "rank": 7 }, { "date": "2026-01-25", "rank": 6 }, { "date": "2026-01-26", "rank": 5 }, { "date": "2026-01-27", "rank": 5 } ], "rankChange7d": 6, "rankChange30d": 7, "trend": "improving" } } ``` *** ## Response Fields | Field | Type | Description | | ------------- | ------ | ------------------------------------------------------------------------------- | | keyword | string | The queried keyword | | appId | string | Apple App ID | | country | string | ISO country code used | | history | array | Array of daily rank data points (see History Object below) | | rankChange7d | number | Rank change over the last 7 days. Positive = improved (rank number decreased). | | rankChange30d | number | Rank change over the last 30 days. Positive = improved (rank number decreased). | | trend | string | Overall trend direction: `"improving"`, `"declining"`, or `"stable"` | ### History Object | Field | Type | Description | | ----- | ------ | --------------------------------------- | | date | string | Date in `YYYY-MM-DD` format | | rank | number | App's rank for the keyword on this date | *** ## Understanding Rank Changes Rank change values are calculated as the **improvement in position**, not the raw difference in rank number: * **Positive value** = rank **improved** (e.g. moved from #12 to #5 → `rankChange7d: 7`) * **Negative value** = rank **declined** (e.g. moved from #5 to #8 → `rankChange7d: -3`) * **Zero** = rank stayed the same Historical data points are only available for dates the keyword has been tracked. Add a keyword to your tracked set with `POST /v1/keywords/track` to start collecting history; trends become richer the longer a keyword has been tracked. **Short-term vs long-term analysis**: Use `days=7` for quick feedback on recent ASO changes (metadata updates, new screenshots, keyword tweaks). Use `days=30` or `days=90` for measuring long-term keyword strategy effectiveness and seasonal trends. *** ## Errors | Status | Code | When | | ------ | ---------------- | --------------------------------- | | 400 | INVALID\_APP\_ID | Non-numeric or missing app ID | | 400 | INVALID\_KEYWORD | Keyword shorter than 2 characters | | 400 | INVALID\_DAYS | Days value outside 7–90 range | | 401 | UNAUTHORIZED | Missing or invalid API key | | 429 | RATE\_LIMITED | Too many requests — slow down | | 500 | INTERNAL\_ERROR | Server error fetching trend data | # Keyword Visibility Score Source: https://docs.appeeky.com/docs/keyword-visibility Per-app share-of-voice score (0–100) across the keywords it ranks for, with a daily series and top contributors. ``` GET /v1/keywords/visibility ``` A single 0–100 score answering: "how visible is this app across all the keywords it ranks for?" Each keyword the app ranks for contributes `volume_score × rank_weight`, where the rank weight curve favors top-3 placements: | Rank | Weight | | ------------- | ------ | | 1 | 1.00 | | 2–3 | 0.85 | | 4–5 | 0.70 | | 6–10 | 0.50 | | 11–20 | 0.30 | | 21–50 | 0.15 | | 51–100 | 0.05 | | >100 / absent | 0 | The final score is normalized against the union of keywords the app ranked for in the window, so you can compare scores across days. *** ## Query Parameters | Name | Type | Required | Default | Description | | ------- | ------ | -------- | -------- | ----------------------------- | | appId | string | Yes | — | Numeric Apple app ID | | country | string | No | `us` | ISO 3166-1 alpha-2 storefront | | device | string | No | `iphone` | `iphone` or `ipad` | | days | number | No | `30` | Window length, 7–90 | *** ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "appId": "123456789", "country": "us", "device": "iphone", "days": 30, "score": 47.3, "scoreChange": 5.8, "series": [ { "date": "2026-03-22", "score": 41.5, "rankedCount": 29 }, { "date": "2026-03-23", "score": 42.7, "rankedCount": 31 }, { "date": "2026-04-21", "score": 47.3, "rankedCount": 33 } ], "topContributors": [ { "keyword": "claude ai", "rank": 2, "volumeScore": 74, "contribution": 62.9 }, { "keyword": "ai assistant", "rank": 5, "volumeScore": 68, "contribution": 47.6 } ], "breakdown": [ { "bucket": "top3", "rankedCount": 4, "weightedScore": 312.5, "share": 78.4 }, { "bucket": "top10", "rankedCount": 11, "weightedScore": 605.0, "share": 64.1 }, { "bucket": "top30", "rankedCount": 22, "weightedScore": 812.7, "share": 47.3 } ], "breakdownByBucket": { "top3": { "rankedCount": 4, "weightedScore": 312.5, "share": 78.4 }, "top10": { "rankedCount": 11, "weightedScore": 605.0, "share": 64.1 }, "top30": { "rankedCount": 22, "weightedScore": 812.7, "share": 47.3 } } }, "meta": { "lastScrapedAt": "2026-04-21T00:00:00Z", "dataAgeHours": 4, "source": "stored", "freshness": "fresh" } } ``` `scoreChange` is the difference between the latest and the first day in the window — a positive value means visibility improved. `breakdown` decomposes today's score into Top-3 / Top-10 / Top-30 buckets so you can see *where* the score is coming from. `share` is on the same 0–100 scale as the top-line score; a Top-3 share much higher than Top-10 means the score is driven by a few hero keywords. `breakdownByBucket` is the same data as `breakdown`, exposed as a fixed-shape object so clients can read `breakdownByBucket.top3.share` directly without scanning an array. The `breakdown` array is preserved for backward compatibility — both fields are always populated. 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 * Mobile **Insights** tab headline metric. * Weekly ASO report opener: "Visibility is up 5.8 points week over week." * Competitor benchmarking when called against multiple `appId`s. # Localize Metadata Source: https://docs.appeeky.com/docs/localize-metadata Translate app store metadata into up to 10 languages with an LLM and optionally publish to App Store Connect ``` POST /v1/localizations GET /v1/localizations/jobs/:jobId ``` Translate and culturally localize an app's store metadata — title, subtitle, keywords, promotional text, description, and what's new — into **up to 10 languages** in a single run. An LLM adapts each field for native speakers (not a word-for-word translation) and respects App Store character limits. The work runs asynchronously on a background job, so `POST /v1/localizations` returns a `jobId` immediately (HTTP `202`). Poll `GET /v1/localizations/jobs/:jobId` for the per-locale results. When the app is connected in your Appeeky account, each translation is saved as an editable **draft** (visible in the dashboard Localizations tab). Set `publish: true` to push each translation straight to the live **App Store Connect** listing after translating. Direct publishing requires a connected App Store Connect key — see [App Store Connect](/docs/app-store-connect-overview). *** ## Create a run ``` POST /v1/localizations ``` ### Request Body | Field | Type | Required | Default | Description | | -------------- | --------- | -------- | ------- | ------------------------------------------------------------------------------------------------------ | | `appId` | string | Yes\* | — | App Store numeric id (preferred). Provide this **or** `userAppId`. | | `userAppId` | string | Yes\* | — | Appeeky `user_apps` row id (uuid). Alternative to `appId`. | | `locales` | string\[] | Yes | — | BCP-47 target locales, e.g. `["tr","de-DE","ja","pt-BR"]`. **1–10** entries. | | `sourceLocale` | string | No | `en-US` | Locale to translate **from**. | | `publish` | boolean | No | `false` | When `true`, publish each translation to the live App Store Connect listing. | | `source` | object | No | — | Source copy override (see below). Falls back to the app's existing draft / base metadata when omitted. | \* Provide either `appId` or `userAppId`. **`source` object** (all fields optional, all `string \| null`): | Field | Description | | ------------------ | ----------------------------- | | `title` | App name | | `subtitle` | Subtitle | | `description` | Long description | | `keywords` | Comma-separated keyword field | | `promotional_text` | Promotional text | | `whats_new` | Release notes / what's new | Cost is metered at **3 credits per requested locale** (one LLM call each). A 5-language run costs 15 credits. The poll endpoint is free. ### Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/localizations" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "913335252", "locales": ["tr", "de-DE", "ja", "pt-BR"], "sourceLocale": "en-US", "publish": false }' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} // 1. Start the run const start = await fetch("https://api.appeeky.com/v1/localizations", { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ appId: "913335252", locales: ["tr", "de-DE", "ja", "pt-BR"], sourceLocale: "en-US", publish: false, }), }); const { data } = await start.json(); const jobId = data.jobId; // 2. Poll for results async function poll() { const res = await fetch( `https://api.appeeky.com/v1/localizations/jobs/${jobId}`, { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); if (data.status === "completed" || data.status === "failed") return data; await new Promise((r) => setTimeout(r, 3000)); return poll(); } console.log(await poll()); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import time, requests headers = {"X-API-Key": "YOUR_API_KEY"} start = requests.post( "https://api.appeeky.com/v1/localizations", headers=headers, json={ "appId": "913335252", "locales": ["tr", "de-DE", "ja", "pt-BR"], "sourceLocale": "en-US", "publish": False, }, ) job_id = start.json()["data"]["jobId"] while True: res = requests.get( f"https://api.appeeky.com/v1/localizations/jobs/{job_id}", headers=headers, ).json()["data"] if res["status"] in ("completed", "failed"): break time.sleep(3) print(res) ``` ### Response (`202 Accepted`) ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "jobId": "f3c1a2b4-...", "triggerRunId": "run_...", "status": "queued", "locales": ["tr", "de-DE", "ja", "pt-BR"], "publish": false, "tracked": true } } ``` | Field | Type | Description | | -------------- | ------- | --------------------------------------------------------------------- | | `jobId` | string | Use this to poll for results. | | `triggerRunId` | string | Underlying background run id (for support/debugging). | | `status` | string | Always `queued` on creation. | | `tracked` | boolean | Whether the app is connected in Appeeky (drafts are saved when true). | *** ## Poll for results ``` GET /v1/localizations/jobs/:jobId ``` ### Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "jobId": "f3c1a2b4-...", "status": "completed", "appId": "913335252", "sourceLocale": "en-US", "locales": ["tr", "de-DE", "ja"], "publish": false, "results": [ { "locale": "tr", "status": "translated", "saved": true, "published": false, "fields": { "title": "Block Blast! - Blok Bulmaca", "subtitle": "Rahatlatıcı blok oyunu", "keywords": "blok,bulmaca,puzzle,zeka,rahatlatıcı,oyun", "promotional_text": "Milyonlarca oyuncuya katıl!", "description": "...", "whats_new": null } }, { "locale": "ja", "status": "failed", "saved": false, "published": false, "fields": null, "error": "..." } ], "error": null, "createdAt": "2026-06-20T21:00:00.000Z", "updatedAt": "2026-06-20T21:00:42.000Z" } } ``` ### Job Fields | Field | Type | Description | | --------- | ------ | ---------------------------------------------------------- | | `status` | string | `queued` \| `processing` \| `completed` \| `failed` | | `results` | array | One entry per locale (see below); `null` until processing. | | `error` | string | Set only if the whole job failed. | ### Result Fields (per locale) | Field | Type | Description | | ----------- | ------- | ---------------------------------------------------------------------------- | | `locale` | string | The target locale. | | `status` | string | `translated` (draft ready) \| `published` (pushed to store) \| `failed`. | | `fields` | object | The localized metadata, or `null` on failure. | | `saved` | boolean | Whether the translation was saved as a draft (only when the app is tracked). | | `published` | boolean | Whether it was published to the live App Store Connect listing. | | `error` | string | Failure reason for this locale, when applicable. | *** ## MCP Tools | Tool | Description | | ----------------------- | ------------------------------------------------- | | `localize_app_metadata` | Start a batch translation run; returns a `jobId`. | | `get_localization_job` | Poll status and per-locale results for a run. | *** ## Errors | Status | Code | When | | ------ | ------------------ | ------------------------------------------------------------ | | 400 | BAD\_REQUEST | `locales` empty, or neither `appId` nor `userAppId` provided | | 400 | TOO\_MANY\_LOCALES | More than 10 locales requested in one run | | 400 | NO\_SOURCE | No source copy to translate and the app isn't connected | | 401 | UNAUTHORIZED | Missing or invalid credentials | | 403 | FORBIDDEN | Polling a job that belongs to another account | | 404 | APP\_NOT\_FOUND | `userAppId` not found for this user | | 404 | JOB\_NOT\_FOUND | Unknown `jobId` | # Market Activity Source: https://docs.appeeky.com/docs/market-activity Live feed of App Store chart movements: new entries, rank changes, and exits A real-time feed of all significant App Store chart movements. Shows new entries, rank ups/downs (3+ positions), and apps that dropped out of the top 100. Sorted by magnitude of change. ``` GET /v1/market/activity?country=us&chart=top-free&genre=all&limit=25 ``` ## Query Parameters | Parameter | Type | Default | Description | | --------- | ------ | ---------- | -------------------------------------------------- | | `country` | string | `us` | ISO country code | | `chart` | string | `top-free` | Chart type: `top-free`, `top-paid`, `top-grossing` | | `genre` | string | `all` | Genre ID or `all` | | `limit` | number | `25` | Max activities (1-50) | ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "ok": true, "data": { "country": "us", "genreId": "all", "chartType": "top-free", "activities": [ { "type": "new_entry", "appId": "111222333", "appName": "Breakout App", "developer": "Hot Dev", "iconUrl": "https://...", "genreName": "Games", "chartType": "top-free", "currentRank": 3, "previousRank": null, "rankChange": 98, "timestamp": "2026-02-24" }, { "type": "rank_up", "appId": "123456789", "appName": "Climbing App", "developer": "Dev Co", "iconUrl": "https://...", "genreName": "Productivity", "chartType": "top-free", "currentRank": 8, "previousRank": 42, "rankChange": 34, "timestamp": "2026-02-24" }, { "type": "rank_down", "appId": "987654321", "appName": "Sliding App", "developer": "Other Dev", "iconUrl": "https://...", "genreName": "Entertainment", "chartType": "top-free", "currentRank": 78, "previousRank": 12, "rankChange": -66, "timestamp": "2026-02-24" }, { "type": "dropped_out", "appId": "444555666", "appName": "Gone App", "developer": "Former Dev", "iconUrl": "https://...", "genreName": "Social Networking", "chartType": "top-free", "currentRank": null, "previousRank": 95, "rankChange": -6, "timestamp": "2026-02-24" } ], "totalCount": 47 } } ``` ## Activity Types | Type | Description | | ------------- | ---------------------------------------------------------------------------- | | `new_entry` | App appeared in the top 100 for the first time (wasn't in previous snapshot) | | `rank_up` | App improved by 3+ positions | | `rank_down` | App dropped by 3+ positions | | `dropped_out` | App was in previous top 100 but is no longer | ## Credit Cost 2 credits per request. ## Use Cases * **Daily briefing**: Get a quick overview of what changed in the charts overnight * **Alert pipeline**: Feed activity data into Slack/Discord bots for real-time competitive alerts * **Trend analysis**: Identify patterns in chart volatility across categories * **Breakout spotting**: Catch new apps entering the top 100 before they go mainstream # Market Movers Source: https://docs.appeeky.com/docs/market-movers Top gainers, losers, new entries, and exits from App Store charts Identifies apps with the biggest rank changes between chart snapshots. Compares the latest snapshot with the previous one to surface top gainers, top losers, new entries, and apps that dropped out. ``` GET /v1/market/movers?country=us&chart=top-free&genre=all&limit=10 ``` ## Query Parameters | Parameter | Type | Default | Description | | --------- | ------ | ---------- | -------------------------------------------------- | | `country` | string | `us` | ISO country code | | `chart` | string | `top-free` | Chart type: `top-free`, `top-paid`, `top-grossing` | | `genre` | string | `all` | Genre ID (e.g. `6014` for Games) or `all` | | `limit` | number | `10` | Max results per section (1-25) | ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "ok": true, "data": { "chartType": "top-free", "country": "us", "genreId": "all", "period": { "current": "2026-02-24", "previous": "2026-02-23" }, "topGainers": [ { "appId": "123456789", "appName": "Rising App", "developer": "Developer Inc", "iconUrl": "https://...", "genreId": "6014", "genreName": "Games", "currentRank": 12, "previousRank": 67, "rankChange": 55, "rating": 4.7, "reviewCount": 12500 } ], "topLosers": [ { "appId": "987654321", "appName": "Declining App", "developer": "Other Dev", "iconUrl": "https://...", "genreId": "6016", "genreName": "Entertainment", "currentRank": 89, "previousRank": 15, "rankChange": -74, "rating": 3.2, "reviewCount": 8000 } ], "newEntries": [ { "appId": "111222333", "appName": "Brand New App", "developer": "New Dev", "iconUrl": "https://...", "genreId": "6007", "genreName": "Productivity", "currentRank": 5, "previousRank": null, "rankChange": 96, "rating": 4.9, "reviewCount": 500 } ], "droppedOut": [ { "appId": "444555666", "appName": "Gone App", "developer": "Former Dev", "iconUrl": "https://...", "genreId": "6005", "genreName": "Social Networking", "currentRank": 0, "previousRank": 95, "rankChange": -6, "rating": 2.8, "reviewCount": 3000 } ] } } ``` ## Rank Change Interpretation | `rankChange` | Meaning | | -------------------- | ------------------------------------------------------ | | Positive | App moved UP (e.g. +55 means went from rank 67 → 12) | | Negative | App moved DOWN (e.g. -74 means went from rank 15 → 89) | | `previousRank: null` | New entry to chart | | `currentRank: 0` | Dropped out of top 100 | ## Credit Cost 3 credits per request. ## How It Works Chart snapshots are taken every 6 hours (00:00, 06:00, 12:00, 18:00 UTC). This endpoint compares the two most recent snapshots to calculate rank changes. Data availability depends on snapshot history — at least two snapshots on different dates are needed to show results. ## Use Cases * **Competitive monitoring**: Track when competitors surge or fall in the charts * **Breakout detection**: Spot apps entering the top 100 for the first time * **Category trends**: Filter by genre to monitor specific verticals * **Market research**: Understand overall chart volatility and seasonality # MCP Server Source: https://docs.appeeky.com/docs/mcp Connect any LLM to real-time App Store & Google Play data using the Model Context Protocol The Appeeky MCP Server lets AI assistants like **Claude**, **ChatGPT**, **Cursor**, and any [MCP-compatible](https://modelcontextprotocol.io) client query App Store and Google Play intelligence data directly. * **Claude Desktop** connects via OAuth — just add the URL as a connector and sign in. * **All other clients** use an API key. Get yours from [appeeky.com → Settings](https://appeeky.com) or the [API Dashboard](https://dashboard.appeeky.com). ``` MCP Endpoint: https://mcp.appeeky.com ``` ## Connect Your Client Pick your AI client for a step-by-step setup guide. Every client connects to the same endpoint — Claude Desktop over OAuth, all others with your API key. Claude Desktop (OAuth) and Claude Code (CLI). One-click install or `.cursor/mcp.json`. `codex mcp add` + `~/.codex/config.toml`. GitHub Copilot agent mode via `mcp.json`. Cascade via `mcp_config.json`. Remote server in `opencode.json`. Context server in `settings.json`. ### Programmatic clients Prefer to call the server from code? Use the MCP SDK directly. ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}} import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; const transport = new StreamableHTTPClientTransport( new URL("https://mcp.appeeky.com/mcp"), { requestInit: { headers: { "Authorization": "Bearer apk_your_key_here" } } } ); const client = new Client({ name: "my-app", version: "1.0.0" }); await client.connect(transport); const result = await client.callTool({ name: "search_apps", arguments: { query: "fitness tracker", country: "us" } }); ``` ```python theme={"theme":{"light":"github-light","dark":"github-dark"}} from mcp import ClientSession from mcp.client.streamable_http import streamablehttp_client async with streamablehttp_client( "https://mcp.appeeky.com/mcp", headers={"Authorization": "Bearer apk_your_key_here"} ) as (read, write, _): async with ClientSession(read, write) as session: await session.initialize() result = await session.call_tool( "search_apps", arguments={"query": "fitness tracker", "country": "us"} ) print(result) ``` **Claude Desktop** uses OAuth (no API key required). All other clients use your API key via `Authorization: Bearer` header or `X-API-Key` header. Get your key from [appeeky.com → Settings](https://appeeky.com) or the [API Dashboard](https://dashboard.appeeky.com). ## Available Tools ### Apps | Tool | Description | | ---------------------- | ----------------------------------------------------------------------------------------- | | `search_apps` | Search Apple App Store or Google Play by keyword, app ID, or package name | | `get_app` | Get detailed metadata for a specific Apple or Google Play app | | `get_app_intelligence` | Full intelligence report: downloads/installs, listing data, screenshots, sentiment fields | | `get_app_similar` | Similar and competing apps | | `get_app_reviews` | Fetch Apple or Google Play reviews with pagination | | `get_country_rankings` | App rankings across supported countries | ### Screenshots | Tool | Description | | ---------------------------- | ---------------------------------------------------------------- | | `get_app_screenshots` | Get all screenshots for an Apple or Google Play app | | `get_competitor_screenshots` | Compare screenshots between an app and its competitors | | `get_category_screenshots` | Get screenshots for top apps in an Apple or Google Play category | ### Keywords | Tool | Description | | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `get_keyword_ranks` | Apple or Google Play apps ranking for a keyword with rich metadata (supports `countries`) | | `get_keyword_suggestions` | Apple autocomplete or Google Play suggest ideas from a seed term | | `get_keyword_metrics` | Volume/difficulty-style metrics (Google uses live Play search result counts today; supports `countries`; **10 req/min burst** per API key) | | `get_app_keywords` | Keywords an Apple or Google Play app ranks for | | `get_keyword_trends` | Historical rank data over time (7-90 days) | | `get_keyword_ranks_history` | Historical apps-per-day counts for a tracked keyword | | `compare_keywords` | Compare keyword overlap between two Apple apps or two Google Play apps | | `compare_keyword_cluster` | Your app + 1–5 competitors: lifecycle buckets + gap keywords | | `track_keyword` | Add Apple or Google keyword to daily tracking | | `get_trending_keywords` | Keywords with fastest-growing reach in stored Apple or Google rank data | ### Account | Tool | Description | | --------------- | ----------------------------------------------------------------------------------- | | `get_api_usage` | Plan, per-bucket credits used/remaining, reset date, per-endpoint usage (0 credits) | ### ASO (App Store Optimization) | Tool | Description | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `aso_full_audit` | Full ASO health audit for Apple or Google Play — score (0-100), breakdown, prioritized recommendations | | `aso_brief` | Apple-only: audit + opportunities + storefront score; optional `countries`, `intent_clusters` | | `aso_validate_metadata` | Validate title/subtitle/keywords against Apple or Google character limits | | `aso_suggest_metadata` | Apple-sourced metadata suggestions that include Apple and Google fields | | `aso_find_opportunities` | Apple-only: discover untapped keyword opportunities | | `aso_competitor_report` | Apple-only: deep ASO comparison between two apps with keyword gap analysis | | `localize_app_metadata` | Translate store metadata into up to 10 languages (async); optionally publish to App Store Connect. Returns a `jobId` | | `get_localization_job` | Poll status and per-locale results of a `localize_app_metadata` run | | `validate_idea` | Validate a plain-language app idea against the live App Store — competitors with revenue/download estimates, keyword demand vs difficulty, review pain points, an AI verdict, and a go-to-market starter pack (async). Returns a `jobId` | | `get_idea_validation_job` | Poll status and the full result of a `validate_idea` run | | `generate_app_ad_creative` | Generate a Meta-ready square app ad creative from a real App Store or Google Play listing (async). Supports branded, people/lifestyle, UGC testimonial, problem-solution, and clean mockup presets. Uses 1 API credit for analyze/BYOK or creative credits for platform image generation (`low` = 1, `medium` = 2, `high` = 5) | | `get_app_ad_creative_job` | Poll status and retrieve the generated image URL, Meta copy, key points, and final prompt | ### Discovery & Featured | Tool | Description | | ---------------------- | ------------------------------------------------------------------------------ | | `get_featured_apps` | Apple-only: App of the Day, Game of the Day, and curated editorial collections | | `get_categories` | List Apple App Store or Google Play categories | | `get_category_top` | Top Apple or Google Play apps by category and chart type | | `get_downloads_to_top` | Apple-only: estimated downloads to reach chart positions | | `get_new_releases` | Apple-only: recently released apps | | `discover` | Apple-only: curated discovery feed (new releases + new #1 apps) | | `get_new_number_1` | Apple-only: apps that recently hit #1 in their category | ### Market Intelligence | Tool | Description | | --------------------- | --------------------------------------------------------------------- | | `get_market_movers` | Top gainers, losers, new entries, and apps that dropped out of charts | | `get_market_activity` | Live feed of chart movements: new entries, rank ups/downs, exits | ### ASC Metrics (synced Sales & Trends) | Tool | Description | | -------------------------- | ----------------------------------------------------------------------------------------------------------- | | `asc_get_metrics` | Overview: totals, per-app breakdown, daily rows (2 credits) | | `asc_list_metrics_apps` | List app IDs with metrics data (2 credits) | | `asc_get_app_metrics` | App detail: daily series + country breakdown (2 credits) | | `asc_subscription_metrics` | MRR / ARR / ARPU / churn / trial conversion + daily series (2 credits) | | `asc_get_app_sources` | App Store impression/page-view breakdown by acquisition source, with per-source conversion rate (2 credits) | **Pro feature** — requires Indie plan or higher. ASC Metrics need your **App Store Connect account connected** in [appeeky.com → Settings → Integrations](https://appeeky.com). Data syncs daily. 2 credits per request. ### ASC Reviews (synced + write-through) | Tool | Description | | ---------------------------- | ------------------------------------------------------------------------------ | | `asc_search_reviews` | Search reviews with rich filters: rating, territory, hasResponse, full-text | | `asc_reviews_summary` | Aggregated stats: total, avg rating, distribution, response rate, by-territory | | `asc_respond_to_review` | Post or update a developer response (handles Apple's edit-by-DELETE flow) | | `asc_delete_review_response` | Delete a developer response | | `asc_refresh_review` | Force-refresh one review from Apple (e.g. after replying) | **Pro feature** — requires Indie plan or higher. Reviews data syncs daily; mutations write through to Apple immediately. ### App Store Connect (requires your ASC credentials) | Tool | Description | | ------------------------------------- | ------------------------------------------------------------------------------------------ | | `asc_credentials_status` | Whether App Store Connect is connected on your account | | `asc_trigger_sync` | Start an ASC data sync (sales, analytics, reviews, subscriptions) | | `asc_list_apps` | List apps in your App Store Connect account | | `asc_get_app` | Get app details by App Store Connect app ID | | `asc_list_app_versions` | List versions for an app | | `asc_create_app_version` | Create a new app version (version string + platform) | | `asc_update_app_version` | Patch attributes on an app version | | `asc_list_app_infos` | List app info resources for an app | | `asc_list_app_info_localizations` | List app info localizations (name, subtitle, privacy) | | `asc_create_app_info_localization` | Add a new App Information language | | `asc_update_app_info_localization` | Update app info localization fields | | `asc_delete_app_info_localization` | Remove an App Information language | | `asc_list_version_localizations` | List metadata localizations for a version | | `asc_get_version_localization` | Get one version localization by ID | | `asc_create_version_localization` | Add a new metadata language to a version | | `asc_update_localization` | Update description, keywords, whatsNew, etc. | | `asc_delete_version_localization` | Remove a metadata language from a version | | `asc_pull_localizations` | Pull live App Store metadata into Appeeky (bulk) | | `asc_publish_localization` | Publish translated metadata to the App Store (bulk) | | `asc_remove_localization` | Remove locales in bulk | | `asc_list_customer_reviews` | List customer reviews live from Apple (legacy — prefer `asc_search_reviews` for filtering) | | `asc_list_app_analytics_requests` | List analytics report requests | | `asc_create_analytics_request` | Create analytics report request | | `asc_list_reports_for_request` | List reports for a request | | `asc_list_analytics_report_instances` | List instances for an analytics report | | `asc_list_analytics_report_segments` | List segments for a report instance | | `asc_get_analytics_report_segment` | Get one segment (download metadata) | | `asc_download_analytics_segment` | Download and parse an analytics segment (gzip TSV → JSON) | | `asc_download_sales_report` | Download a Sales & Trends report (parsed JSON or raw) | ### ASC Release Management Ship a version end-to-end: screenshots, builds, review submission, phased rollout. See [Release Management](/docs/app-store-connect-release). | Tool | Description | | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | `asc_list_screenshot_sets` | List screenshot sets for a localization (per device type) | | `asc_create_screenshot_set` | Create a screenshot set for a display type | | `asc_list_screenshots` | List screenshots in a set | | `asc_upload_screenshot` | Upload a screenshot from a public URL (dimension-validated, full Apple 3-step upload) | | `asc_reorder_screenshots` | Reorder screenshots in a set | | `asc_delete_screenshot` | Delete a screenshot (confirm required) | | `asc_list_builds` | List builds with processing-state and version filters | | `asc_get_build` | Get one build | | `asc_get_version_build` | Get the build attached to a version | | `asc_attach_build` | Attach a build to a version | | `asc_detach_build` | Detach the build from a version (confirm required) | | `asc_get_review_detail` / `asc_create_review_detail` / `asc_update_review_detail` | App Review contact info, demo account, notes | | `asc_get_submission` | Open review submissions for the version's app | | `asc_submit_for_review` | Submit a version for App Review (current reviewSubmissions flow; confirm required) | | `asc_cancel_submission` | Cancel a review submission (confirm required) | | `asc_get_phased_release` / `asc_create_phased_release` | Read or start a 7-day phased rollout | | `asc_update_phased_release` | Pause / resume / complete the rollout | | `asc_delete_phased_release` | Remove phased release before rollout (confirm required) | ### ASC TestFlight Beta distribution management. See [TestFlight](/docs/app-store-connect-testflight). | Tool | Description | | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | | `asc_list_beta_groups` / `asc_create_beta_group` / `asc_delete_beta_group` | Manage beta groups (public link support) | | `asc_update_beta_group_membership` | Add/remove builds or testers in a group | | `asc_list_beta_testers` / `asc_create_beta_tester` / `asc_delete_beta_tester` | Manage testers | | `asc_invite_beta_tester` | Resend a tester invitation | | `asc_list_beta_app_localizations` / `asc_create_beta_app_localization` / `asc_update_beta_app_localization` | TestFlight Test Information per locale | | `asc_list_beta_build_localizations` / `asc_create_beta_build_localization` / `asc_update_beta_build_localization` / `asc_delete_beta_build_localization` | "What to Test" notes per build | | `asc_get_beta_review_detail` / `asc_update_beta_review_detail` | Beta review contact and demo account | | `asc_get_build_beta_detail` / `asc_update_build_beta_detail` | Build TestFlight state and auto-notify | | `asc_get_beta_review_submission` / `asc_submit_beta_review` | External beta review status / submit | | `asc_cancel_beta_review` | Withdraw from beta review by expiring the build (confirm required) | ### ASC Monetization & Pricing IAPs, subscriptions, price schedules, availability. See [Monetization & Pricing](/docs/app-store-connect-monetization). | Tool | Description | | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | | `asc_list_iaps` / `asc_get_iap` / `asc_create_iap` / `asc_update_iap` | In-app purchase products (v2) | | `asc_list_iap_localizations` / `asc_create_iap_localization` / `asc_update_iap_localization` | IAP display names and descriptions | | `asc_get_iap_price_schedule` / `asc_list_iap_price_points` / `asc_replace_iap_price_schedule` | IAP pricing (replace requires confirm) | | `asc_list_subscription_groups` / `asc_create_subscription_group` | Subscription groups | | `asc_list_subscriptions` / `asc_get_subscription` / `asc_create_subscription` / `asc_update_subscription` | Auto-renewable subscriptions | | `asc_list_subscription_localizations` / `asc_create_subscription_localization` / `asc_update_subscription_localization` | Subscription localizations | | `asc_list_subscription_price_points` / `asc_create_subscription_price` | Subscription pricing (supports grandfathering) | | `asc_get_app_availability` / `asc_update_app_availability` / `asc_update_territory_availability` | Territory availability | | `asc_get_app_price_schedule` / `asc_list_app_price_points` / `asc_replace_app_price_schedule` | App pricing | ASC tools work with your **connected App Store Connect account** (connect once in [appeeky.com → Settings → Integrations](https://appeeky.com)), or pass `issuer_id`, `key_id`, and `private_key` per call. See [App Store Connect Overview](/docs/app-store-connect-overview#authentication). Destructive tools require `confirm: true`. ### Google Play Console (requires your service account) Owned Google Play apps — separate from public `platform=google` ASO data. Connect once at `POST /v1/connect/google-play/credentials`, or pass optional per-call credentials (`service_account_json`, or `client_email` + `private_key`) to the MCP tools. | Tool | Description | | --------------------------------------------- | ------------------------------------------------------------------- | | `google_play_credentials_status` | Whether Google Play is connected on your account | | `google_play_list_apps` | List apps visible to the Play service account | | `google_play_list_reviews` | List owned-app Play reviews | | `google_play_get_review` | Get one review by ID | | `google_play_reply_review` | Reply to or update a review response | | `google_play_get_vitals_metric` | Get Android vitals metric metadata/freshness | | `google_play_query_vitals` | Query Android vitals metrics | | `google_play_list_anomalies` | List vitals anomalies | | `google_play_list_subscriptions` | List subscription products | | `google_play_list_one_time_products` | List one-time products | | `google_play_get_release_filter_options` | Release/version filters for Reporting API queries | | `google_play_list_tracks` | List release tracks | | `google_play_get_track` | Get one release track | | `google_play_list_track_releases` | List releases on a track | | `google_play_update_track` | Patch a track; supports `validate_only` | | `google_play_list_listings` | List store listing localizations | | `google_play_get_listing` | Get one localized store listing | | `google_play_update_listing` | Patch title/description/video; supports `validate_only` | | `google_play_list_report_objects` | List GCS report files for sales, earnings, stats, store performance | | `google_play_get_report_object` | Get one report object metadata | | `google_play_import_store_performance_report` | Import one GCS stats CSV into synced analytics data | | `google_play_import_reports` | Bulk-import GCS stats reports by prefix | | `google_play_analytics_overview` | Imported Play analytics overview across apps | | `google_play_app_analytics` | Imported Play analytics metrics for one app | | `google_play_sync_app_vitals` | Sync crash/ANR vitals into synced analytics data | | `google_play_app_analytics_sources` | Traffic source, country, and UTM breakdowns | | `google_play_app_search_terms` | Play Console search-term visitors, acquisitions, and conversion | For Play installs, ratings, crashes, search-term, and store-listing conversion reports, use `google_play_import_reports` after saving a `reportsBucket`, or discover files with `google_play_list_report_objects`. Report bucket names usually start with `pubsite_prod_`. ### Apple Search Ads Paid App Store search campaigns — **separate** from App Store Connect. Connect once at `POST /v1/connect/apple-ads/credentials`, then manage your org via REST or MCP with only your Appeeky API key. **Coverage:** credentials · campaigns & ad groups (list + pause/resume/budget) · targeting keywords (list, find, create, update, delete, recommendations, bid suggestions) · negative keywords (campaign + ad group CRUD) · keyword performance + search terms reports · Platform API v1 insights (search term popularity, impression share, keyword/phrase suggestions). See [Apple Search Ads](/docs/apple-search-ads) for full endpoint reference and [Insights & Popularity](/docs/apple-search-ads-insights) for Platform API v1 search popularity. | Tool | Description | | --------------------------------------- | -------------------------------------------------------------------- | | `asa_credentials_status` | Whether Search Ads is connected on your account | | `asa_list_campaigns` | List campaigns (IDs, names, status, budget) | | `asa_update_campaign` | Enable/pause campaign or update daily budget | | `asa_list_adgroups` | Ad groups inside a campaign | | `asa_update_adgroup` | Enable/pause ad group or update default bid | | `asa_list_targeting_keywords` | List targeting keywords in an ad group | | `asa_get_targeting_keyword` | Get one targeting keyword by ID | | `asa_find_targeting_keywords` | Find targeting keywords across a campaign | | `asa_create_targeting_keywords` | Bulk-create targeting keywords | | `asa_update_targeting_keywords` | Bulk-update keyword bid / status | | `asa_delete_targeting_keywords` | Delete targeting keywords | | `asa_targeting_keyword_recommendations` | Apple keyword suggestions | | `asa_bid_recommendations` | Suggested bids for keyword texts | | `asa_profitability` | Join Apple Ads spend with RevenueCat revenue, profit, and ROAS | | `asa_playbook_status` | Integration readiness for ROAS analysis | | `asa_admaxxing_recommendations` | Scale/pause recommendations, attribution sample, review country gate | | `asa_review_country_gate` | App Store rating vs active campaign countries | | `asa_list_campaign_negative_keywords` | List campaign-level negative keywords | | `asa_find_campaign_negative_keywords` | Find campaign negative keywords | | `asa_create_campaign_negative_keywords` | Create campaign negative keywords | | `asa_update_campaign_negative_keywords` | Update campaign negative keywords | | `asa_delete_campaign_negative_keywords` | Delete campaign negative keywords | | `asa_list_adgroup_negative_keywords` | List ad group negative keywords | | `asa_find_adgroup_negative_keywords` | Find ad group negative keywords | | `asa_create_adgroup_negative_keywords` | Create ad group negative keywords | | `asa_update_adgroup_negative_keywords` | Update ad group negative keywords | | `asa_delete_adgroup_negative_keywords` | Delete ad group negative keywords | | `asa_report_keywords` | Keyword performance: impressions, taps, installs, spend | | `asa_report_search_terms` | Real search queries users typed before tapping your ad | | `asa_search_term_popularity` | Official Apple popularity ranking by country + genre | | `asa_impression_share` | Impression share, rank, and popularity for your advertised app | | `asa_keyword_suggestions` | Keyword ideas for an app with 0–100 popularity scores | | `asa_phrase_popularity` | Brand/business phrase catalog, or phrase ideas for an advertised app | **Indie plan or higher.** Requires an [Apple Search Ads](https://searchads.apple.com) API key (not your ASC key). Optional per-call credentials: `client_id`, `team_id`, `key_id`, `org_id`, `private_key`. ### AI Visibility (LLM mind-share tracking) How often your app is recommended by AI assistants — ChatGPT, Claude, Gemini, and Perplexity — for the intents users actually search for, partitioned per country and per model. | Tool | Description | | ------------------------------------------------------ | --------------------------------------------------------------------------------------- | | `ai_visibility_overview` | Composite score, sentiment, intent coverage, and prior-period delta | | `ai_visibility_intents` | All tracked intents with per-intent visibility, sentiment, and average position | | `ai_visibility_intent_detail` | One intent + its prompts + the latest AI answers per prompt | | `ai_visibility_competitors` | Apps that AI recommends instead of (or alongside) yours over the last N days | | `ai_visibility_trend` | Daily time series — app-level or scoped to a single intent | | `ai_visibility_answer` | Verbatim raw model output, extracted app mentions in rank order, citations | | `ai_visibility_runs` | Recent scan runs (manual, scheduled, bootstrap) with status and answer counts | | `ai_visibility_run_detail` | One scan run plus stored prompt-answer excerpts (`answerId` for `ai_visibility_answer`) | | `ai_visibility_settings_get` | Current config + tier policy + AI Visibility credit budget | | `ai_visibility_settings_update` | Toggle enabled/models/cadence (tier-capped, 0 credits) | | `ai_visibility_intent_create` / `_update` / `_archive` | Manually manage tracked intents (0 credits) | | `ai_visibility_prompt_create` / `_update` / `_archive` | Manually manage prompts under an intent (0 credits) | | `ai_visibility_bootstrap` | Auto-generate intents+prompts from App Store metadata, then run an initial scan | | `ai_visibility_scan` | Run an on-demand scan across every active prompt × enabled model (dynamic credits) | **Indie plan or higher.** AI Visibility uses a separate credit pool from API credits — see [AI Visibility Overview](/docs/ai-visibility-overview). Bootstrap and Scan are fire-and-forget queued tasks; they return a `taskRunId` immediately and write results back as scans finish. ### Growth Channels (Reddit lead generation) Scan subreddits for high-intent posts, score buying intent with AI, draft replies, and schedule/publish Reddit posts via Social Media. | Tool | Description | | ---------------------------------------------------------------- | ---------------------------------------------------------------- | | `growth_channels_list` | Channel registry + Reddit connect status | | `growth_reddit_connect_status` | Whether Reddit is connected for posting/scans | | `growth_reddit_connect_start` | Start Reddit OAuth (`authorizeUrl`) via Social Media | | `growth_reddit_disconnect` | Disconnect Reddit | | `growth_reddit_subscriptions` | User's subscribed subreddits | | `growth_projects_list` / `_get` | List or fetch Growth projects | | `growth_projects_bootstrap` | Suggest subreddits + config from an app (3 API credits) | | `growth_projects_create` / `_update` | Create or patch a project | | `growth_projects_scan` | Queue a manual inbox scan (once per 24h per project) | | `growth_projects_stats` | Funnel + budget stats | | `growth_opportunities_list` / `_get` | Reddit inbox (filter by status, urgency, min score) | | `growth_opportunities_review` | Approve or dismiss | | `growth_opportunities_regenerate_draft` | LLM-generate reply draft | | `growth_opportunities_post_reply` | Schedule a reply on the calendar (inbox replies stay copy-paste) | | `growth_opportunities_mark_replied` | Mark manually posted (paste comment URL) | | `growth_scheduled_posts_list` / `_create` / `_cancel` / `_retry` | Standalone scheduled Reddit posts | **Available to all signed-in users.** See [Growth Channels overview](/docs/growth-channels) and [Reddit Growth](/docs/growth-channels-reddit). Connect Reddit (`growth_reddit_connect_start`) before publishing scheduled posts. Inbox replies are copy-paste. Most tools cost 0 API credits; bootstrap costs 3. New accounts should follow [Reddit account warmup](/docs/reddit-account-warmup) before standalone scheduled posts. ### SEO (web rank tracking, research, link building) Track a website and its store listing in Google, research keywords, diff yourself against competitors, and run backlink outreach from your own mailbox. Everything is scoped to an SEO project, so start with `seo_projects_list`. | Tool | Description | | ---------------------------------------------------------- | -------------------------------------------------------------------------- | | `seo_projects_list` / `_get` | List or fetch SEO projects | | `seo_projects_create` / `_update` / `_delete` | Manage projects (website URL, store URL, market, budget) | | `seo_projects_scan` | Queue a rank scan (own key: on demand; shared key: every 3 days) | | `seo_projects_stats` | Visibility, position bands, AI Overview citations, outreach funnel, budget | | `seo_visibility_trend` | Daily average position and top-3/10/100 counts | | `seo_keyword_movers` | Biggest gains and drops over a window | | `seo_keywords_list` / `_add` / `_remove` | Manage tracked keywords (filter by position band) | | `seo_keyword_history` | Every rank check for one keyword, with the SERP at each check | | `seo_keyword_live_check` | Buy a fresh SERP for one keyword now (provider credits) | | `seo_keyword_ideas` | Expand seeds into related keywords (provider credits) | | `seo_keyword_metrics` | Volume, difficulty, CPC, intent, seasonality for an exact list | | `seo_competitors_list` / `_add` / `_remove` | Manage competitor domains | | `seo_competitors_analyze` | Pull a competitor's keywords and diff against yours (provider credits) | | `seo_gaps_list` / `_track` | Keyword gap report, and promote gaps into tracked keywords | | `seo_opportunities_list` / `_add` / `_review` | Link prospects with type, relevance, and contact | | `seo_opportunities_discover` | Mine tracked SERPs for pages worth pitching (0 provider credits) | | `seo_opportunities_draft_pitch` | LLM-write a personalized pitch as a draft thread | | `seo_threads_list` / `_get` / `_update` | Outreach conversations and draft editing | | `seo_threads_queue` / `_send` / `_stop` / `_won` / `_lost` | Move a thread through the outreach funnel | | `seo_backlinks_list` / `_add` / `_verify` / `_remove` | Monitor earned links for nofollow or removal | | `seo_provider_status` | Whether a DataForSEO key is connected, and its balance | | `seo_provider_connect` | Verify and save the user's DataForSEO login + API password | | `seo_mailboxes_list` | Connected sending mailboxes and their daily limits | **Available to all signed-in users.** Every SEO read comes from DataForSEO. Rank scans on the shared platform key run every 3 days; connect the user's own key with `seo_provider_connect` to scan on demand. If `seo_provider_status` reports `configured: false`, call `seo_provider_connect` with the user's DataForSEO login and API password (from their DataForSEO dashboard, not their account password), then `seo_projects_scan`. Until a key is saved, rank scans skip without writing positions. Set a per-project daily budget so scans fall back to pooled results instead of overspending. `seo_threads_send` emails a real person: only call it on a message the user approved. ### RevenueCat (your credentials: `X-RC-Key` / `X-RC-Project`) | Tool | Description | | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | `rc_overview` | Snapshot: MRR, actives, trials, revenue, etc. | | `rc_chart_options` | Valid filters (countries, products, …) for a chart name | | `rc_revenue`, `rc_mrr`, `rc_active_subscriptions`, `rc_churn` | Common chart shortcuts | | `rc_chart` | Generic chart by name | | `rc_attribution_summary` | Sample customers with Apple Search Ads attribution; aggregate by keyword, campaign, country | | `rc_customer_attributes` | Attribution and custom attributes for one RevenueCat customer | ### Superwall (saved organization API key) Uses the Superwall key stored in Appeeky Settings. Connect Superwall first, then call these tools. | Tool | Description | | ----------------- | ------------------------------------------------ | | `sw_apps` | List Superwall projects and applications | | `sw_overview` | Snapshot KPIs (proceeds, MRR, users, conversion) | | `sw_dashboard` | Bundled stats + KPI charts + recent transactions | | `sw_chart` | Time-series for a Superwall y-axis metric | | `sw_transactions` | Recent purchases, renewals, and trials | | `sw_campaigns` | Campaigns and placements for an application | ### Co-founder (action queue & workspace) Pending work the Co-founder proposed — ASC metadata, review replies, Linear/GitHub issues, Slack posts — plus the founder's workspace library. Start with `cofounder_actions_stats` or `cofounder_actions_list` (defaults to `proposed`). | Tool | Description | | -------------------------------------- | ----------------------------------------------- | | `cofounder_actions_stats` | Counts by status + failed (`last_error`) | | `cofounder_actions_list` | Pending inbox (`status=proposed` by default) | | `cofounder_actions_review` | Approve (executes now) or reject | | `workspace_search` / `_list` / `_read` | Search, list, or read workspace documents | | `workspace_write` / `_delete` | Create/overwrite or delete a workspace document | **Available to all signed-in users.** 0 API credits. `cofounder_actions_review` with `decision=approved` runs the connected integration immediately — only call it when the founder explicitly asked. Workspace writes to `business_context.md` and `AGENTS.md` require `confirm_owner_document: true`. ## Example Prompts Once connected, ask your AI assistant in natural language: **Market research:** ``` Show me the top 10 apps for "meditation" and analyze their ratings and review counts. ``` **Keyword intelligence:** ``` Find keyword suggestions for "screen recorder" and tell me which ones have high volume but low difficulty. ``` **Competitor analysis:** ``` Compare keyword overlap between Headspace (id: 493145008) and Calm (id: 571800810). What keywords is Calm ranking for that Headspace is missing? ``` **App intelligence:** ``` Give me the full intelligence report for Duolingo. How many downloads does it get? What's the revenue estimate? ``` **Featured apps:** ``` What apps are featured on the App Store today? What's the App of the Day and Game of the Day? ``` **Trending keywords:** ``` What keywords are trending on the App Store this week? Show me keywords with the highest growth in the US. ``` **Downloads to top:** ``` How many downloads does it take to reach the top 10 in the Games category? Show me the current apps at each rank. ``` **New releases:** ``` What new apps were released in the last 3 days? Are any of them in the Games category? ``` **ASO audit:** ``` Run a full ASO audit on Duolingo (id: 570060128). What's its score and what should they improve? ``` **ASO decision brief (orchestration):** ``` Give me an ASO decision brief for Brilliant (id: 913335252) in the US. Summarize prioritized actions and top keyword opportunities. ``` **ASO metadata optimization:** ``` I want to optimize my app (id: 913335252) for the keywords "learn math", "science education", and "brain training". Suggest optimized metadata for Apple and Google. ``` **ASC Metrics (synced Sales & Trends):** ``` What's my app's revenue and downloads for the last 30 days? Use asc_get_metrics or asc_get_app_metrics. ``` **Superwall (saved organization API key):** ``` What's my Superwall paywall proceeds and MRR for the last 30 days? Use sw_overview or sw_dashboard. ``` **Keyword opportunities:** ``` Find keyword opportunities for Headspace (id: 493145008). Which keywords should they target next? ``` **Market movers:** ``` Which apps gained the most ranks in the top-free chart today? Show me the top gainers and any new entries in Games. ``` **Market activity:** ``` What's happening in the App Store right now? Show me the latest chart activity for the US free apps chart. ``` **ASO competitor analysis:** ``` Run a full ASO competitor report comparing Brilliant (id: 913335252) vs Khan Academy (id: 1157115554). Where is Brilliant falling behind? ``` **App Store Connect reviews (synced + write-through):** ``` Find every unanswered 1- or 2-star review for app 6759740679 from the last two weeks that mentions "crash" or "bug", and reply with an empathetic message that points to support@example.com. Then show me the updated unanswered count. ``` **Subscription health (MRR / Churn):** ``` Pull subscription metrics for the last 90 days. Tell me whether MRR growth is being driven by new subscribers, lower churn, or higher trial conversion — and which app contributes most to MRR. ``` **Apple Search Ads — real search queries:** ``` List my Search Ads campaigns, then pull the search terms report for the main campaign over the last 30 days. Which queries have high impressions but few installs? Suggest ASO keywords to add. ``` **Apple Search Ads — keyword efficiency:** ``` For campaign 2143596801, show keyword performance for the last month. Which keywords have the best tap-through rate and lowest cost per install? ``` **Apple Search Ads — official popularity:** ``` Show the top US Productivity search terms by Apple popularity this week, then suggest keywords for my app with popularity above 60. ``` **AI Visibility — how often does AI recommend my app?** ``` For app 6759740679 in the US, give me my AI Visibility on ChatGPT: overall score, the 3 lowest-visibility intents, and the top 5 apps ChatGPT recommends instead of mine. ``` **AI Visibility — see exactly what the model said:** ``` For app 6759740679, find the worst-performing intent on ChatGPT, pick its top prompt, and show me the verbatim ChatGPT response (including which competitor apps it ranked above mine). ``` **AI Visibility — track a new intent manually:** ``` Add a new AI Visibility intent for app 6759740679 in the US: "Find a privacy-respecting note-taking app for students". Then add 3 prompts a real user would type for it (mix of unbranded and comparison styles), and run a scan. ``` **Co-founder — pending action items:** ``` Are there any pending Co-founder action items? If so, list them with type, app, and rationale. ``` ## Tool Reference ### search\_apps Search the App Store by keyword or numeric App ID. | Parameter | Type | Default | Description | | --------- | ------ | -------- | ------------------------ | | `query` | string | required | Search keyword or App ID | | `country` | string | `us` | ISO country code | | `limit` | number | `20` | Max results (1-50) | ### get\_app Get metadata for a specific app. | Parameter | Type | Default | Description | | ---------- | ------ | -------- | -------------------------------------------------- | | `app_id` | string | required | Apple App ID (numeric) or Google Play package name | | `platform` | string | `apple` | `apple` or `google` | | `country` | string | `us` | ISO country code | | `lang` | string | `en` | Google Play language code when `platform=google` | ### get\_app\_intelligence Full intelligence report with downloads, revenue, and sentiment. | Parameter | Type | Default | Description | | ---------- | ------ | -------- | ------------------------------------------------ | | `app_id` | string | required | Apple App ID or Google Play package name | | `platform` | string | `apple` | `apple` or `google` | | `country` | string | `us` | ISO country code | | `lang` | string | `en` | Google Play language code when `platform=google` | ### get\_app\_similar Similar and competing apps. Apple uses the 3-layer matching pipeline; Google uses public Play Store similar apps. | Parameter | Type | Default | Description | | ---------- | ------ | -------- | ------------------------------------------------ | | `app_id` | string | required | Apple App ID or Google Play package name | | `platform` | string | `apple` | `apple` or `google` | | `country` | string | `us` | ISO country code | | `lang` | string | `en` | Google Play language code when `platform=google` | ### get\_keyword\_ranks All apps ranking for a keyword with metadata (rating, reviews, category, price). | Parameter | Type | Default | Description | | ----------- | ------ | -------- | -------------------------------------------------- | | `keyword` | string | required | Keyword to search | | `platform` | string | `apple` | `apple` or `google` | | `country` | string | `us` | ISO country code | | `countries` | string | — | Comma-separated ISO codes for multi-country lookup | | `device` | string | `iphone` | Apple only: `iphone` or `ipad`; ignored for Google | | `lang` | string | `en` | Google Play language code when `platform=google` | ### get\_keyword\_suggestions Keyword ideas from a seed term with optional volume/difficulty metrics. | Parameter | Type | Default | Description | | ---------- | ------- | -------- | ------------------------------------------------ | | `term` | string | required | Seed term | | `platform` | string | `apple` | `apple` or `google` | | `country` | string | `us` | ISO country code | | `lang` | string | `en` | Google Play language code when `platform=google` | | `expand` | boolean | `false` | Long-tail expansion | | `metrics` | boolean | `true` | Include volume/difficulty | ### get\_keyword\_metrics Detailed metrics for a keyword. **Burst rate limit:** 10 requests per minute per API key (sliding window). This is separate from monthly credits. HTTP `429` responses include a `Retry-After` header (seconds). **Best practices for agents:** * Batch storefronts: `countries=us,gb,de` in **one** call instead of three separate calls. * Do not loop over large keyword lists sequentially — prioritize high-value terms and space calls out. * On `429`, wait for `Retry-After` then retry; use `get_keyword_suggestions` or `get_app_keywords` for broader discovery. | Parameter | Type | Default | Description | | ----------- | ------ | -------- | -------------------------------------------------------------------------------- | | `keyword` | string | required | Keyword to analyze | | `platform` | string | `apple` | `apple` or `google` | | `country` | string | `us` | ISO country code (ignored if `countries` is set) | | `lang` | string | `en` | Google Play language code when `platform=google` | | `countries` | string | — | Comma-separated ISO codes (e.g. `us,gb,de`) — one burst slot for all storefronts | ### get\_app\_keywords All keywords an app ranks for with trend data. | Parameter | Type | Default | Description | | -------------- | ------- | -------- | ------------------------------------------------ | | `app_id` | string | required | Apple App ID or Google Play package name | | `platform` | string | `apple` | `apple` or `google` | | `country` | string | `us` | ISO country code | | `lang` | string | `en` | Google Play language code when `platform=google` | | `include_weak` | boolean | `false` | Include ranks 21-50 | | `force` | boolean | `false` | Force rediscovery where supported | ### get\_keyword\_trends Historical rank data for a keyword + app pair. | Parameter | Type | Default | Description | | ---------- | ------ | -------- | ------------------------------------------------ | | `app_id` | string | required | Apple App ID or Google Play package name | | `platform` | string | `apple` | `apple` or `google` | | `keyword` | string | required | Keyword to track | | `country` | string | `us` | ISO country code | | `lang` | string | `en` | Google Play language code when `platform=google` | | `days` | number | `30` | History length (7-90) | ### compare\_keywords Compare keyword rankings between two Apple apps or two Google Play apps. | Parameter | Type | Default | Description | | --------------- | ------ | -------- | --------------------------------------------------- | | `app_id` | string | required | Your Apple App ID or Google Play package name | | `competitor_id` | string | required | Competitor Apple App ID or Google Play package name | | `platform` | string | `apple` | `apple` or `google` | | `country` | string | `us` | ISO country code | ### compare\_keyword\_cluster Multi-competitor keyword lifecycle for Apple or Google Play (your app + 1–5 competitors). | Parameter | Type | Default | Description | | ------------- | ------ | -------- | --------------------------------------------------------------------------- | | `app_id` | string | required | Your Apple App ID or Google Play package name | | `platform` | string | `apple` | `apple` or `google` | | `competitors` | string | required | Comma-separated competitor Apple App IDs or Google Play package names (1–5) | | `country` | string | `us` | ISO country code | ### get\_keyword\_ranks\_history Historical apps-per-day counts for a tracked keyword. | Parameter | Type | Default | Description | | ---------- | ------ | -------- | -------------------------------------------------- | | `keyword` | string | required | Keyword | | `platform` | string | `apple` | `apple` or `google` | | `country` | string | `us` | ISO country code | | `device` | string | `iphone` | Apple only: `iphone` or `ipad`; ignored for Google | | `days` | number | `14` | Lookback (1–90) | ### get\_api\_usage Current billing period usage across API, AI Visibility, and creative buckets (0 credits). | Parameter | Type | Default | Description | | --------- | ---- | ------- | ----------- | | *(none)* | — | — | — | ### get\_categories List Apple App Store or Google Play categories. | Parameter | Type | Default | Description | | ---------- | ------ | ------- | ------------------- | | `platform` | string | `apple` | `apple` or `google` | ### get\_category\_top Top apps in a specific category or all categories. | Parameter | Type | Default | Description | | ---------- | ------ | ---------- | ------------------------------------------------------------------------- | | `genre_id` | string | `all` | Apple genre ID (e.g. `6014`) or Google category ID (e.g. `TOOLS`, `GAME`) | | `platform` | string | `apple` | `apple` or `google` | | `country` | string | `us` | ISO country code | | `lang` | string | `en` | Google Play language code when `platform=google` | | `chart` | string | `top-free` | `top-free`, `top-paid`, or `top-grossing` | | `limit` | number | `25` | Max results (1-100) | ### get\_downloads\_to\_top Apple-only. Estimated daily downloads needed to reach specific chart positions (#1, #5, #10, #25, #50, #100). | Parameter | Type | Default | Description | | ---------- | ------ | ---------- | ----------------------------------------- | | `genre_id` | string | `all` | Genre ID (e.g. `6014` for Games) | | `country` | string | `us` | ISO country code | | `chart` | string | `top-free` | `top-free`, `top-paid`, or `top-grossing` | ### get\_trending\_keywords Keywords with the fastest-growing reach in Apple or Google stored rank data. | Parameter | Type | Default | Description | | ---------- | ------ | ------- | ------------------------------ | | `platform` | string | `apple` | `apple` or `google` | | `country` | string | `us` | ISO country code | | `days` | number | `7` | Trend window in days (1-30) | | `limit` | number | `50` | Max keywords to return (1-100) | ### get\_featured\_apps Apps currently featured on the App Store Today tab. | Parameter | Type | Default | Description | | --------- | ------ | ------- | ---------------- | | `country` | string | `us` | ISO country code | ### get\_new\_releases Recently released apps. | Parameter | Type | Default | Description | | ---------- | ------ | ------- | ------------------------------ | | `country` | string | `us` | ISO country code | | `max_days` | number | `7` | Show releases from last N days | ### aso\_full\_audit Full ASO health audit with scoring, breakdown, and recommendations. | Parameter | Type | Default | Description | | ---------- | ------ | -------- | ------------------------------------------------ | | `app_id` | string | required | Apple App ID or Google Play package name | | `platform` | string | `apple` | `apple` or `google` | | `country` | string | `us` | ISO country code | | `lang` | string | `en` | Google Play language code when `platform=google` | ### aso\_validate\_metadata Validate metadata against Apple or Google character limits. | Parameter | Type | Default | Description | | ------------------- | ------ | -------- | ------------------------------------- | | `platform` | string | required | `apple` or `google` | | `title` | string | optional | App title | | `subtitle` | string | optional | Apple subtitle | | `keywords` | string | optional | Apple keyword field (comma-separated) | | `short_description` | string | optional | Google short description | | `full_description` | string | optional | Google full description | ### aso\_suggest\_metadata Generate optimized metadata from target keywords. | Parameter | Type | Default | Description | | ---------- | --------- | -------- | --------------------- | | `app_id` | string | required | Apple App ID | | `keywords` | string\[] | required | Target keywords array | | `country` | string | `us` | ISO country code | ### aso\_find\_opportunities Discover untapped keyword opportunities. | Parameter | Type | Default | Description | | --------- | ------ | -------- | ---------------- | | `app_id` | string | required | Apple App ID | | `country` | string | `us` | ISO country code | ### aso\_brief Orchestrated ASO brief: audit + opportunities, storefront readiness score, optional intent clusters, optional multi-country. | Parameter | Type | Default | Description | | ----------------- | ------- | -------- | ------------------------------------------------ | | `app_id` | string | required | Apple App ID | | `country` | string | `us` | ISO country code (ignored if `countries` set) | | `countries` | string | optional | Comma-separated ISO codes for per-country briefs | | `fresh` | boolean | `false` | Force fresh re-analysis (slower) | | `intent_clusters` | boolean | `false` | Semantic intent clustering (+2 credits) | ### aso\_competitor\_report Deep ASO comparison between two apps. | Parameter | Type | Default | Description | | --------------- | ------ | -------- | ---------------- | | `app_id` | string | required | Your app's ID | | `competitor_id` | string | required | Competitor's ID | | `country` | string | `us` | ISO country code | ### get\_app\_screenshots Get all screenshots for an Apple or Google Play app. | Parameter | Type | Default | Description | | ---------- | ------ | -------- | ------------------------------------------------------------------------------------------- | | `app_id` | string | required | Apple App ID or Google Play package name | | `platform` | string | `apple` | `apple` or `google` | | `country` | string | `us` | ISO country code | | `lang` | string | `en` | Google Play language code when `platform=google` | | `device` | string | `all` | Apple device filter: `iphone`, `ipad`, or `all`; Google returns android/phone/tablet groups | ### get\_competitor\_screenshots Compare screenshots between an app and its competitors. | Parameter | Type | Default | Description | | ---------- | ------ | -------- | ------------------------------------------------ | | `app_id` | string | required | Apple App ID or Google Play package name | | `platform` | string | `apple` | `apple` or `google` | | `country` | string | `us` | ISO country code | | `lang` | string | `en` | Google Play language code when `platform=google` | | `limit` | number | `5` | Number of competitor apps to include (1-10) | ### get\_category\_screenshots Get screenshots for top-ranked apps in an Apple App Store or Google Play category. | Parameter | Type | Default | Description | | ---------- | ------ | ---------- | ----------------------------------------------------------------------------------- | | `genre_id` | string | `all` | Apple genre ID (e.g. `6014`) or Google category ID (e.g. `TOOLS`, `GAME`), or `all` | | `platform` | string | `apple` | `apple` or `google` | | `country` | string | `us` | ISO country code | | `lang` | string | `en` | Google Play language code when `platform=google` | | `chart` | string | `top-free` | `top-free`, `top-paid`, or `top-grossing` | | `limit` | number | `10` | Number of apps (1-25) | ### asc\_list\_customer\_reviews List customer reviews for your app from App Store Connect. Requires ASC credentials. | Parameter | Type | Default | Description | | ------------- | ------ | -------- | ---------------------------------------- | | `issuer_id` | string | required | App Store Connect Issuer ID | | `key_id` | string | required | App Store Connect Key ID | | `private_key` | string | required | App Store Connect private key (PEM) | | `app_id` | string | required | App Store Connect app ID | | `limit` | number | `50` | Max results per page (1-200) | | `cursor` | string | optional | Pagination cursor from previous response | ### asc\_respond\_to\_review Create or update your response to a single customer review. Transparently handles Apple's edit-by-DELETE-then-POST flow — you always make a single tool call. | Parameter | Type | Default | Description | | --------------- | ------ | -------- | ----------------------------------------------------------------------------- | | `issuer_id` | string | optional | App Store Connect Issuer ID (omit if ASC connected on your Appeeky account) | | `key_id` | string | optional | App Store Connect Key ID | | `private_key` | string | optional | App Store Connect private key (PEM) | | `review_id` | string | required | Customer review ID (from `asc_search_reviews` or `asc_list_customer_reviews`) | | `response_body` | string | required | Your response text (max 5970 chars — Apple's hard limit) | ### asc\_search\_reviews Search persisted customer reviews with rich filters. Reads from a synced copy of your App Store Connect reviews — much faster than `asc_list_customer_reviews` and supports filtering Apple's API does not. | Parameter | Type | Default | Description | | -------------- | ------- | -------- | ------------------------------------------------------------------------ | | `app_id` | string | optional | App Store Connect app ID — omit to search across all your connected apps | | `rating` | number | optional | Filter by exact star rating (1–5) | | `territory` | string | optional | ISO 3-letter country code (e.g. `USA`, `GBR`, `DEU`) | | `has_response` | boolean | optional | `true` for answered, `false` for unanswered, omit for both | | `q` | string | optional | Case-insensitive substring match in title or body | | `limit` | number | `50` | Max results (1–200) | | `before` | string | optional | ISO timestamp cursor — returns reviews created strictly before this date | ### asc\_reviews\_summary Aggregated review stats: total count, average rating, star distribution, response rate, top territories. | Parameter | Type | Default | Description | | --------- | ------ | -------- | ----------------------------------------------- | | `app_id` | string | optional | Filter by app — omit for portfolio-wide summary | ### asc\_delete\_review\_response Delete your developer response to a review (idempotent — calling on a review with no response succeeds with `response: null`). | Parameter | Type | Default | Description | | ------------- | ------ | -------- | --------------------------------------------------- | | `issuer_id` | string | optional | ASC Issuer ID (omit if connected on Appeeky) | | `key_id` | string | optional | ASC Key ID | | `private_key` | string | optional | ASC private key (PEM) | | `review_id` | string | required | Customer review ID whose response should be deleted | ### asc\_refresh\_review Force-refresh a single review from Apple, bypassing the daily sync. Useful right after responding to confirm the `PUBLISHED` state. | Parameter | Type | Default | Description | | ------------- | ------ | -------- | -------------------------------------------- | | `issuer_id` | string | optional | ASC Issuer ID (omit if connected on Appeeky) | | `key_id` | string | optional | ASC Key ID | | `private_key` | string | optional | ASC private key (PEM) | | `review_id` | string | required | Customer review ID to refresh | ### asc\_subscription\_metrics SaaS-grade subscription health: MRR, ARR, ARPU, churn rate, trial conversion rate, plus daily event counts (new, canceled, refunded, reactivated, trial starts/conversions, expired). Computed from Apple's SUBSCRIPTION + SUBSCRIPTION\_EVENT reports, FX-converted to USD using historical rates. | Parameter | Type | Default | Description | | ------------ | ------ | ----------- | --------------------------------------------------------------- | | `app_id` | string | optional | App Store Connect app ID — omit to roll up across all your apps | | `start_date` | string | 30 days ago | ISO date `YYYY-MM-DD` | | `end_date` | string | today | ISO date `YYYY-MM-DD` | ### asc\_get\_app\_sources App Store Discovery & Engagement breakdown by acquisition source (App Store Search, Browse, App Referrer, Web Referrer, etc.) for a single app — impressions, page views, and per-source conversion rate. | Parameter | Type | Default | Description | | --------- | ------ | ----------- | ---------------------------------- | | `app_id` | string | required | App Store Connect app ID (numeric) | | `from` | string | 30 days ago | ISO date `YYYY-MM-DD` | | `to` | string | today | ISO date `YYYY-MM-DD` | ### asa\_credentials\_status Check whether Apple Search Ads credentials are saved for your Appeeky account. | Parameter | Type | Default | Description | | --------- | ---- | ------- | ------------------------------ | | *(none)* | — | — | Uses your Appeeky API key only | ### asa\_list\_campaigns List Search Ads campaigns in your connected organization. | Parameter | Type | Default | Description | | ------------- | ------ | -------- | --------------------------------------------------- | | `client_id` | string | optional | Search Ads Client ID (omit if connected on Appeeky) | | `team_id` | string | optional | Team ID | | `key_id` | string | optional | Key ID | | `org_id` | string | optional | Org ID | | `private_key` | string | optional | EC private key PEM | | `limit` | number | `50` | Max campaigns (1–200) | | `offset` | number | `0` | Pagination offset | ### asa\_list\_adgroups List ad groups for a campaign. | Parameter | Type | Default | Description | | --------------------------- | ------ | -------- | ------------------------------------- | | `campaign_id` | string | required | Campaign ID from `asa_list_campaigns` | | `client_id` … `private_key` | string | optional | Same as above — omit if connected | | `limit` | number | `50` | Max rows (1–200) | | `offset` | number | `0` | Pagination offset | ### asa\_update\_campaign Enable, pause, rename, or set daily budget on a campaign. | Parameter | Type | Default | Description | | --------------------------- | ------------------------- | -------- | ---------------------------------------------- | | `campaign_id` | string | required | Campaign ID | | `status` | `"ENABLED"` \| `"PAUSED"` | optional | User-controlled status | | `name` | string | optional | New name | | `daily_budget_amount` | string | optional | e.g. `2.00` (requires `daily_budget_currency`) | | `daily_budget_currency` | string | optional | e.g. `EUR` | | `client_id` … `private_key` | string | optional | Per-call credentials | ### asa\_update\_adgroup Enable, pause, rename, or set default bid on an ad group. | Parameter | Type | Default | Description | | --------------------------- | ------------------------- | -------- | --------------------------------------------- | | `campaign_id` | string | required | Campaign ID | | `ad_group_id` | string | required | Ad group ID | | `status` | `"ENABLED"` \| `"PAUSED"` | optional | User-controlled status | | `name` | string | optional | New name | | `default_bid_amount` | string | optional | e.g. `0.56` (requires `default_bid_currency`) | | `default_bid_currency` | string | optional | e.g. `EUR` | | `client_id` … `private_key` | string | optional | Per-call credentials | ### asa\_report\_keywords Keyword-level performance report for a campaign. | Parameter | Type | Default | Description | | --------------------------- | ------ | -------- | ----------------------------------------------------- | | `campaign_id` | string | required | Campaign ID | | `from` | string | optional | Start date `YYYY-MM-DD` | | `to` | string | optional | End date `YYYY-MM-DD` | | `days` | number | optional | Trailing window when `from`/`to` omitted (default 30) | | `limit` | number | `50` | Max rows (1–200) | | `client_id` … `private_key` | string | optional | Per-call credentials | ### asa\_report\_search\_terms Search terms report — actual App Store queries that matched your ads. | Parameter | Type | Default | Description | | --------------------------- | ------ | -------- | ---------------------------- | | `campaign_id` | string | required | Campaign ID | | `from` | string | optional | Start date `YYYY-MM-DD` | | `to` | string | optional | End date `YYYY-MM-DD` | | `days` | number | optional | Trailing window (default 30) | | `limit` | number | `50` | Max rows (1–200) | | `client_id` … `private_key` | string | optional | Per-call credentials | ### asa\_search\_term\_popularity Official Apple search term popularity for a country + genre (Platform API v1). See [Insights & Popularity](/docs/apple-search-ads-insights). | Parameter | Type | Default | Description | | --------------------------- | --------------------------------- | ------------------ | ------------------------------------------------------- | | `genre` | string | required | App Store genre, e.g. `PRODUCTIVITY`, `TRAVEL`, `GAMES` | | `countries` | string\[] | `["US"]` | ISO country codes | | `terms` | string\[] | optional | Look up specific terms instead of the full ranking | | `from` / `to` | string | last complete week | `YYYY-MM-DD` | | `granularity` | `"WEEKLY_SUN_SAT"` \| `"MONTHLY"` | `WEEKLY_SUN_SAT` | Time bucket | | `limit` | number | `50` | Max rows (1–5000) | | `client_id` … `private_key` | string | optional | Per-call credentials | ### asa\_impression\_share Your advertised app's impression share, rank, and search popularity. `adam_id` must be in the connected Ads account. ### asa\_phrase\_popularity Brand/business phrase catalog (`SEARCH`) or phrase ideas for an advertised app (`SUGGESTION`). For App Store keyword scores, use `asa_search_term_popularity` with `terms`. ### asa\_keyword\_suggestions Keyword ideas for an advertised app with official 0–100 popularity scores. | Parameter | Type | Default | Description | | --------------------------- | --------- | -------- | -------------------- | | `adam_id` | string | required | App Store adamId | | `countries` | string\[] | `["US"]` | ISO country codes | | `terms` | string\[] | optional | Seed keywords | | `limit` | number | `20` | Max rows | | `client_id` … `private_key` | string | optional | Per-call credentials | ### asa\_profitability Apple Search Ads spend joined with RevenueCat revenue, profit, and ROAS. | Parameter | Type | Default | Description | | --------------------------- | ---------------------------------------------------------------------------- | --------- | --------------------------------------------------- | | `rc_key` | string | required | RevenueCat secret API key | | `rc_project` | string | optional | RevenueCat project ID | | `level` | `"keyword"` \| `"campaign"` \| `"adgroup"` \| `"search_term"` \| `"country"` | `keyword` | Rollup level | | `campaign_ids` | string\[] | optional | Campaign IDs to scan. Omit to scan first campaigns. | | `from` | string | optional | Start date `YYYY-MM-DD` | | `to` | string | optional | End date `YYYY-MM-DD` | | `days` | number | `14` | Trailing day window | | `limit` | number | `200` | Max Apple rows per campaign | | `campaign_limit` | number | `25` | Max campaigns when `campaign_ids` is omitted | | `country` | string | optional | ISO country code (e.g. `US`) | | `currency` | string | `USD` | RevenueCat currency | | `min_spend` | number | `20` | Spend threshold for insight buckets | | `insights` | boolean | `true` | Include optimization insights | | `client_id` … `private_key` | string | optional | Per-call Apple Search Ads credentials | Use this for questions like "what keywords are wasting spend?", "which keywords have the best ROAS?", and "what should I scale this week?". ### asa\_playbook\_status Check whether RevenueCat, Apple Search Ads, and Apple AdServices attribution are configured for ROAS analysis. | Parameter | Type | Default | Description | | ------------ | ------ | -------- | --------------------------------------------------------------- | | `rc_key` | string | optional | RevenueCat secret API key. Uses saved credentials when omitted. | | `rc_project` | string | optional | RevenueCat project ID | ### asa\_admaxxing\_recommendations Bundled optimization output: setup checklist, scale/pause candidates from profitability, RevenueCat attribution sample, and review country gate warnings. | Parameter | Type | Default | Description | | --------------------------- | ------------------------------------------ | --------- | ---------------------------------------- | | `rc_key` | string | required | RevenueCat secret API key | | `rc_project` | string | optional | RevenueCat project ID | | `app_id` | string | optional | App Store app ID for review country gate | | `level` | `"keyword"` \| `"campaign"` \| `"adgroup"` | `keyword` | Profitability rollup | | `country` | string | optional | ISO country filter | | `days` | number | `14` | Trailing window | | `min_spend` | number | `20` | Spend threshold for insights | | `min_rating` | number | `4.5` | Rating threshold for country warnings | | `client_id` … `private_key` | string | optional | Per-call Apple Search Ads credentials | See [ROAS Workflow](/docs/apple-search-ads-roas-workflow) for the full response shape. ### asa\_review\_country\_gate Compare App Store ratings by country against active Apple Search Ads campaign targeting. | Parameter | Type | Default | Description | | --------------------------- | ------ | -------- | -------------------------------------- | | `app_id` | string | required | App Store numeric app ID | | `min_rating` | number | `4.5` | Flag countries at or below this rating | | `client_id` … `private_key` | string | optional | Per-call Apple Search Ads credentials | ### rc\_attribution\_summary Sample RevenueCat customers and aggregate attributed revenue by media source, campaign, keyword, and country. | Parameter | Type | Default | Description | | ------------ | ------ | -------- | ------------------------------- | | `rc_key` | string | required | RevenueCat secret API key | | `rc_project` | string | optional | RevenueCat project ID | | `limit` | number | `50` | Max customers to sample (1–100) | See [RevenueCat Attribution](/docs/revenuecat-attribution). ### rc\_customer\_attributes Read all attributes for one RevenueCat customer, including parsed Apple Search Ads attribution fields. | Parameter | Type | Default | Description | | ------------- | ------ | -------- | ------------------------- | | `rc_key` | string | required | RevenueCat secret API key | | `rc_project` | string | optional | RevenueCat project ID | | `customer_id` | string | required | RevenueCat customer ID | ### sw\_apps List Superwall projects and applications for the connected organization. No parameters. Requires Superwall credentials saved in Appeeky. ### sw\_overview / sw\_dashboard | Parameter | Type | Default | Description | | ---------------- | ------ | -------------- | ------------------------------------------------------------------------ | | `application_id` | string | optional | Superwall application id. First listed app when omitted. | | `environment` | string | `PRODUCTION` | `PRODUCTION` or `SANDBOX` | | `date_preset` | string | `last_30_days` | Superwall date preset (`last_7_days`, `last_30_days`, `last_90_days`, …) | ### sw\_chart | Parameter | Type | Default | Description | | ---------------- | ------ | -------------- | ---------------------------------------------------------------------------------- | | `y_axis` | string | required | Metric: `netProceeds`, `mrr`, `newUsers`, `transactionCompletes`, `trialStarts`, … | | `application_id` | string | optional | Superwall application id | | `x_axis` | string | from `y_axis` | `purchaseDate`, `installDate`, or `mrrDate` | | `date_preset` | string | `last_30_days` | Superwall date preset | | `date_interval` | string | `day` | `hour`, `day`, `week`, or `month` | ### sw\_transactions | Parameter | Type | Default | Description | | ---------------- | ------ | -------------- | --------------------------- | | `application_id` | string | optional | Superwall application id | | `environment` | string | `PRODUCTION` | `PRODUCTION` or `SANDBOX` | | `date_preset` | string | `last_30_days` | Superwall date preset | | `event_type` | string | optional | Superwall event type filter | ### sw\_campaigns | Parameter | Type | Default | Description | | ---------------- | ------ | -------- | ------------------------ | | `application_id` | string | optional | Superwall application id | *** ## My Apps Tools Manage your [appeeky.com](https://appeeky.com) apps, competitors, tracked keywords, metadata versions, and reports directly from your AI assistant. All tools cost **0 credits** and require an API key generated from **appeeky.com → Settings → API Key**. These tools operate on your **appeeky.com** account data — the same data visible in the web dashboard. The key must have the `apps:read` scope for reads and `apps:write` for writes (unrestricted keys have both by default). ### list\_my\_apps List all apps in your appeeky.com tracked app list. | Parameter | Type | Default | Description | | --------- | ---- | ------- | ----------------------------- | | *(none)* | — | — | Returns all your tracked apps | ### add\_my\_app Add an app to your tracked list by its App Store ID. | Parameter | Type | Default | Description | | --------------- | --------- | -------- | ---------------------------------------- | | `appId` | number | required | Numeric Apple App Store ID | | `appName` | string | optional | Display name | | `developerName` | string | optional | Developer name | | `bundleId` | string | optional | Bundle identifier | | `subtitle` | string | optional | App subtitle | | `description` | string | optional | App description | | `languages` | string\[] | optional | Supported language codes | | `isLive` | boolean | optional | Whether the app is live on the App Store | | `category` | string | optional | App category | | `platform` | string | `ios` | `ios`, `macos`, or `tvos` | ### update\_my\_app Update metadata for an existing app in your tracked list. | Parameter | Type | Default | Description | | ------------- | --------- | -------- | ----------------------------------------- | | `appId` | number | required | Numeric App Store ID of the app to update | | `appName` | string | optional | New display name | | `isLive` | boolean | optional | Live status | | `category` | string | optional | App category | | `subtitle` | string | optional | App subtitle | | `description` | string | optional | App description | | `languages` | string\[] | optional | Supported language codes | ### delete\_my\_app Remove an app from your tracked list. | Parameter | Type | Default | Description | | --------- | ------ | -------- | ----------------------------------------- | | `appId` | number | required | Numeric App Store ID of the app to remove | *** ### list\_competitors List competitor apps tracked for one of your apps. | Parameter | Type | Default | Description | | --------- | ------ | -------- | ---------------------------------------- | | `appId` | number | required | Numeric App Store ID of your tracked app | ### add\_competitor Add a competitor app to one of your tracked apps. | Parameter | Type | Default | Description | | ----------------- | ------ | -------- | ------------------------------- | | `appId` | number | required | Your tracked app's App Store ID | | `competitorAppId` | number | required | Competitor's App Store ID | | `competitorName` | string | optional | Display name for the competitor | ### remove\_competitor Remove a competitor from one of your tracked apps. | Parameter | Type | Default | Description | | ----------------- | ------ | -------- | ----------------------------------- | | `appId` | number | required | Your tracked app's App Store ID | | `competitorAppId` | number | required | Competitor's App Store ID to remove | *** ### list\_tracked\_keywords List keywords tracked for one of your apps. | Parameter | Type | Default | Description | | --------- | ------ | -------- | ---------------------------------------- | | `appId` | number | required | Numeric App Store ID of your tracked app | ### add\_tracked\_keyword Start tracking a keyword for one of your apps. | Parameter | Type | Default | Description | | ---------- | ------ | -------- | ------------------------------------ | | `appId` | number | required | Your tracked app's App Store ID | | `keyword` | string | required | Keyword to track (e.g. `"aso tool"`) | | `country` | string | `us` | ISO country code | | `language` | string | `en` | ISO language code | ### remove\_tracked\_keyword Stop tracking a keyword. | Parameter | Type | Default | Description | | ----------- | ------ | -------- | -------------------------------------------------------------- | | `appId` | number | required | Your tracked app's App Store ID | | `keywordId` | string | required | UUID of the tracked keyword row (from `list_tracked_keywords`) | *** ### list\_app\_versions List saved metadata versions for one of your apps. | Parameter | Type | Default | Description | | --------- | ------ | -------- | ---------------------------------------- | | `appId` | number | required | Numeric App Store ID of your tracked app | ### create\_app\_version Save a new set of ASO metadata as a named version. | Parameter | Type | Default | Description | | ------------- | ------ | -------- | --------------------------------------------- | | `appId` | number | required | Your tracked app's App Store ID | | `versionName` | string | required | Label for this version (e.g. `"v2.1 Spring"`) | | `title` | string | optional | App title (max 30 chars) | | `subtitle` | string | optional | App subtitle (max 30 chars) | | `description` | string | optional | Full app description | | `keywords` | string | optional | Comma-separated keyword field | | `language` | string | `en-US` | Locale code | ### update\_app\_version Update fields on an existing metadata version. | Parameter | Type | Default | Description | | ------------- | ------ | -------- | ------------------------------- | | `appId` | number | required | Your tracked app's App Store ID | | `versionId` | string | required | UUID of the version to update | | `versionName` | string | optional | New label | | `title` | string | optional | Updated title | | `subtitle` | string | optional | Updated subtitle | | `description` | string | optional | Updated description | | `keywords` | string | optional | Updated keyword field | ### release\_app\_version Mark a metadata version as the currently released version. | Parameter | Type | Default | Description | | ----------- | ------ | -------- | --------------------------------------- | | `appId` | number | required | Your tracked app's App Store ID | | `versionId` | string | required | UUID of the version to mark as released | *** ### list\_reports List saved ASO reports for your account. | Parameter | Type | Default | Description | | --------- | ------ | -------- | ------------------------------------------------------ | | `appId` | number | optional | Filter by a specific App Store ID | | `type` | string | optional | Filter by report type (e.g. `aso_audit`, `competitor`) | | `limit` | number | `20` | Max results (max `100`) | | `offset` | number | `0` | Pagination offset | ### get\_report Retrieve the full content of a saved report. | Parameter | Type | Default | Description | | ---------- | ------ | -------- | ------------------ | | `reportId` | string | required | UUID of the report | ### delete\_report Permanently delete a saved report. | Parameter | Type | Default | Description | | ---------- | ------ | -------- | ---------------------------- | | `reportId` | string | required | UUID of the report to delete | *** ## AI Visibility Tools Track how often LLM assistants (ChatGPT, Claude, Gemini, Perplexity) recommend your app for the intents your users actually search for. Data is partitioned per `(app, country, model)` because the same prompt yields very different answers in different markets. **Indie plan or higher.** AI Visibility uses a separate credit pool from API credits. Read tools cost AI Visibility credits per call (cheap), `_scan` and `_bootstrap` cost dynamically based on prompt count × enabled models. See [AI Visibility Overview](/docs/ai-visibility-overview) for the credit model. ### ai\_visibility\_overview Top-line scorecard for one model: composite score (0-100), letter grade, sentiment %, intent coverage, and prior-period delta. | Parameter | Type | Default | Description | | --------- | ------ | --------- | -------------------------------------------------- | | `app_id` | number | required | Numeric Apple App Store ID | | `country` | string | `us` | ISO country code | | `model` | string | `chatgpt` | One of `chatgpt`, `claude`, `gemini`, `perplexity` | ### ai\_visibility\_intents List every tracked intent with its visibility, sentiment, average position, and top competitor apps. | Parameter | Type | Default | Description | | --------- | ------ | --------- | -------------------------------------------------- | | `app_id` | number | required | Numeric App Store ID | | `country` | string | `us` | ISO country code | | `model` | string | `chatgpt` | One of `chatgpt`, `claude`, `gemini`, `perplexity` | ### ai\_visibility\_intent\_detail Drill into one intent: returns its metrics plus all of its prompts and the latest AI answers per prompt. Use when you want to inspect "why is this intent under-performing?". | Parameter | Type | Default | Description | | ----------- | ------ | --------- | ------------------------------------------ | | `app_id` | number | required | Numeric App Store ID | | `intent_id` | string | required | Intent UUID (from `ai_visibility_intents`) | | `country` | string | `us` | ISO country code | | `model` | string | `chatgpt` | AI model | ### ai\_visibility\_competitors Apps that the model recommended instead of (or alongside) yours, ranked by appearances in the last N days. | Parameter | Type | Default | Description | | ------------- | ------ | --------- | ----------------------- | | `app_id` | number | required | Numeric App Store ID | | `country` | string | `us` | ISO country code | | `model` | string | `chatgpt` | AI model | | `window_days` | number | `14` | Lookback in days (1–90) | ### ai\_visibility\_trend Daily time series for one model — app-level by default, or scoped to a single intent when `intent_id` is set. | Parameter | Type | Default | Description | | ------------- | ------ | --------- | ------------------------------ | | `app_id` | number | required | Numeric App Store ID | | `country` | string | `us` | ISO country code | | `model` | string | `chatgpt` | AI model | | `window_days` | number | `30` | Trend window in days (1–90) | | `intent_id` | string | optional | Scope the series to one intent | ### ai\_visibility\_answer Fetch the verbatim raw model output for a single prompt, plus extracted app mentions in rank order, sentiment, and citations. The proof layer behind every visibility number. | Parameter | Type | Default | Description | | ----------- | ------ | -------- | ------------------------------------------------------------------------ | | `app_id` | number | required | Numeric App Store ID | | `answer_id` | string | required | Answer UUID (from `ai_visibility_intent_detail.prompts[].latestAnswers`) | ### ai\_visibility\_runs List recent AI Visibility scan runs for an app and country. Each run is one scan (manual, scheduled, or bootstrap) with status, models, prompt count, and how many answers succeeded or failed. | Parameter | Type | Default | Description | | --------- | ------ | -------- | -------------------------------------- | | `app_id` | number | required | Numeric App Store ID | | `country` | string | `us` | ISO country code | | `limit` | number | `30` | How many recent runs to return (1–100) | ### ai\_visibility\_run\_detail Fetch one scan run plus every prompt answer stored for it (excerpt, status, mention count, whether the owner app was named). Open a full verbatim response with `ai_visibility_answer` using `answerId`. | Parameter | Type | Default | Description | | --------- | ------ | -------- | ------------------------------------ | | `app_id` | number | required | Numeric App Store ID | | `run_id` | string | required | Run UUID (from `ai_visibility_runs`) | | `country` | string | `us` | ISO country code | ### ai\_visibility\_settings\_get Per-app config for one country: enabled state, models, scan cadence, last bootstrap/scan timestamps, your tier policy, and your AI Visibility credit budget. | Parameter | Type | Default | Description | | --------- | ------ | -------- | -------------------- | | `app_id` | number | required | Numeric App Store ID | | `country` | string | `us` | ISO country code | ### ai\_visibility\_settings\_update Toggle AI Visibility, change which models are scanned, or adjust scan cadence. Tier-capped: number of models cannot exceed `tier.maxModels`. Costs 0 credits. | Parameter | Type | Default | Description | | ------------------- | --------- | -------- | ----------------------------------------------------- | | `app_id` | number | required | Numeric App Store ID | | `country` | string | `us` | ISO country code | | `enabled` | boolean | optional | Enable/disable scheduled scans | | `models` | string\[] | optional | Subset of `chatgpt`, `claude`, `gemini`, `perplexity` | | `scan_cadence_days` | number | optional | Cadence in days (0–30; 0 pauses scheduled scans) | | `language` | string | optional | ISO language code (e.g. `en`, `tr`, `de`) | ### ai\_visibility\_intent\_create Manually add a user-intent to track. Source is recorded as `user`, so it sits alongside LLM-generated intents and is included in every scan. Costs 0 credits. | Parameter | Type | Default | Description | | ------------- | ------ | -------- | ---------------------------------------------------------------- | | `app_id` | number | required | Numeric App Store ID | | `country` | string | `us` | ISO country code | | `language` | string | `en` | ISO language code | | `label` | string | required | User-facing intent label (≥8 chars), phrased from the user's POV | | `description` | string | optional | One-sentence "who has this intent and what they want" | ### ai\_visibility\_intent\_update Rename, edit description, or change status (`active` / `paused` / `archived`). Pausing keeps history but stops including the intent in scheduled scans. Costs 0 credits. | Parameter | Type | Default | Description | | ------------- | ------ | -------- | --------------------------------- | | `app_id` | number | required | Numeric App Store ID | | `intent_id` | string | required | Intent UUID | | `label` | string | optional | New label | | `description` | string | optional | New description | | `status` | string | optional | `active`, `paused`, or `archived` | ### ai\_visibility\_intent\_archive Archive an intent (soft-delete). The intent and its prompts stop being scanned, but historical scores stay in `ai_visibility_trend`. Costs 0 credits. | Parameter | Type | Default | Description | | ----------- | ------ | -------- | ---------------------- | | `app_id` | number | required | Numeric App Store ID | | `intent_id` | string | required | Intent UUID to archive | ### ai\_visibility\_prompt\_create Add a manual prompt under an intent. Each prompt is sent verbatim to every enabled AI assistant on the next scan. Costs 0 credits. | Parameter | Type | Default | Description | | ----------- | ------ | ----------- | -------------------------------------------------------------- | | `app_id` | number | required | Numeric App Store ID | | `intent_id` | string | required | Parent intent UUID | | `country` | string | `us` | ISO country code | | `language` | string | `en` | ISO language code | | `text` | string | required | Prompt text (≥15 chars), in the user's voice | | `style` | string | `unbranded` | `branded`, `unbranded`, `problem`, `use_case`, or `comparison` | ### ai\_visibility\_prompt\_update Edit prompt text, style, or status (`active` / `paused` / `archived`). Pausing keeps history but excludes the prompt from upcoming scans. Costs 0 credits. | Parameter | Type | Default | Description | | ----------- | ------ | -------- | -------------------------------------------------------------- | | `app_id` | number | required | Numeric App Store ID | | `prompt_id` | string | required | Prompt UUID | | `text` | string | optional | New prompt text | | `style` | string | optional | `branded`, `unbranded`, `problem`, `use_case`, or `comparison` | | `status` | string | optional | `active`, `paused`, or `archived` | ### ai\_visibility\_prompt\_archive Archive a prompt (soft-delete). Historical answers remain queryable via `ai_visibility_answer`. Costs 0 credits. | Parameter | Type | Default | Description | | ----------- | ------ | -------- | ---------------------- | | `app_id` | number | required | Numeric App Store ID | | `prompt_id` | string | required | Prompt UUID to archive | ### ai\_visibility\_bootstrap Starts a background job that auto-generates intents and prompts for an app from its App Store metadata, then kicks off an initial scan. Run this once per `(app, country)` when first enabling AI Visibility. Returns a `taskRunId` you can poll later. Hard-fails on tier-block or zero AI Visibility credits. | Parameter | Type | Default | Description | | ---------- | ------ | -------- | -------------------- | | `app_id` | number | required | Numeric App Store ID | | `country` | string | `us` | ISO country code | | `language` | string | `en` | ISO language code | ### ai\_visibility\_scan Fire-and-forget scan: queries every active prompt against every enabled model and updates the app's metrics. Returns `taskRunId`, `promptsTotal`, `models[]`, and `estimatedCost` (AI Visibility credits). Hard-fails when projected cost exceeds remaining quota — call `ai_visibility_settings_get` first to check budget. | Parameter | Type | Default | Description | | --------- | ------ | -------- | -------------------- | | `app_id` | number | required | Numeric App Store ID | | `country` | string | `us` | ISO country code | # Claude Source: https://docs.appeeky.com/docs/mcp-claude Connect the Appeeky MCP server to Claude (Claude Desktop and Claude Code) and query live App Store & Google Play intelligence by chat. Connect Appeeky to Claude once, then pull real-time App Store and Google Play intelligence straight into your conversations. Pick your Claude below — Desktop connects over **OAuth**, Claude Code connects with your **API key**. ``` MCP endpoint (OAuth): https://mcp.appeeky.com MCP endpoint (API key): https://mcp.appeeky.com/mcp ``` **Claude Desktop** uses OAuth — no API key needed. **Claude Code** and every other client use an API key. Get yours from [appeeky.com → Settings → API Key](https://appeeky.com) or the [API Dashboard](https://dashboard.appeeky.com). ## Claude Desktop In Claude Desktop, go to [Settings → Connectors](https://claude.ai/customize/connectors), or click the **Claude Desktop** button in **appeeky.com → Settings → API Key**, which copies the URL and opens the page for you. Click **Add Connector** and paste the MCP URL: ``` https://mcp.appeeky.com ``` Claude redirects you to sign in with your Appeeky account (Google, GitHub, or email). Approve the prompt and the Appeeky tools become available in every conversation. ## Claude Code From your project root, register the server over HTTP with your API key: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} claude mcp add --transport http appeeky https://mcp.appeeky.com/mcp \ --header "Authorization: Bearer apk_your_key_here" ``` ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} claude mcp list ``` You should see `appeeky` listed as connected. ## Build with Appeeky Ask Claude in plain language and it calls the right Appeeky tools: ``` Show me the top 10 apps for "meditation" and analyze their ratings and review counts. ``` ``` Run a full ASO audit on Duolingo (id: 570060128). What's its score and what should they improve? ``` ``` Compare keyword overlap between Headspace (id: 493145008) and Calm (id: 571800810). What keywords is Calm ranking for that Headspace is missing? ``` See the full [tool reference](/docs/mcp#available-tools) for everything Appeeky exposes. ## Troubleshooting * **Tools missing or "not connected"** — re-add the connector (Desktop) or run `claude mcp list` to confirm the server registered (Claude Code). * **401 / "authentication required"** — your API key is missing or invalid. Regenerate it at [appeeky.com → Settings → API Key](https://appeeky.com) and update the `Authorization` header. Claude Desktop users should re-authorize the connector. * **429 / "rate limit"** — you hit a plan or burst limit. See [Rate Limits](/docs/rate-limits). # Codex Source: https://docs.appeeky.com/docs/mcp-codex Connect the Appeeky MCP server to Codex and query live App Store & Google Play intelligence by chat. Codex connects to Appeeky over **Streamable HTTP** at `https://mcp.appeeky.com/mcp`, authenticated with your API key. Get your API key from [appeeky.com → Settings → API Key](https://appeeky.com) or the [API Dashboard](https://dashboard.appeeky.com). ## Connect Appeeky Register the server with the Codex CLI: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} codex mcp add appeeky --transport http https://mcp.appeeky.com/mcp ``` Codex reads headers from `~/.codex/config.toml`. Add your key there: ```toml theme={"theme":{"light":"github-light","dark":"github-dark"}} [mcp_servers.appeeky.http_headers] Authorization = "Bearer apk_your_key_here" ``` ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} codex mcp list ``` You should see `appeeky` listed. ## Build with Appeeky Ask Codex in plain language and it calls the right Appeeky tools: ``` What apps are featured on the App Store today? What's the App of the Day and Game of the Day? ``` ``` Find keyword opportunities for Headspace (id: 493145008). Which keywords should they target next? ``` ``` What keywords are trending on the App Store this week? Show me keywords with the highest growth in the US. ``` See the full [tool reference](/docs/mcp#available-tools) for everything Appeeky exposes. ## Troubleshooting * **Tools missing or "not connected"** — run `codex mcp list` to confirm the server registered, then restart Codex. * **401 / "authentication required"** — check the `Authorization` header in `~/.codex/config.toml`. Regenerate your key at [appeeky.com → Settings → API Key](https://appeeky.com) if needed. * **429 / "rate limit"** — you hit a plan or burst limit. See [Rate Limits](/docs/rate-limits). # Cursor Source: https://docs.appeeky.com/docs/mcp-cursor Connect the Appeeky MCP server to Cursor and query live App Store & Google Play intelligence by chat. Cursor connects to Appeeky over **Streamable HTTP** at `https://mcp.appeeky.com/mcp`, authenticated with your API key. Get your API key from [appeeky.com → Settings → API Key](https://appeeky.com) or the [API Dashboard](https://dashboard.appeeky.com). ## Connect Appeeky In **appeeky.com → Settings → API Key**, click **Add to Cursor**. Cursor opens, shows the server pre-filled with your key, and asks you to confirm — approve it, then reload. Add Appeeky to `.cursor/mcp.json` in your project (or `~/.cursor/mcp.json` for all projects), then reload Cursor: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "mcpServers": { "appeeky": { "url": "https://mcp.appeeky.com/mcp", "headers": { "Authorization": "Bearer apk_your_key_here" } } } } ``` Open **Cursor Settings → MCP** and confirm `appeeky` is listed and its tools are enabled. ## Build with Appeeky Describe what you want in chat and Cursor calls the right Appeeky tools: ``` Find keyword suggestions for "screen recorder" and tell me which ones have high volume but low difficulty. ``` ``` Give me the full intelligence report for Duolingo. How many downloads does it get? What's the revenue estimate? ``` ``` Which apps gained the most ranks in the top-free chart today? Show me the top gainers and any new entries in Games. ``` See the full [tool reference](/docs/mcp#available-tools) for everything Appeeky exposes. ## Troubleshooting * **Tools missing, greyed out, or "not connected"** — reload Cursor after editing `.cursor/mcp.json`, and confirm the server appears under **Cursor Settings → MCP**. * **401 / "authentication required"** — your API key is missing or invalid. Regenerate it at [appeeky.com → Settings → API Key](https://appeeky.com) and update the `Authorization` header. * **429 / "rate limit"** — you hit a plan or burst limit. See [Rate Limits](/docs/rate-limits). # OpenCode Source: https://docs.appeeky.com/docs/mcp-opencode Connect the Appeeky MCP server to OpenCode and query live App Store & Google Play intelligence by chat. OpenCode connects to Appeeky as a **remote** MCP server at `https://mcp.appeeky.com/mcp`, authenticated with your API key. Get your API key from [appeeky.com → Settings → API Key](https://appeeky.com) or the [API Dashboard](https://dashboard.appeeky.com). ## Connect Appeeky Create or update `opencode.json` in your project root: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "$schema": "https://opencode.ai/config.json", "mcp": { "appeeky": { "type": "remote", "url": "https://mcp.appeeky.com/mcp", "enabled": true, "headers": { "Authorization": "Bearer apk_your_key_here" } } } } ``` You can also use OpenCode's environment syntax to keep the key out of the file: set `"Authorization": "Bearer {env:APPEEKY_API_KEY}"` and export `APPEEKY_API_KEY` in your shell. OpenCode resolves it before the request goes out. Restart OpenCode so it picks up the new server, then confirm the `appeeky` tools are available. ## Build with Appeeky Describe what you want in chat and OpenCode calls the right Appeeky tools: ``` Show me the top 10 apps for "meditation" and analyze their ratings and review counts. ``` ``` Run a full ASO audit on Duolingo (id: 570060128). What's its score and what should they improve? ``` ``` What's happening in the App Store right now? Show me the latest chart activity for the US free apps chart. ``` See the full [tool reference](/docs/mcp#available-tools) for everything Appeeky exposes. ## Troubleshooting * **Tools missing or "not connected"** — verify `opencode.json` is valid JSON and restart OpenCode. * **401 / "authentication required"** — your API key is missing or invalid. Regenerate it at [appeeky.com → Settings → API Key](https://appeeky.com) and update the `Authorization` header. Note the `{env:...}` form is resolved by OpenCode, not the shell — a literal `${VAR}` will fail. * **429 / "rate limit"** — you hit a plan or burst limit. See [Rate Limits](/docs/rate-limits). # VS Code Source: https://docs.appeeky.com/docs/mcp-vscode Connect the Appeeky MCP server to VS Code (GitHub Copilot) and query live App Store & Google Play intelligence by chat. VS Code connects to Appeeky over **HTTP** at `https://mcp.appeeky.com/mcp` for use with GitHub Copilot's agent mode, authenticated with your API key. Get your API key from [appeeky.com → Settings → API Key](https://appeeky.com) or the [API Dashboard](https://dashboard.appeeky.com). MCP support requires a recent VS Code with GitHub Copilot. ## Connect Appeeky Create `.mcp.json` (workspace) or add to your user `mcp.json` via **Command Palette → MCP: Open User Configuration**: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "servers": { "appeeky": { "type": "http", "url": "https://mcp.appeeky.com/mcp", "headers": { "Authorization": "Bearer apk_your_key_here" } } } } ``` VS Code shows a **Start** action on the server entry. Start it, then open **Copilot Chat → Agent mode** and confirm the Appeeky tools appear in the tools picker. ## Build with Appeeky In Copilot agent mode, ask in plain language and it calls the right Appeeky tools: ``` Show me the top 10 apps for "meditation" and analyze their ratings and review counts. ``` ``` Run a full ASO audit on Duolingo (id: 570060128). What's its score and what should they improve? ``` ``` Find keyword opportunities for Headspace (id: 493145008). Which keywords should they target next? ``` See the full [tool reference](/docs/mcp#available-tools) for everything Appeeky exposes. ## Troubleshooting * **Tools missing** — make sure the server is **Started** and you're in Copilot **Agent mode**; the tools only appear there. * **401 / "authentication required"** — your API key is missing or invalid. Regenerate it at [appeeky.com → Settings → API Key](https://appeeky.com) and update the `Authorization` header. * **429 / "rate limit"** — you hit a plan or burst limit. See [Rate Limits](/docs/rate-limits). # Windsurf Source: https://docs.appeeky.com/docs/mcp-windsurf Connect the Appeeky MCP server to Windsurf and query live App Store & Google Play intelligence by chat. Windsurf connects to Appeeky over **HTTP** at `https://mcp.appeeky.com/mcp`, authenticated with your API key. Get your API key from [appeeky.com → Settings → API Key](https://appeeky.com) or the [API Dashboard](https://dashboard.appeeky.com). ## Connect Appeeky In Windsurf, open **Settings → Cascade → MCP Servers → Manage → View raw config**, or edit `~/.codeium/windsurf/mcp_config.json` directly. ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "mcpServers": { "appeeky": { "serverUrl": "https://mcp.appeeky.com/mcp", "headers": { "Authorization": "Bearer apk_your_key_here" } } } } ``` Click **Refresh** in the MCP Servers panel and confirm the `appeeky` tools are listed. ## Build with Appeeky Ask Cascade in plain language and it calls the right Appeeky tools: ``` Show me the top 10 apps for "meditation" and analyze their ratings and review counts. ``` ``` Compare keyword overlap between Headspace (id: 493145008) and Calm (id: 571800810). What keywords is Calm ranking for that Headspace is missing? ``` ``` Which apps gained the most ranks in the top-free chart today? ``` See the full [tool reference](/docs/mcp#available-tools) for everything Appeeky exposes. ## Troubleshooting * **Tools missing or "not connected"** — click **Refresh** in the MCP Servers panel, then restart Windsurf. * **401 / "authentication required"** — your API key is missing or invalid. Regenerate it at [appeeky.com → Settings → API Key](https://appeeky.com) and update the `Authorization` header. * **429 / "rate limit"** — you hit a plan or burst limit. See [Rate Limits](/docs/rate-limits). # Zed Source: https://docs.appeeky.com/docs/mcp-zed Connect the Appeeky MCP server to Zed and query live App Store & Google Play intelligence by chat. Zed connects to Appeeky as a **context server** at `https://mcp.appeeky.com/mcp`, authenticated with your API key. Get your API key from [appeeky.com → Settings → API Key](https://appeeky.com) or the [API Dashboard](https://dashboard.appeeky.com). ## Connect Appeeky Open your Zed settings JSON (`cmd`/`ctrl` + `,` → **Open Settings**) and add a context server: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "context_servers": { "appeeky": { "url": "https://mcp.appeeky.com/mcp", "headers": { "Authorization": "Bearer apk_your_key_here" } } } } ``` Prefer the UI? Open **Settings → AI → MCP Servers → Add Server → Add Remote Server** and paste the same URL and header. Open the assistant panel and confirm the `appeeky` tools are available. ## Build with Appeeky Ask Zed in plain language and it calls the right Appeeky tools: ``` Find keyword suggestions for "screen recorder" and tell me which ones have high volume but low difficulty. ``` ``` Compare keyword overlap between Headspace (id: 493145008) and Calm (id: 571800810). What keywords is Calm ranking for that Headspace is missing? ``` ``` What new apps were released in the last 3 days? Are any of them in the Games category? ``` See the full [tool reference](/docs/mcp#available-tools) for everything Appeeky exposes. ## Troubleshooting * **Tools missing or "not connected"** — re-open Zed's Agent/AI settings and re-add the `appeeky` server, then restart Zed. * **401 / "authentication required"** — your API key is missing or invalid. Regenerate it at [appeeky.com → Settings → API Key](https://appeeky.com) and update the `Authorization` header. * **429 / "rate limit"** — you hit a plan or burst limit. See [Rate Limits](/docs/rate-limits). # My Apps Source: https://docs.appeeky.com/docs/my-apps List, add, update, and delete apps in your appeeky.com tracked app list Manage the apps you track in your [appeeky.com](https://appeeky.com) account. These are the same apps visible under **My Apps** in the web dashboard. Appeeky My Apps Dashboard All My Apps endpoints cost **0 credits** and require the `apps:read` scope for reads and `apps:write` for writes. *** ## List My Apps ``` GET /v1/user/apps ``` Returns all apps in your tracked list, ordered by most recently added. ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/user/apps" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch("https://api.appeeky.com/v1/user/apps", { headers: { "X-API-Key": "YOUR_API_KEY" }, }); const { data } = await res.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/user/apps", headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": [ { "id": "c3f0961b-e252-469b-9d5e-d294d721032e", "app_id": 6443843426, "app_name": "Appeeky", "app_icon_url": "https://...", "developer_name": "Appeeky Inc.", "bundle_id": "com.appeeky.app", "subtitle": "ASO & Keyword Intelligence", "description": "...", "languages": ["en", "tr"], "is_live": true, "category": "Productivity", "platform": "ios", "created_at": "2026-03-19T15:47:06.270736+00:00" } ] } ``` *** ## Add an App ``` POST /v1/user/apps ``` Add an app to your tracked list by its numeric App Store ID. **Request Body:** | Field | Type | Required | Description | | --------------- | --------- | -------- | ----------------------------------------------------------- | | `appId` | number | Yes | Numeric Apple App Store ID | | `appName` | string | No | App display name | | `developerName` | string | No | Developer name | | `bundleId` | string | No | Bundle identifier (e.g. `com.example.app`) | | `subtitle` | string | No | App subtitle | | `description` | string | No | App description | | `languages` | string\[] | No | Supported language codes | | `isLive` | boolean | No | Whether the app is live on the App Store (default: `false`) | | `category` | string | No | App category | | `platform` | string | No | `ios`, `macos`, or `tvos` (default: `ios`) | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/user/apps" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"appId": 6443843426, "appName": "Appeeky", "platform": "ios"}' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch("https://api.appeeky.com/v1/user/apps", { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ appId: 6443843426, appName: "Appeeky", platform: "ios" }), }); const { data } = await res.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.post( "https://api.appeeky.com/v1/user/apps", headers={"X-API-Key": "YOUR_API_KEY"}, json={"appId": 6443843426, "appName": "Appeeky", "platform": "ios"}, ) data = res.json()["data"] ``` **Response (201 Created):** Returns the created app row. *** ## Update an App ``` PATCH /v1/user/apps/:appId ``` Update metadata for an app in your list. Only provide the fields you want to change. **Path Parameters:** | Name | Type | Description | | ------- | ------ | ----------------------------------------- | | `appId` | number | Numeric App Store ID of the app to update | **Request Body:** Same optional fields as Add (all optional). ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X PATCH "https://api.appeeky.com/v1/user/apps/6443843426" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"isLive": true, "category": "Productivity"}' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch("https://api.appeeky.com/v1/user/apps/6443843426", { method: "PATCH", headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ isLive: true, category: "Productivity" }), }); const { data } = await res.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.patch( "https://api.appeeky.com/v1/user/apps/6443843426", headers={"X-API-Key": "YOUR_API_KEY"}, json={"isLive": True, "category": "Productivity"}, ) data = res.json()["data"] ``` **Response (200 OK):** Returns the updated app row. *** ## Remove an App ``` DELETE /v1/user/apps/:appId ``` Remove an app from your tracked list. **Path Parameters:** | Name | Type | Description | | ------- | ------ | ----------------------------------------- | | `appId` | number | Numeric App Store ID of the app to remove | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X DELETE "https://api.appeeky.com/v1/user/apps/6443843426" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch("https://api.appeeky.com/v1/user/apps/6443843426", { method: "DELETE", headers: { "X-API-Key": "YOUR_API_KEY" }, }); const { data } = await res.json(); // { success: true } ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.delete( "https://api.appeeky.com/v1/user/apps/6443843426", headers={"X-API-Key": "YOUR_API_KEY"}, ) ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "success": true } } ``` *** ## Errors | Status | Code | When | | ------ | ---------------- | ----------------------------------------- | | 400 | `BAD_REQUEST` | `appId` missing or not a number | | 403 | `FORBIDDEN` | Key scope does not allow this operation | | 404 | `USER_NOT_FOUND` | No appeeky.com account linked to this key | | 404 | `NOT_FOUND` | App not found in your list | | 409 | `DUPLICATE` | App already in your list | # Competitors Source: https://docs.appeeky.com/docs/my-apps-competitors List, add, and remove competitor apps for any app in your tracked list Track competitor apps against each of your tracked apps. Competitors are displayed on the **Competitors** tab in the appeeky.com dashboard. All Competitor endpoints cost **0 credits** and require the `apps:read` scope for reads and `apps:write` for writes. *** ## List Competitors ``` GET /v1/user/apps/:appId/competitors ``` Returns all competitor apps tracked for a given app. **Path Parameters:** | Name | Type | Description | | ------- | ------ | ---------------------------------------- | | `appId` | number | Numeric App Store ID of your tracked app | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/user/apps/6443843426/competitors" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/user/apps/6443843426/competitors", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/user/apps/6443843426/competitors", headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": [ { "id": "a4b1d302-...", "competitor_app_id": 1547223625, "competitor_name": "Voice Tape Record", "competitor_icon_url": "https://...", "created_at": "2026-03-19T15:47:06.270736+00:00" } ] } ``` *** ## Add a Competitor ``` POST /v1/user/apps/:appId/competitors ``` Add a competitor app by App Store numeric ID or Google Play package name. **Path Parameters:** | Name | Type | Description | | ------- | ------ | ---------------------------------------------------------------------- | | `appId` | number | Numeric App Store ID of your tracked app (or synthetic id for Android) | **Request Body:** | Field | Type | Required | Description | | --------------------------------- | ---------------- | -------- | ----------------------------------------------------------- | | `competitorAppId` | number \| string | Yes\* | App Store ID (number) or Play package name (string) | | `competitorStoreAppId` | string | Yes\* | Canonical store id (preferred for Play packages) | | `platform` / `competitorPlatform` | string | No | `apple`/`ios` or `google`/`android` — inferred when omitted | | `competitorName` | string | No | Display name for the competitor | \* Provide `competitorAppId` and/or `competitorStoreAppId`. ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/user/apps/6443843426/competitors" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"competitorAppId": 1547223625, "competitorName": "Sensor Tower"}' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/user/apps/6443843426/competitors", { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ competitorAppId: 1547223625, competitorName: "Sensor Tower" }), } ); const { data } = await res.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.post( "https://api.appeeky.com/v1/user/apps/6443843426/competitors", headers={"X-API-Key": "YOUR_API_KEY"}, json={"competitorAppId": 1547223625, "competitorName": "Sensor Tower"}, ) data = res.json()["data"] ``` **Response (201 Created):** Returns the created competitor row. *** ## Remove a Competitor ``` DELETE /v1/user/apps/:appId/competitors/:competitorAppId ``` Remove a competitor from a tracked app. **Path Parameters:** | Name | Type | Description | | ----------------- | ------ | ------------------------------------------------ | | `appId` | number | Numeric App Store ID of your tracked app | | `competitorAppId` | number | Numeric App Store ID of the competitor to remove | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X DELETE "https://api.appeeky.com/v1/user/apps/6443843426/competitors/1547223625" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/user/apps/6443843426/competitors/1547223625", { method: "DELETE", headers: { "X-API-Key": "YOUR_API_KEY" } } ); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests requests.delete( "https://api.appeeky.com/v1/user/apps/6443843426/competitors/1547223625", headers={"X-API-Key": "YOUR_API_KEY"}, ) ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "success": true } } ``` *** ## Errors | Status | Code | When | | ------ | -------------------- | -------------------------------------------- | | 400 | `BAD_REQUEST` | `appId` or `competitorAppId` is not a number | | 403 | `FORBIDDEN` | Key scope does not allow this operation | | 404 | `USER_APP_NOT_FOUND` | `appId` is not in your tracked list | | 404 | `NOT_FOUND` | Competitor not found | | 409 | `DUPLICATE` | Competitor already tracked for this app | # Tracked Keywords Source: https://docs.appeeky.com/docs/my-apps-keywords List, add, and remove keywords you track for each app Manage the keywords tracked for any app in your list. Tracked keywords power the keyword rank monitoring shown on the **Keywords** tab in the appeeky.com dashboard. All Tracked Keyword endpoints cost **0 credits** and require the `apps:read` scope for reads and `apps:write` for writes. *** ## List Tracked Keywords ``` GET /v1/user/apps/:appId/keywords ``` Returns all keywords tracked for a given app. **Path Parameters:** | Name | Type | Description | | ------- | ------ | ---------------------------------------- | | `appId` | number | Numeric App Store ID of your tracked app | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/user/apps/6443843426/keywords" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/user/apps/6443843426/keywords", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/user/apps/6443843426/keywords", headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": [ { "id": "7e4a1c90-...", "keyword": "aso tool", "country": "us", "language": "en", "created_at": "2026-03-19T15:47:06.270736+00:00" } ] } ``` *** ## Add a Tracked Keyword ``` POST /v1/user/apps/:appId/keywords ``` Start tracking a keyword for an app. **Path Parameters:** | Name | Type | Description | | ------- | ------ | ---------------------------------------- | | `appId` | number | Numeric App Store ID of your tracked app | **Request Body:** | Field | Type | Required | Description | | ---------- | ------ | -------- | ---------------------------------------- | | `keyword` | string | Yes | The keyword to track (e.g. `"aso tool"`) | | `country` | string | No | ISO country code (default: `"us"`) | | `language` | string | No | ISO language code (default: `"en"`) | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/user/apps/6443843426/keywords" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"keyword": "aso tool", "country": "us", "language": "en"}' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/user/apps/6443843426/keywords", { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ keyword: "aso tool", country: "us", language: "en" }), } ); const { data } = await res.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.post( "https://api.appeeky.com/v1/user/apps/6443843426/keywords", headers={"X-API-Key": "YOUR_API_KEY"}, json={"keyword": "aso tool", "country": "us", "language": "en"}, ) data = res.json()["data"] ``` **Response (201 Created):** Returns the created keyword row. *** ## Remove a Tracked Keyword ``` DELETE /v1/user/apps/:appId/keywords/:keywordId ``` Stop tracking a keyword. **Path Parameters:** | Name | Type | Description | | ----------- | ------ | ---------------------------------------- | | `appId` | number | Numeric App Store ID of your tracked app | | `keywordId` | string | UUID of the tracked keyword row | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X DELETE "https://api.appeeky.com/v1/user/apps/6443843426/keywords/7e4a1c90-..." \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/user/apps/6443843426/keywords/7e4a1c90-...", { method: "DELETE", headers: { "X-API-Key": "YOUR_API_KEY" } } ); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests requests.delete( "https://api.appeeky.com/v1/user/apps/6443843426/keywords/7e4a1c90-...", headers={"X-API-Key": "YOUR_API_KEY"}, ) ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "success": true } } ``` *** ## Errors | Status | Code | When | | ------ | -------------------- | -------------------------------------------------- | | 400 | `BAD_REQUEST` | `keyword` field missing or `appId` is not a number | | 403 | `FORBIDDEN` | Key scope does not allow this operation | | 404 | `USER_APP_NOT_FOUND` | `appId` is not in your tracked list | | 404 | `NOT_FOUND` | Keyword row not found | | 409 | `DUPLICATE` | Keyword already tracked for this app + country | # Metadata Versions Source: https://docs.appeeky.com/docs/my-apps-versions Create, update, and release versioned ASO metadata for your apps Store and version your App Store metadata — title, subtitle, description, and keywords — directly through the API. Versions appear in the **Versions** tab of the appeeky.com dashboard. All Metadata Version endpoints cost **0 credits** and require the `apps:read` scope for reads and `apps:write` for writes. *** ## List Versions ``` GET /v1/user/apps/:appId/versions ``` Returns all saved metadata versions for an app, ordered newest first. **Path Parameters:** | Name | Type | Description | | ------- | ------ | ---------------------------------------- | | `appId` | number | Numeric App Store ID of your tracked app | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/user/apps/6443843426/versions" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/user/apps/6443843426/versions", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/user/apps/6443843426/versions", headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": [ { "id": "b5c2e891-...", "version_name": "v2.1 Spring", "title": "Appeeky – ASO Intelligence", "subtitle": "Keyword & Rank Tracker", "description": "Track your keywords and grow your App Store rank...", "keywords": "aso, keyword tracker, app store optimization", "language": "en-US", "is_released": false, "released_at": null, "created_at": "2026-03-19T15:47:06.270736+00:00" } ] } ``` *** ## Create a Version ``` POST /v1/user/apps/:appId/versions ``` Save a new set of metadata as a named version. **Path Parameters:** | Name | Type | Description | | ------- | ------ | ---------------------------------------- | | `appId` | number | Numeric App Store ID of your tracked app | **Request Body:** | Field | Type | Required | Description | | ------------- | ------ | -------- | ----------------------------------------------- | | `versionName` | string | Yes | A label for this version (e.g. `"v2.1 Spring"`) | | `title` | string | No | App title (max 30 chars for App Store) | | `subtitle` | string | No | App subtitle (max 30 chars) | | `description` | string | No | Full app description | | `keywords` | string | No | Comma-separated keyword field | | `language` | string | No | Locale code (default: `"en-US"`) | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/user/apps/6443843426/versions" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "versionName": "v2.1 Spring", "title": "Appeeky – ASO Intelligence", "subtitle": "Keyword & Rank Tracker", "keywords": "aso, keyword tracker, app store optimization", "language": "en-US" }' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/user/apps/6443843426/versions", { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ versionName: "v2.1 Spring", title: "Appeeky – ASO Intelligence", subtitle: "Keyword & Rank Tracker", keywords: "aso, keyword tracker, app store optimization", language: "en-US", }), } ); const { data } = await res.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.post( "https://api.appeeky.com/v1/user/apps/6443843426/versions", headers={"X-API-Key": "YOUR_API_KEY"}, json={ "versionName": "v2.1 Spring", "title": "Appeeky – ASO Intelligence", "subtitle": "Keyword & Rank Tracker", "keywords": "aso, keyword tracker, app store optimization", "language": "en-US", }, ) data = res.json()["data"] ``` **Response (201 Created):** Returns the created version row. *** ## Update a Version ``` PATCH /v1/user/apps/:appId/versions/:versionId ``` Update any metadata field on an existing version. Only include the fields you want to change. **Path Parameters:** | Name | Type | Description | | ----------- | ------ | ---------------------------------------- | | `appId` | number | Numeric App Store ID of your tracked app | | `versionId` | string | UUID of the version to update | **Request Body:** Same optional fields as Create (all optional). ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X PATCH "https://api.appeeky.com/v1/user/apps/6443843426/versions/b5c2e891-..." \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"keywords": "aso tool, keyword rank, app growth"}' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/user/apps/6443843426/versions/b5c2e891-...", { method: "PATCH", headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ keywords: "aso tool, keyword rank, app growth" }), } ); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests requests.patch( "https://api.appeeky.com/v1/user/apps/6443843426/versions/b5c2e891-...", headers={"X-API-Key": "YOUR_API_KEY"}, json={"keywords": "aso tool, keyword rank, app growth"}, ) ``` **Response (200 OK):** Returns the updated version row. *** ## Mark a Version as Released ``` POST /v1/user/apps/:appId/versions/:versionId/release ``` Mark a metadata version as the currently released version. Sets `is_released = true` and records a `released_at` timestamp. **Path Parameters:** | Name | Type | Description | | ----------- | ------ | ---------------------------------------- | | `appId` | number | Numeric App Store ID of your tracked app | | `versionId` | string | UUID of the version to mark as released | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/user/apps/6443843426/versions/b5c2e891-.../release" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/user/apps/6443843426/versions/b5c2e891-.../release", { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY" } } ); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests requests.post( "https://api.appeeky.com/v1/user/apps/6443843426/versions/b5c2e891-.../release", headers={"X-API-Key": "YOUR_API_KEY"}, ) ``` **Response (200 OK):** Returns the updated version row with `is_released: true`. *** ## Errors | Status | Code | When | | ------ | -------------------- | -------------------------------------------------------- | | 400 | `BAD_REQUEST` | `versionName` missing on create, or `appId` not a number | | 403 | `FORBIDDEN` | Key scope does not allow this operation | | 404 | `USER_APP_NOT_FOUND` | `appId` is not in your tracked list | | 404 | `NOT_FOUND` | Version not found | # My Data Overview Source: https://docs.appeeky.com/docs/my-data-overview Manage your apps, competitors, tracked keywords, metadata versions, and saved reports via API or MCP The **My Data** endpoints let you read and write the data tied to your [appeeky.com](https://appeeky.com) account — the same data you see in the web dashboard — directly through the REST API or MCP server. These endpoints are linked to your **appeeky.com** account, not dashboard.appeeky.com. You must have an account at [appeeky.com](https://appeeky.com) and an API key generated from **Settings → API Key**. *** ## What You Can Manage | Resource | Endpoints | Description | | --------------------- | --------------------------------------------------- | ----------------------------------------------------------- | | **My Apps** | `GET/POST/PATCH/DELETE /v1/user/apps` | Your tracked app list | | **Competitors** | `GET/POST/DELETE /v1/user/apps/:appId/competitors` | Competitor apps per tracked app | | **Tracked Keywords** | `GET/POST/DELETE /v1/user/apps/:appId/keywords` | Keywords tracked per app | | **Metadata Versions** | `GET/POST/PATCH/POST /v1/user/apps/:appId/versions` | Versioned ASO copy (title, subtitle, description, keywords) | | **Reports** | `GET/DELETE /v1/user/reports` | Saved ASO analysis reports | *** ## Credits All My Data endpoints cost **0 credits**. They do not count against your monthly credit limit. *** ## Scopes My Data endpoints are protected by API key scopes. When you generate or update your key at [appeeky.com/settings](https://appeeky.com) you can restrict access: | Scope | Access | | -------------------------------- | ------------------------------------------------------------ | | `apps:read` *(or unrestricted)* | `GET` endpoints — list and read | | `apps:write` *(or unrestricted)* | `POST`, `PATCH`, `DELETE` endpoints — create, update, delete | An unrestricted key (default) has access to everything. *** ## Common Error Codes | Status | Code | When | | ------ | ----------------- | --------------------------------------------- | | 401 | `MISSING_API_KEY` | No API key provided | | 401 | `INVALID_API_KEY` | Invalid or inactive key | | 403 | `FORBIDDEN` | Key scope does not allow this operation | | 404 | `USER_NOT_FOUND` | No appeeky.com account linked to this API key | | 404 | `NOT_FOUND` | Resource not found or doesn't belong to you | | 409 | `DUPLICATE` | Item already exists | # Reports Source: https://docs.appeeky.com/docs/my-reports List, retrieve, and delete saved ASO reports from your appeeky.com account Access the ASO analysis reports saved in your [appeeky.com](https://appeeky.com) account. Reports are generated by tools such as [ASO Audit](/docs/aso-audit), [Competitor Report](/docs/aso-competitor-report), and [Metadata Suggestions](/docs/aso-metadata-suggest) and stored so you can retrieve them later. All Report endpoints cost **0 credits** and require the `apps:read` scope for reads and `apps:write` for deletes. *** ## List Reports ``` GET /v1/user/reports ``` Returns all saved reports for your account, ordered most recently created first. **Query Parameters:** | Name | Type | Default | Description | | -------- | ------ | ------- | ------------------------------------------------------------------------------ | | `appId` | number | — | Filter by a specific App Store ID | | `type` | string | — | Filter by report type (e.g. `aso_audit`, `competitor`, `metadata_suggestions`) | | `limit` | number | `20` | Max number of results to return (max `100`) | | `offset` | number | `0` | Pagination offset | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/user/reports?appId=6443843426&limit=10" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/user/reports?appId=6443843426&limit=10", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/user/reports", params={"appId": 6443843426, "limit": 10}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": [ { "id": "d9f3a122-...", "app_id": 6443843426, "report_type": "aso_audit", "title": "ASO Audit — Appeeky", "summary": "Overall score: 82/100. Title length optimal. Keyword density low.", "created_at": "2026-03-19T15:47:06.270736+00:00" } ] } ``` *** ## Get a Report ``` GET /v1/user/reports/:reportId ``` Retrieve the full content of a single saved report. **Path Parameters:** | Name | Type | Description | | ---------- | ------ | ------------------ | | `reportId` | string | UUID of the report | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/user/reports/d9f3a122-..." \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/user/reports/d9f3a122-...", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/user/reports/d9f3a122-...", headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "id": "d9f3a122-...", "app_id": 6443843426, "report_type": "aso_audit", "title": "ASO Audit — Appeeky", "summary": "Overall score: 82/100. Title length optimal. Keyword density low.", "content": { "score": 82, "sections": [ { "name": "Title", "score": 95, "feedback": "Title is within the 30-character limit." }, { "name": "Keywords", "score": 68, "feedback": "Keyword field is underutilized." } ] }, "created_at": "2026-03-19T15:47:06.270736+00:00" } } ``` *** ## Delete a Report ``` DELETE /v1/user/reports/:reportId ``` Permanently delete a saved report from your account. **Path Parameters:** | Name | Type | Description | | ---------- | ------ | ---------------------------- | | `reportId` | string | UUID of the report to delete | ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X DELETE "https://api.appeeky.com/v1/user/reports/d9f3a122-..." \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/user/reports/d9f3a122-...", { method: "DELETE", headers: { "X-API-Key": "YOUR_API_KEY" } } ); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests requests.delete( "https://api.appeeky.com/v1/user/reports/d9f3a122-...", headers={"X-API-Key": "YOUR_API_KEY"}, ) ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "success": true } } ``` *** ## Errors | Status | Code | When | | ------ | ---------------- | -------------------------------------------------- | | 403 | `FORBIDDEN` | Key scope does not allow this operation | | 404 | `USER_NOT_FOUND` | No appeeky.com account linked to this key | | 404 | `NOT_FOUND` | Report not found or doesn't belong to your account | # New Source: https://docs.appeeky.com/docs/new-number-1 Apps currently ranked ``` GET /v1/discover/new-number-1 ``` Returns apps that are currently ranked **#1** in their primary App Store category. The endpoint fetches the overall Top Free chart from Apple RSS, groups apps by their primary genre, and picks the top-ranked app for each category. ## Query Parameters | Name | Type | Default | Description | | ------- | ------ | ------- | ---------------------------------------------- | | country | string | `us` | ISO country code (e.g. `us`, `gb`, `de`, `jp`) | ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/discover/new-number-1?country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/discover/new-number-1?country=us", { headers: { "X-API-Key": "YOUR_API_KEY", }, } ); const { data } = await response.json(); data.apps.forEach((app) => { console.log(`#1 in ${app.category}: ${app.title}`); }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/discover/new-number-1", params={"country": "us"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = response.json()["data"] for app in data["apps"]: print(f"#1 in {app['category']}: {app['title']}") ``` ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "country": "us", "apps": [ { "category": "Sports", "appId": "6449023896", "title": "Olympic Games™", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/a1/b2/c3/a1b2c3d4-icon/AppIcon-0-0-1x_U007emarketing-0-7-0-85-220.png/512x512bb.jpg", "reachedAt": null }, { "category": "Weather", "appId": "300048137", "title": "AccuWeather: Weather Alerts", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/d4/e5/f6/d4e5f6a7-icon/AppIcon-0-0-1x_U007emarketing-0-7-0-85-220.png/512x512bb.jpg", "reachedAt": null }, { "category": "Photo & Video", "appId": "1642763440", "title": "Lensa: Photo Editor", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/b8/c9/da/b8c9da01-icon/AppIcon-0-0-1x_U007emarketing-0-7-0-85-220.png/512x512bb.jpg", "reachedAt": null }, { "category": "Social Networking", "appId": "835599320", "title": "Threads", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/e2/f3/04/e2f30415-icon/AppIcon-0-0-1x_U007emarketing-0-7-0-85-220.png/512x512bb.jpg", "reachedAt": null }, { "category": "Entertainment", "appId": "1446075923", "title": "Tubi: Movies & Live TV", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/f5/06/17/f5061728-icon/AppIcon-0-0-1x_U007emarketing-0-7-0-85-220.png/512x512bb.jpg", "reachedAt": null } ] } } ``` ## App Object | Field | Type | Description | | --------- | -------------- | --------------------------------------------------------------- | | category | string | App Store category name (e.g. `Sports`, `Weather`) | | appId | string | Apple App ID | | title | string | App name | | icon | string | App icon URL (512px) | | reachedAt | string \| null | ISO timestamp of when the app first reached #1 (see note below) | ## How It Works 1. Fetches the **Top Free** overall chart from Apple RSS for the specified country (up to 200 apps). 2. Each app's **primary genre** is determined from its iTunes metadata. 3. Apps are grouped by genre, and the **highest-ranked** app in each group becomes the category's #1. 4. Results are returned as a flat list — one app per category. **`reachedAt` is always `null` currently.** Determining when an app first reached #1 would require storing historical chart snapshots and comparing them over time. This feature is planned for a future release once daily chart tracking is implemented. Use this endpoint to **monitor category leaders** and spot emerging trends. If a new or unexpected app appears as #1 in a category, it may indicate a viral moment, seasonal trend, or major marketing push. ## Errors | Status | Code | When | | ------ | ----------------- | ------------------------------------ | | 400 | INVALID\_PARAMS | Invalid `country` value | | 401 | MISSING\_API\_KEY | No API key in the request | | 401 | INVALID\_API\_KEY | Invalid or expired API key | | 500 | FETCH\_FAILED | Failed to fetch Apple RSS chart data | # New Releases Source: https://docs.appeeky.com/docs/new-releases Discover recently released apps on the App Store ``` GET /v1/new-releases ``` Returns recently released apps discovered via iTunes Search across multiple App Store categories. Only apps with an original `releaseDate` within the specified time window are included — updated apps are filtered out. Results are sorted newest first. ## Query Parameters | Name | Type | Default | Description | | ------- | ------ | ------- | ------------------------------------------------------------------ | | country | string | `us` | ISO country code (e.g. `us`, `gb`, `de`, `jp`) | | limit | number | `25` | Maximum number of results to return (max `50`) | | maxDays | number | `30` | Only include apps released within the last N days (range `7`–`90`) | ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/new-releases?country=us&limit=10&maxDays=14" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/new-releases?country=us&limit=10&maxDays=14", { headers: { "X-API-Key": "YOUR_API_KEY", }, } ); const { data } = await response.json(); console.log(data.apps); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/new-releases", params={"country": "us", "limit": 10, "maxDays": 14}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = response.json()["data"] print(data["apps"]) ``` ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "apps": [ { "appId": "6742819203", "title": "Pixel Dojo", "developer": "Neon Arc Studios", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple221/v4/ab/cd/ef/abcdef12-icon/AppIcon-0-0-1x_U007emarketing-0-7-0-85-220.png/512x512bb.jpg", "category": "Games", "releasedAt": "2026-02-15T00:00:00Z", "releasedAgo": "2d ago", "url": "https://apps.apple.com/app/id6742819203" }, { "appId": "6738501274", "title": "FocusFlow - Deep Work Timer", "developer": "Mindful Apps Co.", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple221/v4/12/34/56/12345678-icon/AppIcon-0-0-1x_U007emarketing-0-7-0-85-220.png/512x512bb.jpg", "category": "Productivity", "releasedAt": "2026-02-13T00:00:00Z", "releasedAgo": "4d ago", "url": "https://apps.apple.com/app/id6738501274" }, { "appId": "6751093847", "title": "Routemaster - Trip Planner", "developer": "Wanderlust Digital Ltd", "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple221/v4/78/9a/bc/789abc01-icon/AppIcon-0-0-1x_U007emarketing-0-7-0-85-220.png/512x512bb.jpg", "category": "Travel", "releasedAt": "2026-02-10T00:00:00Z", "releasedAgo": "7d ago", "url": "https://apps.apple.com/app/id6751093847" } ] } } ``` ## App Object | Field | Type | Description | | ----------- | -------------- | ---------------------------------------------------------- | | appId | string | Apple App ID | | title | string | App name | | developer | string | Developer / publisher name | | icon | string | App icon URL (512px) | | category | string | Primary category (e.g. `Games`, `Productivity`) | | releasedAt | string \| null | Original release date (ISO 8601) | | releasedAgo | string \| null | Human-readable relative time (e.g. `"2d ago"`, `"1w ago"`) | | url | string | App Store URL | ## How It Works The endpoint uses **iTunes Search** across 55+ category-specific search terms to build a large candidate pool of apps. Each app's original `releaseDate` is checked — only apps truly released within the `maxDays` window are kept. Apps that were merely *updated* (where `releaseDate` is old but `currentVersionReleaseDate` is recent) are filtered out. Results are sorted by `releaseDate` descending (newest first) and capped at the requested `limit`. Use `maxDays=7` for the freshest releases. This narrows the window to only the past week, giving you the most recently launched apps without older entries. ## Errors | Status | Code | When | | ------ | ----------------- | ---------------------------------------------- | | 400 | INVALID\_PARAMS | Invalid `limit`, `maxDays`, or `country` value | | 401 | MISSING\_API\_KEY | No API key in the request | | 401 | INVALID\_API\_KEY | Invalid or expired API key | | 500 | FETCH\_FAILED | Failed to fetch data from iTunes Search | # API Overview Source: https://docs.appeeky.com/docs/overview Complete endpoint reference, response format, error codes, pagination, and country codes Base URL: `https://api.appeeky.com` All endpoints are versioned under `/v1`. Authentication is required for all endpoints except `/v1/health`. Most endpoints cover both the **Apple App Store** and **Google Play** via a `platform` parameter (`apple` by default, or `google`). Apple uses numeric App IDs (`1617391485`); Google Play uses package names (`com.spotify.music`). See [Platforms](/docs/platforms) for the full cross-platform list and identifier rules. *** ## Endpoints ### Health & Auth | Method | Endpoint | Description | Credits | | ------ | ---------------- | ----------------------- | ------- | | GET | `/v1/health` | Service health check | 0 | | GET | `/v1/auth/usage` | Check your credit usage | 0 | ### Apps | Method | Endpoint | Description | Credits | | ------ | ------------------------------- | -------------------------------------------------------- | ------- | | GET | `/v1/apps/:id` | Full app metadata (iTunes Lookup) | 2 | | GET | `/v1/apps/:id/intelligence` | Intelligence report: revenue, downloads, IAPs, sentiment | 5 | | GET | `/v1/apps/:id/similar` | Similar and competing apps (3-layer matching) | 2 | | GET | `/v1/apps/:id/reviews` | User reviews from Apple RSS (up to 500) | 2 | | GET | `/v1/apps/:id/keywords` | All organic keyword rankings for an app | 3 | | GET | `/v1/apps/:id/keywords/trends` | Historical rank trend for a keyword | 2 | | GET | `/v1/apps/:id/country-rankings` | App's chart positions across countries | 3 | ### Screenshots | Method | Endpoint | Description | Credits | | ------ | ----------------------------------------- | --------------------------------------------------------- | ------- | | GET | `/v1/apps/:id/screenshots` | All screenshots for an app, split by device (iPhone/iPad) | 2 | | GET | `/v1/apps/:id/screenshots/competitors` | Compare screenshots between an app and its competitors | 3 | | GET | `/v1/categories/:genreId/top/screenshots` | Screenshots for top apps in a category | 3 | ### Keywords | Method | Endpoint | Description | Credits | | ------ | ------------------------------ | ---------------------------------------------- | ------- | | GET | `/v1/keywords/ranks` | Apps ranking for a specific keyword | 2 | | GET | `/v1/keywords/suggestions` | Apple Search autocomplete suggestions | 1 | | GET | `/v1/keywords/metrics` | Search volume and difficulty for a keyword | 2 | | GET | `/v1/keywords/compare` | Competitor keyword overlap and gap analysis | 3 | | GET | `/v1/keywords/compare-cluster` | Multi-competitor keyword lifecycle buckets | 4 | | GET | `/v1/keywords/visibility` | App visibility score across tracked keywords | 3 | | GET | `/v1/keywords/movers` | Per-app keyword rank gainers / losers | 3 | | GET | `/v1/keywords/gap` | Top opportunity-ranked competitor keyword gaps | 4 | | GET | `/v1/keywords/trending` | Keywords with fastest-growing reach | 2 | | POST | `/v1/keywords/track` | Add keyword to your tracking list | 1 | ### Search & Discovery | Method | Endpoint | Description | Credits | | ------ | ------------------------------------- | ----------------------------------------------- | ------- | | GET | `/v1/search` | Search apps by keyword or App ID | 1 | | GET | `/v1/categories` | List all App Store categories | 1 | | GET | `/v1/categories/:id/top` | Top Free / Top Paid / Top Grossing per category | 2 | | GET | `/v1/categories/:id/downloads-to-top` | Estimated downloads to reach chart positions | 2 | | GET | `/v1/featured` | Apps featured on the App Store Today tab | 3 | | GET | `/v1/new-releases` | Recently released apps from Apple RSS | 2 | | GET | `/v1/discover` | Trending and noteworthy apps | 2 | | GET | `/v1/discover/new-number-1` | Apps that just reached #1 in their category | 2 | ### Market Intelligence | Method | Endpoint | Description | Credits | | ------ | --------------------- | ------------------------------------------------------- | ------- | | GET | `/v1/market/movers` | Top gainers, losers, new entries, and exits from charts | 3 | | GET | `/v1/market/activity` | Live feed of chart movements and rank changes | 2 | ### Localization | Method | Endpoint | Description | Credits | | ------ | ------------------------------- | ------------------------------------------------------------------------------------------------- | ---------- | | POST | `/v1/localizations` | Translate store metadata into up to 10 languages (async); optionally publish to App Store Connect | 3 / locale | | GET | `/v1/localizations/jobs/:jobId` | Poll status and per-locale results of a localization run | 0 | ### Idea Validation Turn a plain-language app idea into real App Store evidence — direct competitors with revenue/download estimates, keyword demand vs difficulty, and pain points mined from competitor reviews — plus an AI verdict and a go-to-market / ASO starter pack. Runs async; the POST returns a `jobId` you poll. | Method | Endpoint | Description | Credits | | ------ | ------------------------------- | ------------------------------------------------------- | ------- | | POST | `/v1/validate-idea` | Validate an app idea against the live App Store (async) | 12 | | GET | `/v1/validate-idea/jobs/:jobId` | Poll status and the full validation result | 0 | **Request body** (`POST /v1/validate-idea`): ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "idea": "An AI-powered personal budgeting app that auto-categorizes spending and nudges you before you overspend", "country": "us" } ``` | Field | Type | Default | Description | | --------- | ------ | ------- | -------------------------------------------- | | `idea` | string | — | The app idea in plain language (8–600 chars) | | `country` | string | `us` | ISO 3166-1 alpha-2 storefront to research | **Result** (`GET /v1/validate-idea/jobs/:jobId` once `status` is `completed`) includes: a refined `plan` (search queries, seed keywords, category), `competitors[]` (with `estimatedRevenue` / `estimatedDownloads`), `keywords[]` (`volumeScore` + `difficulty`), `painPoints[]` mined from competitor reviews, a `verdictScore` (0–100) and `verdict` (`build` | `validate` | `pivot` | `avoid`), and a `marketing` starter pack (suggested name, subtitle, keywords, positioning, go-to-market moves). ### App Ad Creatives Generate a Meta-ready square ad creative and paste-ready ad copy from a real App Store or Google Play listing. Runs async; the POST returns a `jobId` you poll. | Method | Endpoint | Description | Credits | | ------ | ---------------------------------- | ------------------------------------------------------------------------- | --------------------- | | POST | `/v1/app-ad-creatives/generate` | Analyze a listing, generate copy, and optionally create a square ad image | 1 API or 1-5 creative | | GET | `/v1/app-ad-creatives/jobs/:jobId` | Poll status and retrieve the generated creative result | 0 | **Credits:** `analyze` or `generateImage: false` costs 1 API credit. Platform-funded image generation or editing uses creative credits by quality: `low` = 1, `medium` = 2, `high` = 5. BYOK image jobs use 1 API credit and no creative credits. ### RevenueCat (Your App Analytics) RevenueCat endpoints require an `X-RC-Key` header with your RevenueCat secret API key (`sk_xxx`). These proxy to the [RevenueCat Charts API](https://www.revenuecat.com/docs/api-v2#tag/Charts-and-Metrics) and require the Pro plan (free up to \$2,500 MTR). | Method | Endpoint | Description | Credits | | ------ | ------------------------------------------ | ---------------------------------------------------------------- | ------- | | GET | `/v1/revenuecat/overview` | Subscription metrics snapshot: MRR, revenue, active subs, trials | 2 | | GET | `/v1/revenuecat/charts/:chartName` | Time-series chart data (revenue, MRR, churn, trials, etc.) | 3 | | GET | `/v1/revenuecat/charts/:chartName/options` | Available resolutions, segments, and filters for a chart | 1 | ### Superwall (Your Paywall Analytics) Superwall endpoints use the organization API key saved in Appeeky Settings (`POST /v1/connect/superwall/credentials`). There is no per-request Superwall key header. | Method | Endpoint | Description | Credits | | ------ | -------------------------------------------- | -------------------------------------------------- | ------- | | GET | `/v1/connect/superwall/apps` | List Superwall projects and applications | 0 | | GET | `/v1/connect/superwall/metrics/overview` | Paywall KPIs: proceeds, MRR, users, conversion | 0 | | GET | `/v1/connect/superwall/metrics/dashboard` | Bundled stats, KPI charts, and recent transactions | 0 | | POST | `/v1/connect/superwall/metrics/charts/data` | Time-series for a Superwall y-axis metric | 0 | | GET | `/v1/connect/superwall/metrics/transactions` | Recent purchases, renewals, and trials | 0 | | GET | `/v1/connect/superwall/campaigns` | Campaigns and placements for an application | 0 | See [Superwall](/docs/superwall) and [Superwall Metrics](/docs/superwall-metrics). ### App Store Connect (Your Apps) App Store Connect endpoints require `X-ASC-Issuer-Id`, `X-ASC-Key-Id`, and `X-ASC-Private-Key` headers with your [App Store Connect API key](https://appstoreconnect.apple.com/access/api). | Method | Endpoint | Description | Credits | | ------ | ------------------------------------------------------------- | ---------------------------------------------------- | ------- | | GET | `/v1/connect/apps` | List your apps (filter by bundleId) | 2 | | GET | `/v1/connect/apps/:appId` | Get app details | 2 | | GET | `/v1/connect/apps/:appId/app-infos` | List App Info resources (for app-level localization) | 2 | | GET | `/v1/connect/apps/:appId/versions` | List App Store versions | 3 | | POST | `/v1/connect/apps/:appId/versions` | Create new version | 4 | | PATCH | `/v1/connect/versions/:versionId` | Update version attributes | 4 | | GET | `/v1/connect/app-infos/:appInfoId/localizations` | List App Information localizations (name, subtitle) | 2 | | PATCH | `/v1/connect/app-info-localizations/:localizationId` | Update App Information localization (name, subtitle) | 3 | | GET | `/v1/connect/versions/:versionId/localizations` | List metadata localizations | 2 | | GET | `/v1/connect/localizations/:localizationId` | Get localization (description, keywords) | 2 | | PATCH | `/v1/connect/localizations/:localizationId` | Update metadata (description, keywords, whatsNew) | 3 | | GET | `/v1/connect/apps/:appId/analytics/report-requests` | List analytics report requests | 2 | | POST | `/v1/connect/apps/:appId/analytics/report-requests` | Create analytics report request | 3 | | GET | `/v1/connect/analytics/report-requests/:requestId/reports` | List reports for a request | 2 | | GET | `/v1/connect/analytics/reports/:reportId/instances` | List instances for a report | 2 | | GET | `/v1/connect/analytics/report-instances/:instanceId/segments` | List segments (download URLs) | 2 | | GET | `/v1/connect/analytics/report-segments/:segmentId` | Get segment details | 1 | | GET | `/v1/connect/sales-reports` | Download Sales and Trends report (gzip) | 3 | ### Google Play Console (Your Apps) Google Play Console endpoints use a Google service account. Connect once with `POST /v1/connect/google-play/credentials`, or pass `X-GP-Service-Account-Json` / `X-GP-Client-Email` + `X-GP-Private-Key-B64` per request. Public Google Play ASO/keyword endpoints still use `platform=google`; these endpoints are for apps you own in Play Console. | Method | Endpoint | Description | Credits | | ------ | ------------------------------------------------------------------- | --------------------------------------------------------------------------- | ------- | | POST | `/v1/connect/google-play/credentials` | Save & verify service account credentials | 0 | | GET | `/v1/connect/google-play/credentials/status` | Connection status | 2 | | DELETE | `/v1/connect/google-play/credentials` | Remove stored credentials | 0 | | GET | `/v1/connect/google-play/apps` | List Play Console apps visible to the service account | 2 | | GET | `/v1/connect/google-play/apps/:packageName/reviews` | List reviews | 2 | | GET | `/v1/connect/google-play/apps/:packageName/reviews/:reviewId` | Get one review | 2 | | POST | `/v1/connect/google-play/apps/:packageName/reviews/:reviewId/reply` | Reply to or update a review response | 2 | | GET | `/v1/connect/google-play/apps/:packageName/vitals/:metricSet` | Vitals metric metadata/freshness | 2 | | POST | `/v1/connect/google-play/apps/:packageName/vitals/:metricSet/query` | Query Android vitals metric data | 2 | | GET | `/v1/connect/google-play/apps/:packageName/anomalies` | List vitals anomalies | 2 | | GET | `/v1/connect/google-play/apps/:packageName/subscriptions` | List subscription products | 2 | | GET | `/v1/connect/google-play/apps/:packageName/one-time-products` | List one-time products | 2 | | GET | `/v1/connect/google-play/apps/:packageName/tracks` | List release tracks via a temporary edit | 2 | | GET | `/v1/connect/google-play/apps/:packageName/tracks/:track` | Get a release track | 2 | | GET | `/v1/connect/google-play/apps/:packageName/tracks/:track/releases` | List releases on a track | 2 | | PATCH | `/v1/connect/google-play/apps/:packageName/tracks/:track` | Patch a release track; supports `validateOnly=true` | 2 | | GET | `/v1/connect/google-play/apps/:packageName/listings` | List store listing localizations | 2 | | GET | `/v1/connect/google-play/apps/:packageName/listings/:language` | Get localized store listing metadata | 2 | | PATCH | `/v1/connect/google-play/apps/:packageName/listings/:language` | Patch title, short/full description, or video; supports `validateOnly=true` | 2 | | GET | `/v1/connect/google-play/reports/objects` | List GCS report objects for sales, earnings, stats, store performance | 2 | | GET | `/v1/connect/google-play/reports/object` | Get one report object metadata | 2 | | GET | `/v1/connect/google-play/reports/download` | Download a report object | 2 | | POST | `/v1/connect/google-play/analytics/import-report` | Import one GCS stats CSV report into analytics tables | 2 | | POST | `/v1/connect/google-play/analytics/import-reports` | Bulk-import GCS stats reports by prefix | 2 | | POST | `/v1/connect/google-play/apps/:packageName/analytics/vitals-sync` | Sync crash/ANR vitals into analytics tables | 2 | | GET | `/v1/connect/google-play/analytics` | Google Play analytics overview across imported apps | 2 | | GET | `/v1/connect/google-play/apps/:packageName/analytics` | Google Play analytics overview for one app | 2 | | GET | `/v1/connect/google-play/apps/:packageName/analytics/sources` | Traffic source, country, and UTM breakdowns | 2 | | GET | `/v1/connect/google-play/apps/:packageName/analytics/search-terms` | Play Console search-term visitors, acquisitions, and conversion | 2 | ### Apple Search Ads Separate from App Store Connect. Connect with `POST /v1/connect/apple-ads/credentials` or pass `X-ASA-*` headers per request. Full guide: [Apple Search Ads](/docs/apple-search-ads). **Credentials & account** (0 credits) | Method | Endpoint | Description | Credits | | ------ | ------------------------------------------ | -------------------------------- | ------- | | POST | `/v1/connect/apple-ads/credentials` | Save & verify Search Ads API key | 0 | | GET | `/v1/connect/apple-ads/credentials/status` | Connection status | 0 | | DELETE | `/v1/connect/apple-ads/credentials` | Remove stored credentials | 0 | | GET | `/v1/connect/apple-ads/me` | Current API user | 2 | | GET | `/v1/connect/apple-ads/acls` | Accessible orgs | 2 | **Campaigns & ad groups** (2 credits) | Method | Endpoint | Description | Credits | | ------ | ----------------------------------------------------------------- | --------------------------------- | ------- | | GET | `/v1/connect/apple-ads/campaigns` | List campaigns | 2 | | PUT | `/v1/connect/apple-ads/campaigns/:campaignId` | Update status, name, daily budget | 2 | | GET | `/v1/connect/apple-ads/campaigns/:campaignId/adgroups` | List ad groups | 2 | | PUT | `/v1/connect/apple-ads/campaigns/:campaignId/adgroups/:adGroupId` | Update status, name, default bid | 2 | **Targeting keywords** (2 credits) | Method | Endpoint | Description | Credits | | ------ | -------------------------------------------------------------------- | --------------------- | ------- | | GET | `.../adgroups/:adGroupId/targetingkeywords` | List keywords | 2 | | GET | `.../targetingkeywords/:keywordId` | Get one keyword | 2 | | POST | `/v1/connect/apple-ads/campaigns/:campaignId/targetingkeywords/find` | Find across ad groups | 2 | | POST | `.../targetingkeywords/bulk` | Create keywords | 2 | | PUT | `.../targetingkeywords/bulk` | Update bids / status | 2 | | POST | `.../targetingkeywords/delete` | Delete keywords | 2 | | POST | `.../targetingkeywords/recommendations` | Keyword suggestions | 2 | | POST | `.../bid-recommendations` | Bid suggestions | 2 | **Negative keywords** (2 credits) — campaign and ad group: list, find, create, update, delete. See [Apple Search Ads](/docs/apple-search-ads#negative-keywords). **Reports** (2 credits) | Method | Endpoint | Description | Credits | | ------ | ----------------------------------------------------------------- | -------------------------- | ------- | | POST | `/v1/connect/apple-ads/campaigns/:campaignId/reports/keywords` | Keyword performance report | 2 | | POST | `/v1/connect/apple-ads/campaigns/:campaignId/reports/searchterms` | Search terms report | 2 | *** ## Response Format ### Success All successful responses wrap data in a `data` envelope: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "trackId": 1617391485, "trackName": "Block Blast!", "sellerName": "Hungry Studio", "primaryGenreName": "Games", "averageUserRating": 4.7, "price": 0 } } ``` ### Error Errors return an `error` object containing a machine-readable `code` and a human-readable `message`: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "error": { "code": "INVALID_APP_ID", "message": "App ID must be a positive integer" } } ``` *** ## Error Codes ### Client Errors (4xx) | Status | Code | Description | | ------ | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | 400 | `INVALID_APP_ID` | App ID is invalid for the platform — a positive integer for `apple`, or a package name (e.g. `com.example.app`) for `google` | | 400 | `INVALID_QUERY` | Search query is too short (min 2 characters) | | 400 | `INVALID_KEYWORD` | Keyword is missing or too short (min 2 characters) | | 400 | `INVALID_COUNTRY` | Country code is not a valid ISO 3166-1 alpha-2 code | | 400 | `INVALID_GENRE_ID` | Genre ID is not valid | | 400 | `INVALID_SORT` | Sort parameter is not one of the allowed values | | 400 | `MISSING_PARAMETER` | A required parameter is missing | | 401 | `MISSING_API_KEY` | No API key provided in request headers | | 401 | `INVALID_API_KEY` | API key is invalid, inactive, or revoked | | 404 | `APP_NOT_FOUND` | App does not exist or is unavailable in the specified country | | 404 | `KEYWORD_NOT_FOUND` | No ranking data available for this keyword | | 409 | `EMAIL_ALREADY_REGISTERED` | Email address already has an active API key | | 429 | `RATE_LIMIT_EXCEEDED` | Monthly credit limit has been reached | ### Server Errors (5xx) | Status | Code | Description | | ------ | --------------------- | ----------------------------------------------- | | 500 | `INTERNAL_ERROR` | Unexpected server error | | 500 | `DB_ERROR` | Database operation failed | | 502 | `UPSTREAM_ERROR` | Apple API or upstream service returned an error | | 503 | `SERVICE_UNAVAILABLE` | Service temporarily unavailable (maintenance) | | 504 | `UPSTREAM_TIMEOUT` | Apple API or upstream service timed out | All error responses include the `error.code` field, which is stable and safe to use for programmatic error handling. The `error.message` field is human-readable and may change. *** ## Pagination Endpoints that return lists support pagination via query parameters: | Parameter | Type | Default | Description | | --------- | ------- | ------- | ---------------------------------------------------- | | `limit` | integer | 25 | Number of results to return (max varies by endpoint) | | `offset` | integer | 0 | Number of results to skip | **Example:** ``` GET /v1/apps/1617391485/reviews?country=us&limit=10&offset=20 ``` Paginated responses include a `pagination` object when applicable: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "reviews": [ ... ], "pagination": { "limit": 10, "offset": 20, "total": 487, "hasMore": true } } } ``` | Field | Type | Description | | --------- | ------- | --------------------------------------- | | `limit` | integer | Number of results returned | | `offset` | integer | Current offset | | `total` | integer | Total number of results available | | `hasMore` | boolean | Whether there are more results to fetch | *** ## Country Codes Many endpoints accept a `country` query parameter using **ISO 3166-1 alpha-2** codes. If omitted, the default is `us`. ### Common Country Codes | Code | Country | Code | Country | | ---- | -------------- | ---- | ------------ | | `us` | United States | `jp` | Japan | | `gb` | United Kingdom | `kr` | South Korea | | `de` | Germany | `cn` | China | | `fr` | France | `br` | Brazil | | `it` | Italy | `in` | India | | `es` | Spain | `au` | Australia | | `ca` | Canada | `mx` | Mexico | | `nl` | Netherlands | `tr` | Turkey | | `se` | Sweden | `sa` | Saudi Arabia | | `ru` | Russia | `id` | Indonesia | The full list includes all countries where the App Store and Google Play are available (175+ storefronts). Use the two-letter ISO 3166-1 alpha-2 country code in lowercase. *** ## HTTP Status Codes | Status | Meaning | When | | ------ | --------------------- | ---------------------------------------------------------- | | `200` | OK | Request succeeded | | `201` | Created | Resource successfully created (e.g., API key registration) | | `400` | Bad Request | Invalid parameters, missing required fields | | `401` | Unauthorized | Missing or invalid API key | | `404` | Not Found | App, keyword, or resource not found | | `409` | Conflict | Duplicate resource (e.g., email already registered) | | `429` | Too Many Requests | Monthly credit limit exceeded | | `500` | Internal Server Error | Unexpected server failure | | `502` | Bad Gateway | Upstream service error | | `503` | Service Unavailable | Temporary maintenance | | `504` | Gateway Timeout | Upstream service timeout | *** ## Key Features * **App Metadata** — Complete iTunes Lookup data including description, screenshots, version, size, languages, and age rating * **In-App Purchases** — Subscription and IAP names with prices, sourced directly from the App Store product page * **Market Intelligence** — Estimated downloads and revenue using power-law models with confidence levels and ranges * **Similar Apps** — Competitors via 3-layer matching: keyword overlap, title search + genre filter, and same developer (dedicated endpoint) * **Category Charts** — Top Free, Top Paid, and Top Grossing per category with genre filtering * **Downloads to Top** — Estimated daily downloads needed to reach specific chart positions per category * **Keyword Intelligence** — Volume scoring, difficulty analysis, competitor overlap, autocomplete suggestions, and rank trends * **User Reviews** — Customer reviews from Apple RSS with sorting and pagination (up to 500 per country) * **App Search** — Keyword search or direct App ID lookup via the iTunes Search API * **Country Rankings** — Track an app's chart positions across multiple countries simultaneously * **Featured Apps** — App of the Day, Game of the Day, and curated editorial collections from the App Store Today tab * **Trending Keywords** — Keywords with the fastest-growing reach, ranked by growth percentage * **Market Movers** — Top gainers, losers, new entries, and apps that dropped out of charts, powered by periodic chart snapshots * **Discovery** — New releases, trending apps, and apps that just hit #1 in their category * **RevenueCat Integration** — Your own app's subscription analytics: MRR, revenue, active subscriptions, churn, trials, and 20+ chart types via RevenueCat's API * **Superwall Integration** — Paywall proceeds, MRR, conversion, transactions, and campaigns via the Superwall organization API * **App Store Connect Integration** — Manage your apps: list apps/versions, update metadata (description, keywords, what's new), download analytics reports (140+ types), and Sales and Trends reports # Platforms (App Store & Google Play) Source: https://docs.appeeky.com/docs/platforms Query the Apple App Store (iOS) and Google Play (Android) through the same API using the platform parameter Appeeky covers both the **Apple App Store** (iOS) and **Google Play** (Android). Most store-intelligence endpoints accept a `platform` query parameter so you can pull the same metadata, keyword, and competitor data for either store. `platform` defaults to `apple`, so existing iOS integrations keep working unchanged. Add `platform=google` to target Google Play. *** ## The `platform` parameter | Value | Store | Notes | | ------------------- | --------------- | ----------------------------------------- | | `apple` *(default)* | Apple App Store | Aliases: `app-store`, `ios`, `iphone` | | `google` | Google Play | Aliases: `play`, `google-play`, `android` | ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} # Apple (default) curl "https://api.appeeky.com/v1/keywords/metrics?keyword=music&country=us" \ -H "X-API-Key: YOUR_API_KEY" # Google Play curl "https://api.appeeky.com/v1/keywords/metrics?platform=google&keyword=music&country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` *** ## App identifiers differ by store | Platform | Identifier | Example | | -------- | -------------------------------- | ------------------- | | `apple` | Numeric App ID (iTunes track ID) | `1617391485` | | `google` | Package name | `com.spotify.music` | Endpoints that take an app `:id` validate it against the selected platform. Passing a numeric ID with `platform=google` (or a package name with `platform=apple`) returns `INVALID_APP_ID`. *** ## The `lang` parameter (Google Play) Google Play localizes content by language as well as country. Pass an optional `lang` (e.g. `en`, `tr`, `de`) on Google requests; it defaults to `en`. `lang` is ignored for Apple, which derives language from the storefront `country`. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/apps/com.spotify.music?platform=google&country=de&lang=de" \ -H "X-API-Key: YOUR_API_KEY" ``` The `country` parameter uses **ISO 3166-1 alpha-2** codes for both stores. *** ## Cross-platform endpoints These accept `platform=apple` or `platform=google`: | Group | Endpoints | | --------------- | ---------------------------------------------------------------------------------------------------------------------------- | | **Search** | `GET /v1/search` | | **Apps** | `GET /v1/apps/:id`, `/intelligence`, `/similar`, `/reviews`, `/country-rankings`, `/keywords`, `/keywords/trends` | | **Keywords** | `GET /v1/keywords/ranks`, `/suggestions`, `/metrics`, `/compare`, `/compare-cluster`, `/trending`, `POST /v1/keywords/track` | | **Screenshots** | `GET /v1/apps/:id/screenshots`, `/screenshots/competitors`, `GET /v1/categories/:id/top/screenshots` | | **Categories** | `GET /v1/categories/:id/top` | | **ASO** | `GET /v1/aso/apps/:id/audit`, `/opportunities` | *** ## Apple-only endpoints These rely on Apple-specific data sources or accounts and accept iOS apps only: * **App Store Connect** — your apps, versions, metadata, analytics, sales reports, reviews (Apple developer account) * **Apple Search Ads** — campaign management (Apple Ads account) * **AI Visibility** — AI-assistant recommendation tracking * **Advanced keyword insights** — `keywords/visibility`, `keywords/movers`, `keywords/gap`, `keywords/demand-trend`, `keywords/suggestions/history`, `keywords/suggestions/emerging`, `apps/:id/keyword-clusters`, `keywords/expand` * **ASO** — `aso/.../brief`, intent clusters, metadata suggest/validate, competitor report Google Play coverage for the advanced keyword-insight endpoints is on the roadmap. Until then, those endpoints return data for Apple apps only. *** ## Notes on Google Play metrics * **Volume, difficulty, and opportunity** scores are computed from Google Play-native signals (search results, ratings, review counts, autocomplete) using the same scoring core as Apple, so values are comparable across stores. * **Enrichment fields** (`rankVolatility`, `marketDominance`, `historicalDays`) are currently populated for Apple only. On Google these return `null` and `interpretation.trustworthy` is `false` until enough historical rank data accumulates. # Rate Limits & Credits Source: https://docs.appeeky.com/docs/rate-limits Credit-based rate limiting, plans, endpoint costs, and usage tracking The Appeeky API uses a **credit-based** rate limiting system. Each plan has monthly allowances for normal API work and, on eligible plans, dedicated AI Visibility and creative buckets. Every API call deducts credits from the bucket that matches the endpoint's cost profile. *** ## Plans | Plan | Plan ID | API credits | Creative credits | Price (monthly) | Price (yearly) | Best for | | -------------- | -------- | ----------: | ---------------: | --------------- | -------------- | -------------------------------------- | | **Indie** | `indie` | 5,000 | 10 | \$9/mo | \$90/yr | Solo developers and side projects | | **Starter** | `small` | 10,000 | 40 | \$19/mo | \$190/yr | Small applications and research | | **Growth** | `growth` | 50,000 | 120 | \$39/mo | \$390/yr | Scaling teams and production workloads | | **Pro** | `medium` | 100,000 | 250 | \$69/mo | \$690/yr | Production services | | **Enterprise** | `large` | 1,000,000 | 500 | \$149/mo | \$1,490/yr | High-volume commercial use | `X-Plan` and `/v1/auth/usage` report your **Plan ID** (`indie`, `small`, `growth`, `medium`, or `large`). Web subscription bundles use `web_indie`, `web_startup`, and `web_agentic_scale`; each includes 1,000 API credits plus its plan-specific creative quota. Need more credits? Contact us at **[support@appeeky.com](mailto:support@appeeky.com)** to upgrade your plan or discuss custom pricing for higher volumes. *** ## Credit Costs Per Endpoint Credits are charged based on the computational cost of each endpoint. Heavier endpoints that aggregate multiple data sources cost more. Dedicated buckets are used for AI Visibility (`credits.aiVisibility`) and creative generation (`credits.creative`). ### Free (0 credits) | Endpoint | Credits | Description | | ---------------- | ------- | ---------------------------------- | | `GET /v1/health` | 0 | Service health check — always free | ### Light (1 credit) | Endpoint | Credits | Description | | ------------------------------ | ------- | ------------------------------------- | | `GET /v1/search` | 1 | Search apps by keyword or App ID | | `POST /v1/keywords/track` | 1 | Add keyword to your tracking list | | `GET /v1/keywords/suggestions` | 1 | Apple Search autocomplete suggestions | | `GET /v1/categories` | 1 | List all App Store categories | ### Standard (2 credits) | Endpoint | Credits | Description | | ---------------------------------- | ------- | ----------------------------------- | | `GET /v1/categories/:id/top` | 2 | Top charts for a category | | `GET /v1/apps/:id` | 2 | Full app metadata (iTunes Lookup) | | `GET /v1/apps/:id/reviews` | 2 | User reviews (Apple RSS feed) | | `GET /v1/apps/:id/screenshots` | 2 | App screenshots by device type | | `GET /v1/keywords/ranks` | 2 | Apps ranking for a specific keyword | | `GET /v1/keywords/metrics` | 2 | Search volume and difficulty scores | | `GET /v1/apps/:id/keywords/trends` | 2 | Historical rank trend for a keyword | | `GET /v1/new-releases` | 2 | Recently released apps | | `GET /v1/discover` | 2 | Trending and noteworthy apps | | `GET /v1/discover/new-number-1` | 2 | Apps that just reached #1 | ### Heavy (3 credits) | Endpoint | Credits | Description | | ------------------------------------------ | ------- | -------------------------------------------- | | `GET /v1/apps/:id/keywords` | 3 | All keyword rankings for an app | | `GET /v1/keywords/compare` | 3 | Competitor keyword overlap analysis | | `GET /v1/keywords/visibility` | 3 | App visibility score across tracked keywords | | `GET /v1/keywords/movers` | 3 | Per-app keyword rank gainers / losers | | `GET /v1/apps/:id/country-rankings` | 3 | Chart positions across countries | | `GET /v1/apps/:id/screenshots/competitors` | 3 | Competitor screenshot comparison | | `GET /v1/categories/:id/top/screenshots` | 3 | Screenshots for top apps in a category | ### Advanced (4 credits) | Endpoint | Credits | Description | | ---------------------------------- | ------- | ---------------------------------------------- | | `GET /v1/keywords/compare-cluster` | 4 | Multi-competitor keyword lifecycle buckets | | `GET /v1/keywords/gap` | 4 | Top opportunity-ranked competitor keyword gaps | ### Premium (5 credits) | Endpoint | Credits | Description | | ------------------------------- | ------- | ------------------------------------------------------------- | | `GET /v1/apps/:id/intelligence` | 5 | Full intelligence report: revenue, downloads, IAPs, sentiment | ### Standard (2 credits) — continued | Endpoint | Credits | Description | | -------------------------- | ------- | --------------------------------------------- | | `GET /v1/apps/:id/similar` | 2 | Similar and competing apps (3-layer matching) | Credits are only deducted on **successful** responses (2xx status codes). Failed requests (4xx, 5xx) do **not** consume credits. ### Creative credits | Endpoint | Bucket | Credits | Description | | ------------------------------------------------------------------------------------- | -------- | -------------------------------: | ------------------------------------------------- | | `POST /v1/app-ad-creatives/generate` with `mode: "analyze"` or `generateImage: false` | API | 1 | Listing analysis and paste-ready copy only | | `POST /v1/app-ad-creatives/generate` with platform-funded image generation or editing | Creative | `low`: 1, `medium`: 2, `high`: 5 | Generate or edit one square image creative | | `POST /v1/app-ad-creatives/generate` with `X-OpenAI-Key` | API | 1 | BYOK image job; does not consume creative credits | *** ## Response Headers Every authenticated response includes rate limit headers so you can track consumption programmatically: | Header | Description | Example | | -------------------------- | --------------------------------------------------------------- | ------------ | | `X-RateLimit-Limit` | General API monthly credits for your plan | `5000` | | `X-RateLimit-Remaining` | General API credits remaining this month | `4995` | | `X-RateLimit-Reset` | Unix timestamp when credits reset (first of next month) | `1743465600` | | `X-Credit-Cost` | Credits consumed by this request | `2` | | `X-Credit-Category` | Bucket charged for this request | `general` | | `X-AiVisibility-Limit` | Monthly AI Visibility bucket limit | `3000` | | `X-AiVisibility-Remaining` | AI Visibility credits remaining when the route uses that bucket | `2991` | | `X-Creative-Limit` | Monthly creative bucket limit | `10` | | `X-Creative-Remaining` | Creative credits remaining when the route uses that bucket | `8` | | `X-Plan` | Your current plan identifier | `indie` | **Example response headers:** ``` HTTP/2 200 Content-Type: application/json X-RateLimit-Limit: 5000 X-RateLimit-Remaining: 4993 X-RateLimit-Reset: 1743465600 X-Credit-Cost: 2 X-Credit-Category: general X-Creative-Limit: 10 X-Plan: indie ``` Use `X-RateLimit-Remaining` in your code to implement graceful throttling before hitting the limit. *** ## Check Your Usage Use the usage endpoint to see a detailed breakdown of your current month's consumption: ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/auth/usage" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch("https://api.appeeky.com/v1/auth/usage", { headers: { "X-API-Key": "YOUR_API_KEY" }, }); const { data } = await res.json(); console.log(`Plan: ${data.plan}`); console.log(`Used: ${data.used} / ${data.monthlyCredits}`); console.log(`Remaining: ${data.remaining}`); console.log(`Resets: ${data.resetDate}`); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/auth/usage", headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] print(f'Plan: {data["plan"]}') print(f'Used: {data["used"]} / {data["monthlyCredits"]}') print(f'Remaining: {data["remaining"]}') print(f'Resets: {data["resetDate"]}') ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "plan": "indie", "monthlyCredits": 5000, "used": 127, "remaining": 4873, "resetDate": "2026-03-01T00:00:00.000Z", "credits": { "general": { "limit": 5000, "used": 127, "remaining": 4873 }, "aiVisibility": { "limit": 3000, "used": 0, "remaining": 3000 }, "creative": { "limit": 10, "used": 0, "remaining": 10 }, "resetDate": "2026-03-01T00:00:00.000Z" }, "usageByEndpoint": [ { "endpoint": "GET /apps/:id", "totalCredits": 40, "requestCount": 20 }, { "endpoint": "GET /apps/:id/intelligence", "totalCredits": 50, "requestCount": 10 }, { "endpoint": "GET /keywords/ranks", "totalCredits": 20, "requestCount": 10 }, { "endpoint": "GET /search", "totalCredits": 12, "requestCount": 12 }, { "endpoint": "GET /apps/:id/keywords", "totalCredits": 3, "requestCount": 1 }, { "endpoint": "POST /keywords/track", "totalCredits": 2, "requestCount": 2 } ] } } ``` | Field | Type | Description | | ---------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------- | | `plan` | string | Current plan id: `indie`, `small`, `growth`, `medium`, `large`, or web bundles `web_indie` / `web_startup` / `web_agentic_scale` | | `monthlyCredits` | number | General API credits for the billing period | | `used` | number | Total credits consumed across all buckets, kept for backward compatibility | | `remaining` | number | General API credits left until reset | | `credits.general` | object | General API bucket limit, used, and remaining | | `credits.aiVisibility` | object | Dedicated AI Visibility bucket limit, used, and remaining | | `credits.creative` | object | Dedicated creative bucket limit, used, and remaining | | `resetDate` | string | ISO 8601 date when credits reset | | `usageByEndpoint` | array | Breakdown by endpoint with credit totals and request counts | *** ## When the Limit Is Reached When your monthly credits are exhausted, all credit-consuming endpoints return **429 Too Many Requests**: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Monthly credit limit reached (5000/5000). Upgrade plan for more credits." } } ``` The response still includes rate limit headers so you can check the reset time: ``` HTTP/2 429 X-RateLimit-Limit: 5000 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1743465600 X-Plan: indie ``` The `/v1/health` endpoint (0 credits) always works, even when your credit limit is exhausted. Use it to verify the service is up before investigating 429 errors. *** ## Monthly Reset Credits reset automatically on the **first day of each calendar month** at **00:00 UTC**. Unused credits do **not** carry over to the next month. | Event | Timing | | ------------- | ----------------------------------------------------- | | Credit reset | 1st of each month, 00:00 UTC | | Carry-over | No — unused credits expire | | Billing cycle | Calendar month | | Plan changes | Take effect immediately; new credit allowance applies | Check your `X-RateLimit-Reset` response header or the `resetDate` field from `/v1/auth/usage` to see the exact reset timestamp for your current billing period. # Reddit account warmup Source: https://docs.appeeky.com/docs/reddit-account-warmup Build Reddit account trust before posting via Growth Channels so API replies and scheduled posts are not removed by spam filters. Reddit applies **sitewide spam filters** on top of each subreddit's rules. Posts and comments sent through the Reddit API (including Appeeky's Composio integration) are judged more harshly than content typed in the browser. If you see **"Sorry, this post was removed by Reddit's filters"**, the publish succeeded technically — Reddit's automation removed it afterward. This is almost always an **account-trust** problem, not an Appeeky bug. ## What Reddit looks at | Signal | Why it matters | | ---------------------- | ------------------------------------------------------------------------------------------ | | Account age | Brand-new accounts are heavily filtered | | Karma (comment + post) | Low or zero karma = high removal rate | | Sub history | First-ever post in a sub is riskier than joining conversations first | | API / OAuth posting | Third-party apps are a spam vector; Reddit scores them stricter | | Velocity | Multiple posts/comments in a short window (you may also hit rate limits) | | Content shape | Survey-style titles, multiple questions, "would love to hear…" patterns look like lead-gen | ## Recommended warmup timeline ### Week 1 — Lurk and comment manually 1. Join your target subreddits in the Reddit app or website (not only via Appeeky). 2. Leave **5–10 genuine comments** on other people's threads — help, no product mention. 3. Aim for **50+ comment karma** before any product-adjacent reply. ### Week 2 — Replies via Growth Inbox 1. Use **Growth Inbox → Must reply / Worth a look** for comment replies only. 2. Prefer threads where you add real value; approve drafts that sound like a human founder, not marketing. 3. Space replies: **at most 1–2 per day** on a new account, across different threads. 4. Use **Post now** sparingly; if a reply fails with rate limit, wait 24h. ### Week 3+ — Standalone posts 1. Only schedule **original posts** after you have comment history **in that subreddit**. 2. Lead with a personal story or data point, not a multi-question survey. 3. Avoid titles like *"Is X still worth it in 2026?"* with three bullet questions — Reddit often flags these as engagement bait. 4. If a post is removed, message the sub mods (politely) and ask if they can approve it; some sit in mod queue rather than being permanently spam-banned. ## Content patterns to avoid (especially on new accounts) * Generic market-research posts with stacked questions * Posting the same draft to multiple subs the same day * Link posts or App Store URLs before the account is established * Copy-paste replies across threads * Posting immediately after connecting Reddit OAuth ## Content patterns that work better * **Comment replies** that answer the OP's specific situation * One clear question in the title, body focused on your experience * Disclosure when mentioning your product (*"full disclosure: I built X"*) * Participation in daily threads or weekly megathreads where the sub allows it ## Appeeky-specific notes * **Growth Inbox sidebar → Account health** shows karma, account age, today's API usage, and a `cold` / `warming` / `ready` readiness badge. Use it before scheduling posts. * **Inbox cleanup** (banner when 100+ pending) bulk-dismisses low-urgency and below-threshold rows so you can focus on high-intent threads. * **Inbox replies** are lower risk than **scheduled standalone posts** — start with inbox. * The **reply linter** flags generic openers and missing disclosure; fix warnings before posting. * Unknown subreddits default to **strict** promo rules in the drafter — add high-traffic subs to your project only after reading their rules wiki. * Reddit **rate limits** are enforced by Reddit, not Appeeky. Rapid approve → post → approve cycles will trigger `RATELIMIT` errors. ## If a post was removed 1. Check whether it appears in your profile — if yes, it was likely sitewide spam, not a sub ban. 2. Do **not** immediately repost the same text; edit tone and wait. 3. Build karma with neutral comments in that sub for a few days. 4. Contact sub moderators if you believe the post was on-topic. 5. For repeated removals, warm up on smaller niche subs first (e.g. r/test for API smoke tests only). ## Checklist before first scheduled post * [ ] Reddit account older than 7 days (14+ preferred) * [ ] 50+ comment karma * [ ] At least 3–5 comments in the target subreddit (manual or via inbox) * [ ] No more than one API post that day * [ ] Title + body reviewed — not survey / engagement-bait shaped * [ ] Subreddit rules read (promo, flair, link policies) ## Related * [Growth Channels overview](/docs/growth-channels) — multi-channel model and shared API * [Reddit Growth](/docs/growth-channels-reddit) — REST endpoints, inbox workflow, scheduling * [MCP Growth tools](/docs/mcp#growth-channels-reddit-lead-generation) — agent-facing tool reference # RevenueCat Attribution Source: https://docs.appeeky.com/docs/revenuecat-attribution Read Apple Search Ads attribution from RevenueCat customer attributes and aggregate revenue by campaign, keyword, and country When Apple AdServices is enabled in RevenueCat, attributed installs write reserved customer attributes such as media source, campaign, ad group, and keyword. Appeeky reads these attributes through the RevenueCat API so you can tie subscription revenue back to paid acquisition — without exporting files manually. Use this alongside [Apple Search Ads Profitability](/docs/apple-search-ads-profitability), which joins spend from Apple with revenue from RevenueCat charts. Attribution endpoints complement that view with **customer-level** dimensions, including country. *** ## What This Answers * Which keywords drove paying customers, not just chart-level revenue segments? * How does attributed revenue break down by campaign and country? * Is Apple Search Ads attribution actually flowing into RevenueCat? Customer attributes are fetched per customer. The summary endpoint samples recent customers for responsiveness. For large-scale historical analysis, RevenueCat Scheduled Data Exports remain the right tool inside RevenueCat; Appeeky focuses on live optimization workflows. *** ## Prerequisites 1. An Appeeky API key. 2. A RevenueCat **secret** API key (`sk_…`) via `X-RC-Key`. 3. **Apple AdServices** enabled in RevenueCat → Integrations. 4. Installs with attribution data — attributes appear after attributed users are recorded. Optional: `X-RC-Project` when your API key accesses multiple RevenueCat projects. *** ## Attribution Summary Aggregates a sample of customers who have attribution attributes, grouped by media source, campaign, keyword, and customer country. Includes total attributed revenue per group. ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/revenuecat/attribution-summary ``` The same endpoint is also available at: ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/revenuecat/attribution-summary ``` Both paths behave identically. Connect-prefixed routes work with the Appeeky Connect client and dashboard proxy. ### Query parameters | Parameter | Default | Description | | --------- | ------- | ------------------------------- | | `limit` | `50` | Max customers to sample (1–100) | ### Example ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/revenuecat/attribution-summary?limit=50" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "X-RC-Key: sk_YOUR_REVENUECAT_SECRET_KEY" \ -H "X-RC-Project: proj_YOUR_PROJECT_ID" ``` ### Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "sampleSize": 50, "customersWithAttribution": 14, "appleSearchAdsAttributed": 9, "attributionEnabled": true, "rows": [ { "mediaSource": "Apple Search Ads", "campaign": "US Competitors", "adGroup": "Exact match", "keyword": "rival app", "country": "US", "customerCount": 4, "totalRevenue": 63.96 } ] } } ``` ### Fields | Field | Description | | -------------------------- | ------------------------------------------------------- | | `sampleSize` | Customers scanned in this request | | `customersWithAttribution` | Customers with at least one attribution attribute | | `appleSearchAdsAttributed` | Customers whose media source indicates Apple Search Ads | | `attributionEnabled` | Whether any attribution data was found in the sample | | `rows` | Aggregated groups sorted by `totalRevenue` descending | ### Row dimensions | Field | RevenueCat attribute | | ------------- | -------------------------------------------- | | `mediaSource` | `$mediaSource` | | `campaign` | `$campaign` | | `adGroup` | `$adGroup` | | `keyword` | `$keyword` | | `country` | Customer's last-seen country from RevenueCat | Revenue per row is summed from each customer's subscription proceeds in USD. *** ## Customer Attributes Read the full attribute list for a single RevenueCat customer, including parsed attribution fields. ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/connect/revenuecat/customers/:customerId/attributes ``` Also available at: ```http theme={"theme":{"light":"github-light","dark":"github-dark"}} GET /v1/revenuecat/customers/:customerId/attributes ``` ### Example ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/revenuecat/customers/cust_abc123/attributes" \ -H "X-API-Key: YOUR_APPEEKY_KEY" \ -H "X-RC-Key: sk_YOUR_REVENUECAT_SECRET_KEY" ``` ### Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "customerId": "cust_abc123", "attributes": [ { "name": "$mediaSource", "value": "Apple Search Ads", "updatedAt": 1719590400 }, { "name": "$campaign", "value": "US Competitors", "updatedAt": 1719590400 }, { "name": "$keyword", "value": "rival app", "updatedAt": 1719590400 } ], "attribution": { "mediaSource": "Apple Search Ads", "campaign": "US Competitors", "adGroup": null, "ad": null, "keyword": "rival app", "creative": null } } } ``` ### Reserved attribution keys | Key | Parsed field | | -------------- | ------------- | | `$mediaSource` | `mediaSource` | | `$campaign` | `campaign` | | `$adGroup` | `adGroup` | | `$ad` | `ad` | | `$keyword` | `keyword` | | `$creative` | `creative` | Other custom attributes on the customer are returned in `attributes` but not mapped into `attribution`. *** ## How This Fits With Profitability | View | Source | Granularity | Best for | | ----------------------------------------------------- | ------------------------------------- | ------------------------------------- | ------------------------------------------------------------------ | | [Profitability](/docs/apple-search-ads-profitability) | Apple Ads reports + RevenueCat charts | Keyword / campaign / ad group | Spend, ROAS, and rule-based insights in one table | | Attribution summary | RevenueCat customer API | Keyword × campaign × country (sample) | Confirm attribution is live; see revenue by acquisition dimensions | | [ROAS workflow](/docs/apple-search-ads-roas-workflow) | Bundles both + review gate | End-to-end recommendations | Weekly optimization review | Chart-based profitability matches revenue to segment names from RevenueCat. Customer attributes expose the same acquisition dimensions at the user level, which is useful when chart segments lag or you need country alongside keyword. *** ## MCP Tools | Tool | Description | | ------------------------ | ----------------------------------------- | | `rc_attribution_summary` | Sample and aggregate attributed customers | | `rc_customer_attributes` | Full attribute list for one customer | See [MCP](/docs/mcp) for parameter details. *** ## Errors | Status | Code | When | | ------ | ------------------- | -------------------------------------------- | | 400 | `MISSING_RC_KEY` | `X-RC-Key` not provided | | 400 | `MULTIPLE_PROJECTS` | Multiple RC projects; specify `X-RC-Project` | | 401 | `UNAUTHORIZED` | Invalid RevenueCat API key | | 404 | `NO_PROJECTS` | No projects for this key | *** ## Related Docs * [RevenueCat Overview](/docs/revenuecat-overview) * [RevenueCat Charts](/docs/revenuecat-charts) * [Apple Search Ads Profitability](/docs/apple-search-ads-profitability) * [Apple Search Ads ROAS Workflow](/docs/apple-search-ads-roas-workflow) # RevenueCat Chart Options Source: https://docs.appeeky.com/docs/revenuecat-chart-options Discover available resolutions, segments, and filters for any RevenueCat chart ``` GET /v1/revenuecat/charts/:chartName/options ``` Returns the available configuration options for a specific chart — resolutions, segments, and filters. Use this before calling the [Chart Data](/docs/revenuecat-charts) endpoint to discover valid parameter values. Requires a **RevenueCat secret API key** (`sk_xxx`). This is a lightweight metadata call (1 credit). ## Path Parameters | Name | Type | Required | Description | | --------- | ------ | -------- | ----------------------------------------------------------------------------------- | | chartName | string | Yes | Chart identifier (see [Available Charts](/docs/revenuecat-charts#available-charts)) | ## Headers | Name | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------------------------- | | X-RC-Key | string | Yes | Your RevenueCat **secret** API key (starts with `sk_`) | | X-RC-Project | string | No | RevenueCat project ID. Auto-detected if you have one project. | ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/revenuecat/charts/revenue/options" \ -H "X-API-Key: YOUR_API_KEY" \ -H "X-RC-Key: sk_YOUR_REVENUECAT_SECRET_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/revenuecat/charts/revenue/options", { headers: { "X-API-Key": "YOUR_API_KEY", "X-RC-Key": "sk_YOUR_REVENUECAT_SECRET_KEY", }, } ); const data = await response.json(); console.log(data); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/revenuecat/charts/revenue/options", headers={ "X-API-Key": "YOUR_API_KEY", "X-RC-Key": "sk_YOUR_REVENUECAT_SECRET_KEY", }, ) data = response.json() print(data) ``` ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "resolutions": [ { "id": "day", "display_name": "Daily" }, { "id": "week", "display_name": "Weekly" }, { "id": "month", "display_name": "Monthly" }, { "id": "quarter", "display_name": "Quarterly" }, { "id": "year", "display_name": "Yearly" } ], "segments": [ { "id": "app", "display_name": "App" }, { "id": "country", "display_name": "Country" }, { "id": "product_identifier", "display_name": "Product" }, { "id": "store", "display_name": "Store" }, { "id": "offering", "display_name": "Offering" } ], "filters": [ { "id": "app", "display_name": "App", "values": [ { "id": "app8845704621", "display_name": "My App (App Store)" } ] }, { "id": "country", "display_name": "Country", "values": [ { "id": "US", "display_name": "United States" }, { "id": "GB", "display_name": "United Kingdom" } ] } ] } } ``` *** ## Response Fields ### Resolutions Available time granularity options for the chart's `resolution` parameter. | Field | Type | Description | | ------------- | ------ | ------------------------------------- | | id | string | Resolution identifier (pass to chart) | | display\_name | string | Human-readable name | ### Segments Available dimensions to segment chart data by. Pass the `id` as the `segment` query parameter. | Field | Type | Description | | ------------- | ------ | ---------------------------------- | | id | string | Segment identifier (pass to chart) | | display\_name | string | Human-readable name | ### Filters Available filter dimensions and their possible values. | Field | Type | Description | | ------------- | ------ | -------------------------------- | | id | string | Filter dimension identifier | | display\_name | string | Human-readable name | | values | array | Available values for this filter | Each filter value: | Field | Type | Description | | ------------- | ------ | ------------------------------------- | | id | string | Value identifier (use in filter JSON) | | display\_name | string | Human-readable name | Use the options response to build dynamic filter UIs or validate parameters before requesting chart data. The available options vary per chart — for example, the `churn` chart may support different segments than the `revenue` chart. *** ## Errors | Status | Code | When | | ------ | -------------------- | ------------------------------------------ | | 400 | MISSING\_RC\_KEY | `X-RC-Key` header not provided | | 400 | INVALID\_CHART\_NAME | Chart name is not one of the valid options | | 401 | UNAUTHORIZED | Invalid RevenueCat API key | | 404 | RESOURCE\_MISSING | Project not found or Charts not enabled | # RevenueCat Chart Data Source: https://docs.appeeky.com/docs/revenuecat-charts Time-series subscription analytics — revenue, MRR, churn, active subscribers, trials, and more ``` GET /v1/revenuecat/charts/:chartName ``` Returns time-series chart data from RevenueCat for a specific metric. Supports date ranges, resolution control, segmentation, and filtering. This is the same data that powers the RevenueCat dashboard charts. Requires a **RevenueCat secret API key** (`sk_xxx`) and the **Pro plan** (free up to \$2,500 MTR). Use the [Chart Options](/docs/revenuecat-chart-options) endpoint to discover available resolutions, segments, and filters for each chart. ## Path Parameters | Name | Type | Required | Description | | --------- | ------ | -------- | ------------------------------------------------------------------ | | chartName | string | Yes | Chart identifier (see [Available Charts](#available-charts) below) | ## Headers | Name | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------------------------- | | X-RC-Key | string | Yes | Your RevenueCat **secret** API key (starts with `sk_`) | | X-RC-Project | string | No | RevenueCat project ID. Auto-detected if you have one project. | ## Query Parameters | Name | Type | Default | Description | | ----------- | ------ | ----------- | ------------------------------------------------------------------ | | start\_date | string | 28 days ago | Start date in ISO 8601 format (e.g. `2025-01-01`) | | end\_date | string | today | End date in ISO 8601 format (e.g. `2026-03-08`) | | resolution | string | varies | Time resolution: `day`, `week`, `month`, `quarter`, `year` | | segment | string | — | Segment dimension (use chart options to discover available ones) | | filters | string | — | JSON array of filter objects | | selectors | string | — | JSON object of chart selectors | | currency | string | `USD` | ISO 4217 currency code | | aggregate | string | — | `average`, `total`, or both — returns summary only, no time-series | ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/revenuecat/charts/revenue?start_date=2025-06-01&end_date=2026-03-08&resolution=month" \ -H "X-API-Key: YOUR_API_KEY" \ -H "X-RC-Key: sk_YOUR_REVENUECAT_SECRET_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/revenuecat/charts/revenue?start_date=2025-06-01&end_date=2026-03-08&resolution=month", { headers: { "X-API-Key": "YOUR_API_KEY", "X-RC-Key": "sk_YOUR_REVENUECAT_SECRET_KEY", }, } ); const data = await response.json(); console.log(data); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/revenuecat/charts/revenue", params={ "start_date": "2025-06-01", "end_date": "2026-03-08", "resolution": "month", }, headers={ "X-API-Key": "YOUR_API_KEY", "X-RC-Key": "sk_YOUR_REVENUECAT_SECRET_KEY", }, ) data = response.json() print(data) ``` ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "category": "revenue", "display_name": "Revenue", "display_type": "area", "description": "Revenue displays the revenue generated during a period...", "start_date": 1751241600, "end_date": 1774915200, "last_computed_at": 1772985539587, "resolution": "month", "segments": [ { "chartable": true, "decimal_precision": 0, "description": "The total revenue generated in a given period, minus refunds.", "display_name": "Revenue", "tabulable": true, "unit": "$" }, { "chartable": false, "decimal_precision": 0, "description": "The count of revenue generating transactions for the period.", "display_name": "Transactions", "tabulable": true, "unit": "#" } ], "values": [ [1751241600, 0.0, 0.0], [1753920000, 0.0, 0.0], [1756598400, 0.0, 0.0], [1759276800, 0.0, 0.0], [1761955200, 0.0, 0.0], [1764547200, 0.0, 0.0], [1767225600, 11.6, 1.0], [1769904000, 0.0, 0.0], [1772323200, 0.0, 0.0], [1774915200, 0.0, 0.0] ], "summary": { "average": { "Revenue": 1.16, "Transactions": 0.1 }, "total": { "Revenue": 11.6, "Transactions": 1.0 } } } } ``` *** ## Response Fields | Field | Type | Description | | ------------------ | -------- | ---------------------------------------------------- | | category | string | Chart category (e.g. `revenue`, `mrr`, `churn`) | | display\_name | string | Human-readable chart name | | display\_type | string | Chart visualization type (`area`, `bar`, `line`) | | description | string | Detailed description of what the chart shows | | start\_date | number | Unix timestamp of the data range start | | end\_date | number | Unix timestamp of the data range end | | last\_computed\_at | number | Unix timestamp of when data was last computed | | resolution | string | Time resolution used (`day`, `week`, `month`, etc.) | | segments | array | Metadata for each data column in `values` | | values | array\[] | Time-series data: `[timestamp, value1, value2, ...]` | | summary | object | Aggregated statistics (`average`, `total`) | ### Values Array Each entry in `values` is an array where: * Index `0` = Unix timestamp for the period * Index `1+` = Values corresponding to each segment in order For example, with the Revenue chart: * `[1767225600, 11.6, 1.0]` means: at timestamp `1767225600` → Revenue = \$11.60, Transactions = 1 *** ## Available Charts | Chart Name | Description | | ---------------------------- | ----------------------------------------------- | | `revenue` | Total revenue generated per period | | `mrr` | Monthly Recurring Revenue over time | | `mrr_movement` | MRR changes: new, expansion, contraction, churn | | `arr` | Annual Recurring Revenue | | `actives` | Active subscriptions over time | | `actives_movement` | Active subscription changes | | `actives_new` | New active subscriptions per period | | `churn` | Churn rate and churned subscribers | | `trials` | Active trials over time | | `trials_movement` | Trial starts, conversions, and expirations | | `trial_conversion` | Trial-to-paid conversion rate | | `conversion_to_paying` | Overall conversion to paying customers | | `customers_new` | New customers per period | | `subscription_status` | Breakdown by subscription status | | `subscription_retention` | Cohort-based subscription retention | | `refund_rate` | Refund rate over time | | `ltv_per_customer` | Lifetime value per customer | | `ltv_per_paying_customer` | Lifetime value per paying customer | | `cohort_explorer` | Cohort analysis explorer | | `non_subscription_purchases` | Non-subscription purchase revenue | *** ## Errors | Status | Code | When | | ------ | -------------------- | ---------------------------------------------- | | 400 | MISSING\_RC\_KEY | `X-RC-Key` header not provided | | 400 | INVALID\_CHART\_NAME | Chart name is not one of the valid options | | 400 | PARAMETER\_ERROR | Invalid query parameter (e.g. bad date format) | | 401 | UNAUTHORIZED | Invalid RevenueCat API key | | 404 | RESOURCE\_MISSING | Project or app not found / Charts not enabled | # RevenueCat Overview Source: https://docs.appeeky.com/docs/revenuecat-overview Real-time subscription metrics — MRR, revenue, active subscriptions, trials, and customer counts ``` GET /v1/revenuecat/overview ``` Returns a snapshot of your app's key subscription metrics from RevenueCat, including MRR, revenue (last 28 days), active subscriptions, active trials, new customers, and active users. This is the fastest way to get a high-level view of your subscription business. This endpoint proxies to the [RevenueCat Charts & Metrics API](https://www.revenuecat.com/docs/api-v2#tag/Charts-and-Metrics). You need a **RevenueCat secret API key** (`sk_xxx`) to use it. Charts require the **Pro plan** (free up to \$2,500 MTR). ## Headers | Name | Type | Required | Description | | ------------ | ------ | -------- | ----------------------------------------------------------------------------------- | | X-RC-Key | string | Yes | Your RevenueCat **secret** API key (starts with `sk_`) | | X-RC-Project | string | No | RevenueCat project ID (e.g. `proj28fad9ba`). Auto-detected if you have one project. | ## Query Parameters | Name | Type | Default | Description | | -------- | ------ | ------- | ------------------------------------------------- | | currency | string | `USD` | ISO 4217 currency code (e.g. `USD`, `EUR`, `GBP`) | ## Code Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/revenuecat/overview" \ -H "X-API-Key: YOUR_API_KEY" \ -H "X-RC-Key: sk_YOUR_REVENUECAT_SECRET_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/revenuecat/overview", { headers: { "X-API-Key": "YOUR_API_KEY", "X-RC-Key": "sk_YOUR_REVENUECAT_SECRET_KEY", }, } ); const data = await response.json(); console.log(data); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/revenuecat/overview", headers={ "X-API-Key": "YOUR_API_KEY", "X-RC-Key": "sk_YOUR_REVENUECAT_SECRET_KEY", }, ) data = response.json() print(data) ``` ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "metrics": [ { "id": "active_trials", "name": "Active Trials", "description": "In total", "unit": "#", "period": "P0D", "value": 0, "last_updated_at": null, "last_updated_at_iso8601": null }, { "id": "active_subscriptions", "name": "Active Subscriptions", "description": "In total", "unit": "#", "period": "P0D", "value": 1, "last_updated_at": null, "last_updated_at_iso8601": null }, { "id": "mrr", "name": "MRR", "description": "Monthly Recurring Revenue", "unit": "$", "period": "P28D", "value": 11, "last_updated_at": null, "last_updated_at_iso8601": null }, { "id": "revenue", "name": "Revenue", "description": "Last 28 days", "unit": "$", "period": "P28D", "value": 11, "last_updated_at": null, "last_updated_at_iso8601": null }, { "id": "new_customers", "name": "New Customers", "description": "Last 28 days", "unit": "#", "period": "P28D", "value": 67, "last_updated_at": null, "last_updated_at_iso8601": null }, { "id": "active_users", "name": "Active Users", "description": "Last 28 days", "unit": "#", "period": "P28D", "value": 67, "last_updated_at": null, "last_updated_at_iso8601": null } ] } } ``` The response includes metrics for multiple currencies (e.g. `mrr_eur`, `revenue_gbp`). Only the primary currency metrics are shown above — the full response includes all currency variants. *** ## Metric Object | Field | Type | Description | | -------------------------- | -------------- | ------------------------------------------------------------------- | | id | string | Metric identifier (e.g. `mrr`, `active_subscriptions`) | | name | string | Human-readable name | | description | string | Short description of the metric | | unit | string | `$` for currency, `#` for count | | period | string | ISO 8601 duration — `P0D` = current snapshot, `P28D` = last 28 days | | value | number | Metric value | | last\_updated\_at | number \| null | Unix timestamp of last update | | last\_updated\_at\_iso8601 | string \| null | ISO 8601 timestamp of last update | ### Key Metrics | Metric ID | Description | | ---------------------- | ----------------------------------- | | `active_trials` | Currently active free trials | | `active_subscriptions` | Currently active paid subscriptions | | `mrr` | Monthly Recurring Revenue (USD) | | `revenue` | Total revenue in last 28 days (USD) | | `new_customers` | New customers in last 28 days | | `active_users` | Active users in last 28 days | *** ## X-RC-Project Header If your RevenueCat account has **one project**, the project is auto-detected from your API key — no `X-RC-Project` header needed. If you have **multiple projects**, omitting the header returns an error listing your available projects: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "error": { "code": "MULTIPLE_PROJECTS", "message": "Multiple projects found: My App (projABC123), Other App (projDEF456). Please specify X-RC-Project header." } } ``` *** ## Errors | Status | Code | When | | ------ | ------------------ | ----------------------------------------------------- | | 400 | MISSING\_RC\_KEY | `X-RC-Key` header not provided | | 400 | MULTIPLE\_PROJECTS | Multiple projects found, `X-RC-Project` not specified | | 401 | UNAUTHORIZED | Invalid RevenueCat API key | | 404 | NO\_PROJECTS | No projects found for this API key | *** ## Attribution Customer-level Apple Search Ads dimensions (media source, campaign, keyword) and aggregated revenue by country are available through [RevenueCat Attribution](/docs/revenuecat-attribution). | Endpoint | Description | | ------------------------------------------------------------- | ----------------------------------------- | | `GET /v1/connect/revenuecat/attribution-summary` | Sample and aggregate attributed customers | | `GET /v1/connect/revenuecat/customers/:customerId/attributes` | Full attribute list for one customer | Pair with [Apple Search Ads ROAS Workflow](/docs/apple-search-ads-roas-workflow) for end-to-end paid acquisition optimization. # Search Apps Source: https://docs.appeeky.com/docs/search Search apps by keyword or look up by App ID ``` GET /v1/search ``` Search apps by keyword. On the Apple App Store, a numeric query performs a **direct App ID lookup** instead of a keyword search. Pass `platform=google` to search Google Play instead. ## Query Parameters | Name | Type | Default | Required | Description | | -------- | ------ | ------- | -------- | -------------------------------------------------------------------------------- | | q | string | — | Yes | Search query (minimum 2 characters) or numeric App ID (Apple) | | platform | string | `apple` | No | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | `us` | No | ISO country code (e.g. `us`, `gb`, `de`) | | lang | string | `en` | No | Google Play language code (used when `platform=google`) | | limit | number | `20` | No | Max results to return (1–50, capped at **50**) | The `limit` parameter is capped at **50**. Any value above 50 will be silently reduced to 50. The minimum query length is 2 characters — shorter queries will return a `400` error. ## Code Examples ### Keyword Search ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/search?q=puzzle%20games&country=us&limit=20" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/search?q=puzzle%20games&country=us&limit=20", { headers: { "X-API-Key": "YOUR_API_KEY", }, } ); const data = await response.json(); console.log(data); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/search", params={"q": "puzzle games", "country": "us", "limit": 20}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = response.json() print(data) ``` ### Google Play Search ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/search?q=puzzle%20games&platform=google&country=us&lang=en" \ -H "X-API-Key: YOUR_API_KEY" ``` For Google Play, results use package names (e.g. `com.king.candycrushsaga`) as the app `id`. ### Numeric App ID Lookup ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X GET "https://api.appeeky.com/v1/search?q=1617391485" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const response = await fetch( "https://api.appeeky.com/v1/search?q=1617391485", { headers: { "X-API-Key": "YOUR_API_KEY", }, } ); const data = await response.json(); console.log(data); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests response = requests.get( "https://api.appeeky.com/v1/search", params={"q": "1617391485"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = response.json() print(data) ``` ## Response — Keyword Search ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "apps": [ { "id": "1617391485", "title": "Block Blast!", "developer": "Hungry Studio", "iconUrl": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../512x512bb.jpg" }, { "id": "544007664", "title": "Candy Crush Saga", "developer": "King", "iconUrl": "https://is1-ssl.mzstatic.com/image/thumb/Purple221/v4/.../512x512bb.jpg" }, { "id": "1040639975", "title": "Hexa Sort", "developer": "IEC Global Pty Ltd", "iconUrl": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../512x512bb.jpg" } ] } } ``` ## Response — Numeric App ID Lookup When `q` is a numeric string, the API performs a direct iTunes Lookup and returns the single matching app: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "apps": [ { "id": "1617391485", "title": "Block Blast!", "developer": "Hungry Studio", "iconUrl": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/.../512x512bb.jpg" } ] } } ``` ## App Object | Field | Type | Description | | --------- | ------ | -------------------- | | id | string | Apple App ID | | title | string | App name | | developer | string | Developer name | | iconUrl | string | App icon URL (512px) | **Numeric queries**: When `q` is a number (e.g. `1617391485`), the API bypasses the iTunes Search API and performs a **direct iTunes Lookup** by App ID. This returns exactly one result and is faster and more reliable than keyword searching when you already know the App ID. ## Errors | Status | Code | When | | ------ | ----------------- | ------------------------------- | | 400 | INVALID\_QUERY | Query shorter than 2 characters | | 400 | INVALID\_LIMIT | Limit is not a valid number | | 401 | MISSING\_API\_KEY | No API key in the request | | 401 | INVALID\_API\_KEY | Invalid or expired API key | # AI Agent Skills Source: https://docs.appeeky.com/docs/skills Pre-built ASO & app marketing skills for Cursor, Claude Code, and any Agent Skills-compatible AI assistant 17 ready-to-use skills that turn your AI assistant into an ASO expert. Each skill contains frameworks, scoring rubrics, and output templates — the agent reads the skill, pulls real data via the Appeeky MCP Server, and gives you actionable recommendations. Open source: [github.com/eronred/aso-skills](https://github.com/eronred/aso-skills) ## Quick Start Settings (Cmd+Shift+J) → Rules → Add Rule → Remote Rule (Github) → paste: ``` https://github.com/eronred/aso-skills ``` Cursor will automatically discover and use the skills when relevant. ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} npx skills add eronred/aso-skills ``` Or install specific skills: ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} npx skills add eronred/aso-skills --skill aso-audit keyword-research market-movers ``` ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} git clone https://github.com/eronred/aso-skills.git # Cursor cp -r aso-skills/skills/* .cursor/skills/ # Claude Code cp -r aso-skills/skills/* .claude/skills/ # Any Agent Skills-compatible tool cp -r aso-skills/skills/* .agents/skills/ ``` Skills work best with the [Appeeky MCP Server](/docs/mcp) connected — the agent pulls live App Store data automatically. Without it, skills still provide expert frameworks and guidance using general ASO knowledge. ## Available Skills ### ASO Core | Skill | What it does | MCP Tools Used | | ----------------------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | | `aso-audit` | Scores your listing across 10 factors (0-100), flags problems, gives a prioritized fix list | `aso_full_audit`, `get_app`, `get_app_keywords` | | `keyword-research` | Finds keywords by volume × difficulty × relevance, groups into primary/secondary/long-tail | `get_keyword_suggestions`, `get_keyword_metrics`, `get_keyword_ranks` | | `metadata-optimization` | Writes title, subtitle, keyword field, description — with 3 variants and character counts | `aso_validate_metadata`, `aso_suggest_metadata`, `get_app` | | `competitor-analysis` | Keyword gaps, creative teardown, positioning map, and specific opportunities to exploit | `aso_competitor_report`, `compare_keywords`, `get_app_intelligence` | ### Market Intelligence | Skill | What it does | MCP Tools Used | | --------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | | `market-movers` | Identifies top chart gainers/losers, new entries, and dropped apps — explains what's driving changes | `get_market_movers`, `get_market_activity`, `get_category_top` | | `market-pulse` | Full market briefing: chart movements + trending keywords + featured apps + new launches in one view | `get_market_movers`, `get_market_activity`, `get_trending_keywords`, `get_featured_apps`, `get_new_releases` | ### Creative & International | Skill | What it does | MCP Tools Used | | ------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------ | | `screenshot-optimization` | 10-slot screenshot strategy with design briefs, text overlay copy, and competitor audit | `get_app`, competitor screenshots | | `review-management` | Sentiment analysis, response templates (HEAR framework), rating improvement tactics | `get_app_reviews`, `get_app` | | `localization` | Market prioritization matrix, per-country keyword research, cultural adaptation checklist | `get_keyword_suggestions`, `get_keyword_metrics` | ### Growth | Skill | What it does | MCP Tools Used | | -------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | | `app-launch` | 8-week launch timeline with daily checklists, channel strategy, and press outreach templates | `search_apps`, `get_category_top`, `get_keyword_suggestions` | | `ua-campaign` | Apple Search Ads, Meta, Google UAC — campaign structure, bidding, budget allocation | `get_keyword_metrics`, `get_app_intelligence` | | `app-store-featured` | Featuring readiness score, Apple tech checklist, pitch template, In-App Events calendar | `get_featured_apps`, `get_app` | ### Revenue & Retention | Skill | What it does | MCP Tools Used | | ------------------------ | ----------------------------------------------------------------------------- | ----------------------------------------- | | `monetization-strategy` | Pricing tiers, paywall timing/design, trial optimization, category benchmarks | `get_app_intelligence`, `get_app` | | `retention-optimization` | Activation → habit → engagement framework, push sequences, churn prevention | `get_app_reviews`, `get_app_intelligence` | ### Analytics & Testing | Skill | What it does | MCP Tools Used | | ----------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------- | | `app-analytics` | Event tracking plan, dashboard setup, KPI framework with category benchmarks | `get_app_intelligence`, `get_country_rankings` | | `ab-test-store-listing` | Hypothesis → variant design → sample size → interpretation for A/B tests | `get_app`, `get_app_intelligence` | ### Foundation | Skill | What it does | MCP Tools Used | | ----------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------- | | `app-marketing-context` | Creates a context doc (app, audience, competitors, goals) that all other skills reference | `get_app`, `get_app_keywords`, `search_apps` | ## How Skills Work ``` You: "Run an ASO audit for Headspace" Agent: 1. Reads aso-audit/SKILL.md (framework, scoring rubric, output template) 2. Calls Appeeky MCP → fetches metadata, keywords, ratings, competitors 3. Scores each factor (title: 8/10, subtitle: 6/10, keywords: 4/10...) 4. Returns: ASO Score Card + Quick Wins + High-Impact Changes + Strategic Recs ``` Skills reference each other — `aso-audit` might suggest running `keyword-research` for deeper analysis, which then feeds into `metadata-optimization` for implementation. ## Example Prompts **ASO:** ``` Run a full ASO audit on my app (id: 570060128). What's my score and what should I improve? ``` **Keywords:** ``` Find the best keywords for a meditation app. Show me volume, difficulty, and which ones my competitors rank for. ``` **Market intelligence:** ``` What apps are rising in the free charts right now? Show me the top gainers in Games and any new entries. ``` **Market briefing:** ``` Give me a full market pulse for the Health & Fitness category. Include chart movers, trending keywords, and featured apps. ``` **Competitor analysis:** ``` Compare my app (id: 493145008) against Calm (id: 571800810). Where am I falling behind on keywords and creative? ``` **Launch planning:** ``` I'm launching a fitness app in 6 weeks. Create a launch plan with ASO, marketing, and PR milestones. ``` ## Skill Compatibility | Platform | Install Method | Status | | ------------------------- | --------------------------------------------- | --------------- | | **Cursor** | Settings → Rules → Remote Rule | Fully supported | | **Claude Code** | `npx skills add` or manual copy | Fully supported | | **Windsurf** | Copy to `.agents/skills/` | Compatible | | **Any Agent Skills tool** | Copy to `.agents/skills/` or `.codex/skills/` | Compatible | Skills follow the open [Agent Skills](https://agentskills.io) standard — any tool that reads `SKILL.md` files can use them. ## Contributing Found an inaccuracy? Have a better framework? Want to add a skill? Contributions are welcome. See the [Contributing Guide](https://github.com/eronred/aso-skills/blob/main/CONTRIBUTING.md) on GitHub. # Superwall Source: https://docs.appeeky.com/docs/superwall Connect Superwall and read paywall proceeds, MRR, charts, transactions, and campaigns Connect Superwall to pull paywall metrics into Appeeky — REST, MCP, and the co-founder agent. Use an **organization API key** from Superwall → Settings → API Keys (not a public SDK key). Appeeky stores it encrypted. Then attach Superwall apps to each Appeeky product, the same way you attach RevenueCat apps. ## How it works 1. Save your Superwall organization API key in Appeeky Settings, or via `POST /v1/connect/superwall/credentials`. 2. Attach the matching Superwall applications on the app dashboard. 3. Call metrics endpoints, MCP tools (`sw_*`), or ask the co-founder. They use the saved key — no Superwall key header. ## Credentials | Method | Endpoint | Description | | ------ | ------------------------------------------ | ----------------------------------------- | | POST | `/v1/connect/superwall/credentials` | Save and verify the organization API key | | GET | `/v1/connect/superwall/credentials/status` | Connection status | | DELETE | `/v1/connect/superwall/credentials` | Disconnect Superwall | | GET | `/v1/connect/superwall/apps` | List projects and applications in the org | ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/superwall/credentials" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"secretKey":"YOUR_SUPERWALL_ORG_KEY"}' ``` ## Metrics See [Superwall Metrics](/docs/superwall-metrics) for overview, dashboard, charts, transactions, and campaigns. ## MCP After Superwall is connected, these tools use the saved key: | Tool | Description | | ----------------- | ------------------------------------------------ | | `sw_apps` | List Superwall projects and applications | | `sw_overview` | Snapshot KPIs (proceeds, MRR, users, conversion) | | `sw_dashboard` | Stats + KPI charts + recent transactions | | `sw_chart` | Time-series for a Superwall y-axis metric | | `sw_transactions` | Recent purchases, renewals, and trials | | `sw_campaigns` | Campaigns and placements for an application | Full parameter reference: [MCP Server](/docs/mcp#sw_apps). ## Co-founder When Superwall is connected and attached, briefings and chat can read paywall proceeds, MRR, charts, transactions, and campaigns. Campaign and paywall edits stay in the Superwall dashboard. ## Related * [Superwall Metrics](/docs/superwall-metrics) * [RevenueCat Overview](/docs/revenuecat-overview) * [MCP Server](/docs/mcp) # Superwall Metrics Source: https://docs.appeeky.com/docs/superwall-metrics Live Superwall statistics, KPI charts, transactions, and campaigns using the saved organization API key ``` GET /v1/connect/superwall/metrics/overview GET /v1/connect/superwall/metrics/dashboard POST /v1/connect/superwall/metrics/charts/data GET /v1/connect/superwall/metrics/transactions GET /v1/connect/superwall/campaigns ``` These endpoints proxy the [Superwall API](https://api.superwall.com). Connect Superwall first ([Credentials](/docs/superwall)), then call them with your Appeeky API key. Omit `application_id` to use the first application in the connected organization. For co-founder and product dashboards, attach the matching Superwall application on the app so metrics stay scoped to that product. ## Overview ``` GET /v1/connect/superwall/metrics/overview ``` Snapshot KPIs for one Superwall application: proceeds, MRR, users, conversion, and related statistics. ### Query parameters | Name | Type | Default | Description | | ---------------- | ------ | ---------------- | ------------------------------------------------------------------------ | | `application_id` | string | first listed app | Superwall application id | | `environment` | string | `PRODUCTION` | `PRODUCTION` or `SANDBOX` | | `date_preset` | string | `last_30_days` | Superwall date preset (`last_7_days`, `last_30_days`, `last_90_days`, …) | ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/connect/superwall/metrics/overview?date_preset=last_30_days" \ -H "X-API-Key: YOUR_API_KEY" ``` ## Dashboard ``` GET /v1/connect/superwall/metrics/dashboard ``` Bundled payload: statistics, KPI charts (`netProceeds`, `mrr`, `newUsers`, `transactionCompletes`, `trialStarts`), and recent transactions. Same query parameters as overview. ## Chart data ``` POST /v1/connect/superwall/metrics/charts/data ``` Time-series for one Superwall y-axis metric. ### Body | Name | Type | Required | Description | | --------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | `yAxis` | string | yes | Metric key, e.g. `netProceeds`, `mrr`, `newUsers`, `transactionCompletes`, `trialStarts`, `paywallConvRate` | | `applicationId` | string | no | Superwall application id | | `xAxis` | string | no | `purchaseDate`, `installDate`, or `mrrDate`. Defaults from `yAxis` (`mrr`/`arr` → `mrrDate`, user metrics → `installDate`, else `purchaseDate`) | | `datePreset` | string | no | Superwall date preset (`last_30_days` default) | | `dateInterval` | string | no | `hour`, `day`, `week`, or `month` (`day` default) | ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/connect/superwall/metrics/charts/data" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"yAxis":"netProceeds","datePreset":"last_30_days","dateInterval":"day"}' ``` ## Transactions ``` GET /v1/connect/superwall/metrics/transactions ``` Recent purchases, renewals, and trials. | Name | Type | Description | | ---------------- | ------ | ------------------------------------ | | `application_id` | string | Superwall application id | | `environment` | string | `PRODUCTION` or `SANDBOX` | | `date_preset` | string | Superwall date preset | | `event_type` | string | Optional Superwall event type filter | ## Campaigns ``` GET /v1/connect/superwall/campaigns ``` Campaigns and placements for an application. Optional `application_id` query parameter. ## Errors | Status | Code | Meaning | | --------------- | ------------------ | ------------------------------------- | | 401 | `AUTH_REQUIRED` | Missing Appeeky session or API key | | 404 | `NOT_CONNECTED` | Save Superwall credentials first | | 400 / 401 / 403 | Superwall upstream | Invalid or insufficient Superwall key | | 502 | Superwall upstream | Superwall API error | ## MCP equivalents | REST | MCP | | --------------------------- | ----------------- | | `GET /metrics/overview` | `sw_overview` | | `GET /metrics/dashboard` | `sw_dashboard` | | `POST /metrics/charts/data` | `sw_chart` | | `GET /metrics/transactions` | `sw_transactions` | | `GET /campaigns` | `sw_campaigns` | | `GET /apps` | `sw_apps` | See [Superwall](/docs/superwall) for connect flow and [MCP Server](/docs/mcp#sw_apps) for tool parameters. # Track Keyword Source: https://docs.appeeky.com/docs/track-keyword Add a keyword to the tracking list so historical rank data is collected for it. ``` POST /v1/keywords/track ``` Adds a keyword to your tracking list. Tracked keywords accumulate historical rank snapshots over time, which powers the [Keyword Trends](/docs/keyword-trends) and [Visibility](/docs/keyword-visibility) endpoints. *** ## Request Body ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "keyword": "puzzle game", "platform": "apple", "country": "us" } ``` | Field | Type | Required | Description | | -------- | ------ | -------- | -------------------------------------------------------------------------------- | | keyword | string | Yes | Keyword to track (min 2 characters) | | platform | string | No | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | No | ISO country code (default `us`) | *** ## Code Examples ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST "https://api.appeeky.com/v1/keywords/track" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"keyword": "puzzle game"}' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch("https://api.appeeky.com/v1/keywords/track", { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ keyword: "puzzle game" }), }); const { data } = await res.json(); console.log(data.message); // "Keyword added to tracking list" ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.post( "https://api.appeeky.com/v1/keywords/track", json={"keyword": "puzzle game"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] print(data["message"]) # "Keyword added to tracking list" ``` *** ## Response **Status: `201 Created`** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "keyword": "puzzle game", "message": "Keyword added to tracking list" } } ``` | Field | Type | Description | | ------- | ------ | -------------------- | | keyword | string | The tracked keyword | | message | string | Confirmation message | *** **Idempotent upsert**: If the keyword already exists in the tracking list, it is updated to active status. You can safely call this endpoint multiple times for the same keyword without creating duplicates. **Auto-tracking**: Keywords you query through `GET /v1/keywords/ranks` are also added to your tracking list automatically once they've been queried more than once. If you already use the ranks endpoint for keyword research, many keywords will be tracked for you — no need to call this endpoint separately for those. *** ## Errors | Status | Code | When | | ------ | ---------------- | --------------------------------- | | 400 | INVALID\_KEYWORD | Keyword shorter than 2 characters | | 401 | UNAUTHORIZED | Missing or invalid API key | | 429 | RATE\_LIMITED | Too many requests — slow down | | 500 | DB\_ERROR | Database operation failed | # Trending Keywords Source: https://docs.appeeky.com/docs/trending-keywords Keywords with the fastest-growing reach in the App Store, ranked by growth percentage ``` GET /v1/keywords/trending ``` Returns keywords whose reach (measured by the top-ranking app's review count) has grown the most over a given period. Use this to identify rising search trends and high-momentum keywords for ASO. ## Query Parameters | Name | Type | Default | Description | | -------- | ------ | ------- | -------------------------------------------------------------------------------- | | platform | string | `apple` | `apple` (default) or `google` for Google Play — see [Platforms](/docs/platforms) | | country | string | `us` | ISO country code (e.g. `us`, `gb`, `de`) | | days | number | `7` | Trend window in days (1-30) | | limit | number | `50` | Max keywords to return (1-100) | ## Examples ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/keywords/trending?country=us&days=7&limit=10" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/keywords/trending?country=us&days=7&limit=10", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); console.log(`${data.total} trending keywords in ${data.period}`); for (const kw of data.keywords) { console.log(`${kw.keyword}: +${kw.growthPercent}% growth`); } ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/keywords/trending", params={"country": "us", "days": 7, "limit": 10}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] for kw in data["keywords"]: print(f"{kw['keyword']}: +{kw['growthPercent']}% (vol={kw['volumeScore']})") ``` ## Response ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "country": "us", "period": "7d", "keywords": [ { "keyword": "xai", "growthPercent": 330.5, "maxReach": 10509533, "currentReach": 10509533, "volumeScore": 86, "resultCount": 42, "difficulty": 88 }, { "keyword": "meditation", "growthPercent": 145.2, "maxReach": 3814828, "currentReach": 3814828, "volumeScore": 72, "resultCount": 48, "difficulty": 65 } ], "total": 77 } } ``` ## Response Fields ### Top Level | Field | Type | Description | | ---------- | ------------------ | ------------------------------------------------- | | `country` | string | ISO country code | | `period` | string | Trend window (e.g. `7d`, `14d`, `30d`) | | `keywords` | TrendingKeyword\[] | Keywords sorted by growth percentage (descending) | | `total` | number | Total trending keywords found (before limit) | ### TrendingKeyword | Field | Type | Description | | --------------- | ------------ | --------------------------------------------------------- | | `keyword` | string | The keyword | | `growthPercent` | number | Percentage growth in reach over the period | | `maxReach` | number | Maximum reach (top app review count) in current window | | `currentReach` | number | Most recent reach value | | `volumeScore` | number\|null | Estimated search volume (0-100) | | `resultCount` | number | Number of apps returned by iTunes Search for this keyword | | `difficulty` | number\|null | Ranking difficulty score (0-100) | ## How It Works The trending algorithm compares keyword reach between two time windows: 1. **Current window**: Last `days` days 2. **Previous window**: The `days` before that **Reach** is measured by `top_app_reviews` — the review count of the #1 ranking app for each keyword. This is a strong proxy for keyword popularity because high-traffic keywords attract apps with large user bases. A keyword is "trending" when its max reach in the current window exceeds the previous window. The growth percentage is: `(current - previous) / previous × 100`. Trending data is computed from the tracked keyword set. Add new keywords with [`POST /v1/keywords/track`](/docs/track-keyword) to expand coverage. ## Errors | Status | Code | When | | ------ | --------------- | ------------------------ | | 500 | INTERNAL\_ERROR | Failed to compute trends | # Appeeky API & MCP Source: https://docs.appeeky.com/index The all-in-one mobile growth API — App Store & Google Play intelligence, ASO, AI visibility, paid ads, subscriptions, and publisher integrations via REST and MCP Appeeky is more than store intelligence. One API — and an **MCP server** for Claude, Cursor, and other AI clients — covers the full mobile growth stack for **iOS and Android**: * **Market intelligence** — metadata, keyword ranks, revenue & download, charts, competitors, reviews, and market movers across 175+ storefronts * **ASO** — audits, briefs, metadata suggest/validate, localization, idea validation, and competitor reports * **AI Visibility** — track how ChatGPT, Gemini, Claude, and Perplexity recommend your app vs rivals * **Your connected data** — App Store Connect, Google Play Console, Apple Search Ads, and RevenueCat in the same API * **My Apps workspace** — tracked apps, competitors, keywords, metadata versions, and saved reports via web or desktop (0 credits) * **Growth tools** — ad creative generation, keyword expansion, gap analysis, and profitability workflows Use REST from any stack, or connect an MCP client and run ASO, ads, and analytics from your editor. Appeeky Most endpoints support both stores via a `platform` parameter (`apple` by default, or `google`). See [Platforms](/docs/platforms) for how to target Google Play, identifier formats, and which endpoints are cross-platform. Go from zero to your first API call in 30 seconds Register for a free API key and authenticate requests Metadata, intelligence reports, reviews, screenshots, and country rankings Ranks, suggestions, metrics, trends, gap analysis, expansion, and competitor overlap Audits, briefs, metadata suggest/validate, localization, idea validation, ad creatives, and competitor reports Track how often ChatGPT, Gemini, Claude, and Perplexity recommend your app App search, category charts, new releases, and trending apps Market movers, trending keywords, featured apps, and download estimates Manage your apps, metadata, analytics, sales reports, and reviews via ASC API Reviews, vitals, analytics, reports, listings, releases, and subscriptions for your Play apps Full campaign management — keywords, negatives, reports, profitability, pause/resume via REST or MCP Revenue charts, subscriber metrics, and monetization insights Manage your tracked apps, keywords, versions, and reports via API or MCP Connect Claude, Cursor, or any MCP client to the full Appeeky API — intelligence, ASO, ads, and your app data 17 pre-built ASO & marketing skills for Cursor and Claude — powered by Appeeky MCP data Full endpoint index, error codes, and response format *** ## What You Can Do | Capability | Endpoint | What You Get | | ------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Health Check** | `GET /v1/health` | Service status and uptime | | **App Search** | `GET /v1/search` | Search apps by keyword or direct App ID lookup | | **App Metadata** | `GET /v1/apps/:id` | Title, developer, icon, description, screenshots, version, size, languages, age rating | | **Intelligence Report** | `GET /v1/apps/:id/intelligence` | Revenue & download estimates, in-app purchases, similar apps, sentiment analysis (`topCountries` removed — use Country Rankings) | | **User Reviews** | `GET /v1/apps/:id/reviews` | Star ratings, review text, author, date (up to 500 per country) | | **App Keywords** | `GET /v1/apps/:id/keywords` | Organic keyword rankings, popularity, and competitiveness | | **Keyword Trends** | `GET /v1/apps/:id/keywords/trends` | Historical rank changes for a keyword over time | | **Country Rankings** | `GET /v1/apps/:id/country-rankings` | App's chart positions across multiple countries (use this for top countries data) | | **Keyword Rankings** | `GET /v1/keywords/ranks` | All apps ranking for a specific keyword | | **Keyword Suggestions** | `GET /v1/keywords/suggestions` | App Store / Google Play autocomplete suggestions | | **Keyword Metrics** | `GET /v1/keywords/metrics` | Search volume and difficulty scores | | **Keyword Compare** | `GET /v1/keywords/compare` | Competitor keyword overlap and gap analysis | | **Track Keyword** | `POST /v1/keywords/track` | Add a keyword to the daily scraping pipeline | | **Categories** | `GET /v1/categories` | All App Store category IDs and names | | **Category Charts** | `GET /v1/categories/:id/top` | Top Free, Top Paid, Top Grossing per category | | **New Releases** | `GET /v1/new-releases` | Recently released apps from the App Store | | **Discover** | `GET /v1/discover` | Trending and noteworthy apps | | **New #1 Apps** | `GET /v1/discover/new-number-1` | Apps that just reached #1 in their category | | **My Apps** | `GET/POST/PATCH/DELETE /v1/user/apps` | Manage your tracked app list (0 credits) | | **Competitors** | `GET/POST/DELETE /v1/user/apps/:appId/competitors` | Track competitor apps per app (0 credits) | | **Tracked Keywords** | `GET/POST/DELETE /v1/user/apps/:appId/keywords` | Manage tracked keywords per app (0 credits) | | **Metadata Versions** | `GET/POST/PATCH /v1/user/apps/:appId/versions` | Save and version your ASO copy (0 credits) | | **Reports** | `GET/DELETE /v1/user/reports` | Access saved ASO analysis reports (0 credits) | | **ASO Audit** | `GET /v1/aso/apps/:id/audit` | Full ASO health check — title, subtitle, screenshots, reviews, keyword coverage | | **ASO Brief** | `GET /v1/aso/apps/:id/brief` | Executive summary of your app's ASO posture for stakeholders | | **Intent Clusters** | `GET /v1/aso/apps/:id/intent-clusters` | User intents grouped from your ranking keywords | | **Metadata Suggest** | `POST /v1/aso/metadata/suggest` | AI-generated title, subtitle, and keyword set proposals | | **Metadata Validate** | `POST /v1/aso/metadata/validate` | Lint your app metadata against length, character, and best-practice rules | | **ASO Opportunities** | `GET /v1/aso/apps/:id/opportunities` | Ranked list of keywords and gaps worth pursuing next | | **Competitor Report** | `GET /v1/aso/apps/:id/competitor-report` | Side-by-side ASO comparison against your tracked competitors | | **AI Visibility — Score** | `GET /v1/ai-visibility/:appId/overview` | AI Visibility Score (0–100), sentiment, and intent coverage per model & country | | **AI Visibility — Intents** | `GET /v1/ai-visibility/:appId/intents` | Per-intent visibility, prompt coverage, and which apps appear alongside (or instead of) yours | | **AI Visibility — Prompts** | `GET/POST/PATCH/DELETE /v1/ai-visibility/intents/:intentId/prompts` | Add, edit, pause, or remove the user-style prompts the agent sends to AI assistants | | **AI Visibility — Competitors** | `GET /v1/ai-visibility/:appId/competitors` | The apps ChatGPT, Gemini, Claude, and Perplexity recommend in your category | | **AI Visibility — Trend** | `GET /v1/ai-visibility/:appId/trend` | Daily history of visibility, sentiment, and prompt counts for charting | | **AI Visibility — Answers** | `GET /v1/ai-visibility/:appId/answers/:answerId` | Drill into a single AI response — raw text, parsed mentions, and citations | | **AI Visibility — Bootstrap** | `POST /v1/ai-visibility/:appId/bootstrap` | Generate the initial intents + prompts and queue the first scan | | **AI Visibility — Scan** | `POST /v1/ai-visibility/:appId/scan` | Trigger an on-demand AI Visibility scan for a country and selected models | | **AI Visibility — Settings** | `GET/PATCH /v1/ai-visibility/:appId/settings` | Configure enabled models, scan cadence, and feature toggles per app | | **Apple Search Ads** | `GET/POST/PUT /v1/connect/apple-ads/...` | Full Campaign Management API v5: campaigns, ad groups, targeting keywords (CRUD), negative keywords, bid recommendations, keyword + search-term reports ([guide](/docs/apple-search-ads)) | *** ## Base URL All API requests are made to: ``` https://api.appeeky.com ``` All endpoints are prefixed with `/v1`. For example, the full URL for app metadata is: ``` https://api.appeeky.com/v1/apps/1617391485 ``` *** ## Response Format **Successful responses** wrap data in a `data` envelope: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "trackId": 1617391485, "trackName": "Block Blast!", "sellerName": "Hungry Studio" } } ``` **Error responses** return an `error` object with a machine-readable code and a human-readable message: ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "error": { "code": "INVALID_APP_ID", "message": "App ID must be a positive integer" } } ``` Credits are only deducted on **successful** responses (2xx). Failed requests never consume credits. *** ## Get Started in 30 Seconds **1. Create a free account** on the [Appeeky Dashboard](https://dashboard.appeeky.com) to get your API key with **100 monthly credits**. Sign up with email or GitHub. **2. Make your first request:** ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/apps/1617391485?country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/apps/1617391485?country=us", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); console.log(data.trackName); // "Block Blast!" ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/apps/1617391485", params={"country": "us"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] print(data["trackName"]) # "Block Blast!" ``` That's it — you're ready to explore. Head to the [Quick Start](/quickstart) for a full walkthrough. # Quick Start Source: https://docs.appeeky.com/quickstart Go from zero to your first App Store or Google Play intelligence request in under a minute This guide walks you through registering for an API key, fetching app metadata, pulling an intelligence report, and exploring keyword data — all in five steps. **Base URL** for all requests: `https://api.appeeky.com` Examples below use the Apple App Store (the default). To query **Google Play**, add `platform=google` and use a package name as the app identifier (e.g. `com.spotify.music`). See [Platforms](/docs/platforms). *** Create a free account on the **Appeeky Dashboard** to get your API key with **100 monthly credits**. Sign up with email or GitHub — your API key is generated instantly. Save your API key immediately — it is displayed only once. The key is stored as a SHA-256 hash and cannot be retrieved later. You can regenerate a new key from the dashboard at any time. For the remaining steps, replace `YOUR_API_KEY` with the key you received. Fetch metadata for **Block Blast!** (App ID `1617391485`). This returns the full iTunes Lookup data: title, developer, screenshots, version, ratings, and more. ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/apps/1617391485?country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/apps/1617391485?country=us", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); console.log(data.trackName); // "Block Blast!" console.log(data.averageUserRating); // 4.7 ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/apps/1617391485", params={"country": "us"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] print(data["trackName"]) # "Block Blast!" print(data["averageUserRating"]) # 4.7 ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "trackId": 1617391485, "trackName": "Block Blast!", "sellerName": "Hungry Studio", "bundleId": "com.hungrystudio.blockblast", "primaryGenreName": "Games", "averageUserRating": 4.7, "userRatingCount": 892451, "price": 0, "currentVersionReleaseDate": "2025-12-10T08:00:00Z", "artworkUrl512": "https://is1-ssl.mzstatic.com/image/...", "screenshotUrls": ["https://is1-ssl.mzstatic.com/image/..."], "description": "Block Blast is an addictive puzzle game..." } } ``` Use the `country` query parameter to get localized metadata. Default is `us`. Common values: `us`, `gb`, `de`, `jp`, `fr`, `kr`, `br`. The intelligence endpoint returns market estimates (downloads, revenue), in-app purchases, similar apps, and sentiment analysis — all in a single call. ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/apps/1617391485/intelligence?country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/apps/1617391485/intelligence?country=us", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); console.log(data.marketIntelligence.estimatedDownloads); console.log(data.inAppPurchases); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/apps/1617391485/intelligence", params={"country": "us"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] print(data["marketIntelligence"]["estimatedDownloads"]) print(data["inAppPurchases"]) ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "trackId": 1617391485, "trackName": "Block Blast!", "marketIntelligence": { "estimatedDownloads": { "value": 4200000, "range": { "low": 3100000, "high": 5600000 }, "confidence": "high", "period": "monthly" }, "estimatedRevenue": { "value": 8900000, "range": { "low": 6200000, "high": 12000000 }, "confidence": "high", "period": "monthly", "currency": "USD" } }, "inAppPurchases": [ { "name": "Remove Ads", "price": "$4.99" }, { "name": "Weekly VIP", "price": "$7.99" }, { "name": "1000 Coins", "price": "$1.99" } ], "similarApps": [ { "trackId": 1580819864, "trackName": "Hexa Sort", "sellerName": "Lion Studios" }, { "trackId": 1594113349, "trackName": "Royal Match", "sellerName": "Dream Games" }, { "trackId": 553834731, "trackName": "Candy Crush Saga", "sellerName": "King" } ], "sentimentAnalysis": { "averageRating": 4.7, "totalReviews": 892451, "positivePercentage": 89.2, "topPositiveThemes": ["addictive gameplay", "simple controls", "relaxing"], "topNegativeThemes": ["too many ads", "difficulty spikes"] } } } ``` The intelligence endpoint costs **5 credits** because it aggregates data from multiple sources in parallel. Use `/v1/apps/:id` (2 credits) if you only need basic metadata. See which apps rank for a specific keyword, along with popularity and competitiveness scores. ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl "https://api.appeeky.com/v1/keywords/ranks?keyword=puzzle+game&country=us" \ -H "X-API-Key: YOUR_API_KEY" ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch( "https://api.appeeky.com/v1/keywords/ranks?keyword=puzzle+game&country=us", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { data } = await res.json(); data.rankings.forEach((app) => { console.log(`#${app.rank} ${app.trackName} (${app.trackId})`); }); ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.get( "https://api.appeeky.com/v1/keywords/ranks", params={"keyword": "puzzle game", "country": "us"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) data = res.json()["data"] for app in data["rankings"]: print(f'#{app["rank"]} {app["trackName"]} ({app["trackId"]})') ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "keyword": "puzzle game", "country": "us", "popularity": 62, "competitiveness": 78, "totalResults": 200, "rankings": [ { "rank": 1, "trackId": 1617391485, "trackName": "Block Blast!", "sellerName": "Hungry Studio" }, { "rank": 2, "trackId": 1594113349, "trackName": "Royal Match", "sellerName": "Dream Games" }, { "rank": 3, "trackId": 553834731, "trackName": "Candy Crush Saga", "sellerName": "King" }, { "rank": 4, "trackId": 913335252, "trackName": "Brilliant", "sellerName": "Brilliant.org" }, { "rank": 5, "trackId": 1580819864, "trackName": "Hexa Sort", "sellerName": "Lion Studios" } ] } } ``` Add a keyword to the daily scraping pipeline so rank data stays fresh. ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}} curl -X POST https://api.appeeky.com/v1/keywords/track \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"keyword": "puzzle game", "country": "us"}' ``` ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}} const res = await fetch("https://api.appeeky.com/v1/keywords/track", { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ keyword: "puzzle game", country: "us" }), }); const { data } = await res.json(); console.log(data.status); // "tracked" ``` ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}} import requests res = requests.post( "https://api.appeeky.com/v1/keywords/track", headers={"X-API-Key": "YOUR_API_KEY"}, json={"keyword": "puzzle game", "country": "us"}, ) data = res.json()["data"] print(data["status"]) # "tracked" ``` **Response (200 OK):** ```json theme={"theme":{"light":"github-light","dark":"github-dark"}} { "data": { "keyword": "puzzle game", "country": "us", "status": "tracked", "message": "Keyword is now being tracked. Rankings will be updated daily." } } ``` Tracked keywords are scraped daily. Historical rank data becomes available through the `/v1/apps/:id/keywords/trends` endpoint after the first scrape cycle. *** ## Next Steps You've covered the basics. Dive deeper into specific areas: Full app details, screenshots, and version history Revenue estimates, downloads, IAPs, and competitor analysis See which apps rank for any keyword Discover all keywords an app ranks for Audits, briefs, intent clusters, and metadata suggestions See how often ChatGPT, Gemini, Claude, and Perplexity recommend your app Search the App Store by keyword or ID Top apps by category, chart type, and country Connect Claude, Cursor, or any MCP client to App Store & Google Play intelligence Credit costs, plans, and usage tracking Complete endpoint reference and error codes