# StoreScope Analytics API reference

> Every StoreScope Analytics tool over REST: the same 38 tools, wording and rules as the MCP server at https://storescope.devino.ca/mcp. OpenAPI 3.1: https://storescope.devino.ca/openapi.json

## Authentication

- Create a workspace key on the MCP & API page (read, or read and write; it is shown once) and send it as `Authorization: Bearer sws_…`.
- An OAuth access token this host issued for its MCP server works the same way.
- A key reaches one workspace. Revoking it on the MCP & API page stops it at once.
- A read key never reaches a write tool; a viewer's key, or any key in a workspace whose owner turned AI changes off, is read-only.

## Request and response

`POST https://storescope.devino.ca/api/tools/<tool name>` with the tool's arguments as one JSON object (an empty body or `{}` when it needs none).

```sh
curl -X POST https://storescope.devino.ca/api/tools/data_freshness \
  -H "Authorization: Bearer sws_…" \
  -H "Content-Type: application/json" \
  -d '{}'
```

- 200: the tool's own JSON answer.
- 400: the tool refused (`{"code": …, "message": …}`), the arguments did not match its schema, or the body was not JSON.
- 401, 404, 413, 429, 502, 503: refused before the tool ran. Every code: https://storescope.devino.ca/docs/errors.md

OpenAPI 3.1: https://storescope.devino.ca/openapi.json (generated from the same tool definitions the MCP server lists).

## Writes: preview first, then confirm

1. Call the write tool with its arguments. Nothing reaches the store: the answer is a preview (the diff or plan) with a `confirm_token` (for example `play_review_reply`).
2. Repeat the same call with `"apply": true` and `"confirmToken": "<the token>"`. When the preview returned `commit_args`, merge them into the arguments: they pin the store state the preview saw, and a store that changed since is refused.
3. The token is single use, valid 10 minutes, and bound to the key or connection, the tool and those exact arguments.
4. A preview with `"status": "rejected"` carries no token and cannot be committed.

## Limits

- 60 calls a minute per key or AI connection, MCP and API together. Over it: 429 `rate_limited` with a `Retry-After` header (seconds) and `retry_after_s` in the body.
- Request bodies up to 256 KB (413 above).
- A call runs at most 15 minutes (502 after). When every worker is busy: 503 with `Retry-After: 5`.
- Answers are capped (at most 200 rows per read, 500 for `sql_query` where offered).

## Tools (38)

### data_freshness (read)

Data freshness. Per-adapter last sync status/time/rows, latest fact date per store, apps with no data, stale (>3d over the store's own lag) warnings, and the count of pending Apple analytics report requests.

No arguments: send an empty body.

### fleet_overview (read)

Overview of every app. Every app x store for the last N days: downloads/installs/acquisitions totals, impressions -> page_views -> downloads funnel where the store provides it, conversion, rating average and review count, plus a one-line…

- `days`: integer 1-365. lookback window in days (default 28) Default: 28.
- `includeEmpty`: boolean. keep app x store rows that have nothing actionable in the window Default: false.

### app_funnel (read)

App funnel. Daily impressions/page_views/downloads time series for one app on one store plus a grouped funnel with conversion computed per group.

- `app` (required): string. app slug (as on the Apps page)
- `store` (required): string. apple | play | microsoft | claude | chatgpt | copilot | registry
- `days`: integer 1-365. Default: 28.
- `groupBy`: "source_type" | "page_type" | "territory" | "device" | "source_info". Default: "source_type".

### compare_periods (read)

Compare periods. Current vs previous period for one metric, broken down per group (defaults: group by store when store is omitted, by source_type when it is given).

- `app` (required): string. app slug (as on the Apps page)
- `store`: string. apple | play | microsoft | claude | chatgpt | copilot | registry
- `metric` (required): string. raw or canonical metric, e.g. downloads_first_time | impressions | page_views | tool_calls
- `periodDays`: integer 1-180. Default: 14.
- `groupBy`: "store" | "source_type" | "page_type" | "territory" | "device" | "source_info".

### listing_change_impact (read)

Listing change impact. For each listing change (listing_changes audit rows and detected listing_versions snapshot diffs), the 14d-before vs 14d-after impressions / page_views / downloads / conversion.

- `app` (required): string. app slug (as on the Apps page)
- `store` (required): string. apple | play | microsoft | claude | chatgpt | copilot | registry
- `changeId`: integer -9007199254740991-9007199254740991. a listing_changes.id; when given, only that change is analysed
- `since`: string. ISO date/timestamp lower bound for events
- `windowDays`: integer 1-90. Default: 14.

### listing_history (read)

Listing history. The listing_versions snapshot chain for an app/store (optionally one locale) with field-level diffs between consecutive snapshots, plus the listing_changes audit rows (what this MCP planned or applied, in which mode).

- `app` (required): string. app slug (as on the Apps page)
- `store` (required): string. apple | play | microsoft | claude | chatgpt | copilot | registry
- `locale`: string.

### reviews_digest (read)

Reviews digest. Review counts by rating, average, unanswered count, top recurring words and two-word phrases (simple stopword tokenizer), and the 10 most recent low-rating (<=3) reviews.

- `app`: string. app slug (as on the Apps page)
- `store`: string. apple | play | microsoft | claude | chatgpt | copilot | registry
- `days`: integer 1-365. Default: 30.
- `minRating`: integer 1-5.
- `maxRating`: integer 1-5.

### reviews_unanswered (read)

Reviews awaiting a developer reply. Lists the archived reviews that have NO developer response yet, newest first, with the ids the *_review_reply tools take (apple_review_reply / play_review_reply / ms_review_reply).

- `app`: string. app slug (as on the Apps page)
- `store`: string. apple | play | microsoft
- `min_rating`: integer 1-5.
- `max_rating`: integer 1-5.
- `days`: integer 1-365. Default: 30.
- `limit`: integer 1-200. Default: 50.

### reviews_inbox (read)

Reviews inbox, per app. Every app's reviews in one place: per app, a per-store summary (count, average rating, unanswered, unanswered <= 3 stars, newest review date) and the newest reviews, unanswered low ratings first, each with the reply…

- `app`: string. app slug (as on the Apps page)
- `store`: string. apple | play | microsoft
- `days`: integer 1-365. Default: 30.
- `only_unanswered`: boolean. Default: false.
- `max_rating`: integer 1-5.
- `limit`: integer 1-50. reviews listed per app Default: 10.

### developer_channels (read)

Developer channels (GitHub, npm, PyPI). Where developers find each app: GitHub traffic on its PUBLIC SDK repos (views, unique visitors, clones per day, stars/forks, and the top REFERRERS and paths that sent the visits) plus npm and PyPI downloads of every…

- `app`: string. app slug (as on the Apps page)
- `days`: integer 1-365. Default: 28.
- `top`: integer 0-25. referrers / paths listed per app Default: 10.

### search_performance (read)

Web search performance (Search Console, Bing). Per app and engine (gsc = Google Search Console, bing = Bing Webmaster Tools): clicks, impressions, CTR and impression-weighted average position on the app's PUBLIC WEBSITE over `days`, anchored on that engine's own…

- `app`: string. app slug (as on the Apps page)
- `engine`: "gsc" | "bing".
- `days`: integer 1-365. Default: 28.
- `top`: integer 0-50. queries / pages listed per app and engine Default: 10.

### web_traffic (read)

Website traffic (Google Analytics 4). Per app: sessions, engaged sessions (and the engagement rate), new users, page views and key events on the app's PUBLIC WEBSITE from Google Analytics 4, summed over `days` anchored on that app's own newest GA4 day, the…

- `app`: string. app slug (as on the Apps page)
- `days`: integer 1-365. Default: 28.

### product_usage (read)

Product usage (PostHog). Per app: what people do IN the app, from PostHog: events, pageviews and sessions summed over `days` anchored on that app's own newest PostHog day, the same sums for the window before, active users of the latest day and…

- `app`: string. app slug (as on the Apps page)
- `days`: integer 1-365. Default: 28.

### app_stability (read)

App stability (Sentry). Per app: Sentry errors and release-health sessions summed over `days` anchored on that app's own newest Sentry day, the window before, the crash-free session rate computed from the session sums (never an average of…

- `app`: string. app slug (as on the Apps page)
- `days`: integer 1-365. Default: 28.

### top_movers (read)

Top movers. Workspace-wide biggest ups and downs across app x store x metric: last N days vs the N days before, each series anchored on its own latest date.

- `days`: integer 1-90. Default: 7.
- `limit`: integer 1-50. Default: 10.

### app_health (read)

App health. Sessions, crashes (+ computed crashes per 1k sessions with its basis), purchases (count only) and pre-orders per app x store over the last N days, from Apple's App Sessions / App Crashes / App Store Purchases /…

- `app`: string. app slug (as on the Apps page)
- `days`: integer 1-365. lookback window in days, per series (default 30) Default: 30.

### listing_proposals (read)

Listing proposals (data-guided). THE optimization tool: deterministic, rule-based, evidence-backed changes to an app's store text and images, derived only from warehouse data.

- `app` (required): string. app slug (as on the Apps page)
- `store`: "apple" | "play" | "microsoft". apple | play | microsoft. Omit for every store this app is listed on.
- `locale`: string. exact listing locale, e.g. en-US (Apple/Play) or en-us (Microsoft). Omit for all locales.
- `days`: integer 7-180. window for the conversion and download-trend rules (default 28) Default: 28.

### aso_audit (read)

ASO audit (every app). Workspace-wide triage over listing_proposals: one row per app x store x locale with its high/medium/low proposal counts, its downloads in the window, and the top 3 proposals as one-liners.

- `days`: integer 7-180. window for downloads and the trend rules (default 28) Default: 28.

### revenue_overview (read)

Revenue overview. Money for the last N days from the stores' own sales reports (warehouse only): proceeds, paid units, free units and refunds per app | store | country | currency.

- `app`: string. app slug (as on the Apps page)
- `days`: integer 1-365. window in days on each series' own anchor (default 30) Default: 30.
- `group_by`: "app" | "store" | "country" | "currency". app | store | country | currency Default: "app".

### subscription_metrics (read)

Subscription metrics. Apple auto-renewable subscriptions per app (warehouse only): active_subscriptions and active_free_trials as the LATEST daily snapshot (as_of; a stock summed over countries and plans, never over days), plus…

- `app`: string. app slug (as on the Apps page)
- `days`: integer 1-365. Default: 30.

### warehouse_schema (read)

Warehouse schema. DDL of every warehouse table plus the raw-metric -> canonical-family map and the known store ids.

No arguments: send an empty body.

### sql_query (read)

SQL query (read-only). Escape hatch: run ONE read-only SELECT/WITH statement over the warehouse on a readonly connection (PRAGMA query_only).

- `sql` (required): string. a single SELECT or WITH statement, no trailing semicolon needed

### apple_reviews_recent (read)

Apple: recent App Store reviews (warehouse). Reads the most recent App Store reviews for an app from the warehouse (populated by the apple sync).

- `app` (required): string.
- `limit`: integer 1-200. default 20

### apple_review_reply (write)

Apple: reply to an App Store review (gated). Posts (or replaces) the developer response under one App Store review.

- `app` (required): string. app slug, Apple app id, or app name
- `review_id` (required): string. customerReviews resource id, as returned by reviews_unanswered / apple_reviews_recent
- `response_text` (required): string, ≤ 5970 chars. the public developer response, max 5970 chars
- `rationale` (required): string. why this reply — stored in the audit log
- `apply`: boolean. true = commit for real. Requires the confirmToken returned by the preceding dry-run of this exact call.
- `confirmToken`: string. The confirm_token from the dry-run preview (single use, 10-minute TTL).

### apple_report_status (read)

Apple: analytics report pipeline status. Shows the app's analyticsReportRequests (including the stopped_due_to_inactivity flag — Apple stops an ONGOING request that is never downloaded), the report ids for the four ASO reports, and the latest DAILY/WEEKLY…

- `app` (required): string.

### play_reviews_recent (read)

Play: recent reviews. Most recent Google Play reviews for an app from the warehouse (synced from the bucket's reviews/ exports).

- `app` (required): string.
- `limit`: integer 1-200. default 20

### play_review_reply (write)

Play: reply to a Google Play review (gated). Posts (or edits) the developer reply under one Google Play review.

- `app` (required): string. app slug or Play package name
- `review_id` (required): string. Play review id (gp:…), as returned by reviews_unanswered / play_reviews_recent
- `response_text` (required): string, ≤ 350 chars. the public developer reply, max 350 chars
- `rationale` (required): string. why this reply — stored in the audit log
- `apply`: boolean. true = commit for real. Requires the confirmToken returned by the preceding dry-run of this exact call.
- `confirmToken`: string. The confirm_token from the dry-run preview (single use, 10-minute TTL).

### play_store_performance (read)

Play: store listing performance. Store-listing visitors, acquisitions and conversion rate by month × traffic source (and by territory) from the warehouse.

- `app` (required): string.
- `months`: integer 1-24. how many recent months to include, default 3

### ms_analytics (read)

Microsoft Store analytics (warehouse). Acquisitions and installs by day for an app from the warehouse, plus the legacy funnel totals and data freshness.

- `app` (required): string.
- `days`: integer 1-365. lookback window, default 30

### ms_review_reply (write)

Microsoft Store review response (gated write). Posts the developer response under one Microsoft Store review through the Partner Center 'respond to reviews' API.

- `app` (required): string. app slug, app name, or Microsoft product id
- `review_id` (required): string. Partner Center review id (a GUID), as returned by reviews_unanswered / ms_reviews_recent
- `response_text` (required): string, ≤ 1000 chars. the public developer response, max 1000 chars
- `rationale` (required): string. why this response — stored in the listing_changes audit log
- `support_email`: string (email). support address Partner Center requires on every response; defaults to the listing's DefaultSupportEmail
- `apply`: boolean. true = commit for real. Requires the confirmToken returned by the preceding dry-run of this exact call.
- `confirmToken`: string. The confirm_token from the dry-run preview (single use, 10-minute TTL).

### ms_reviews_recent (read)

Microsoft Store recent reviews. Most recent Microsoft Store reviews for an app from the warehouse (synced from the Partner Center reviews endpoint).

- `app` (required): string.
- `limit`: integer 1-200. default 20

### registry_status (read)

Public-registry listing + download proxies. Per app: whether the official MCP Registry lists it (and under which server name@version), plus the latest public download/usage proxies — npm, PyPI, Docker Hub, Smithery — from the warehouse.

- `app`: string. app slug. Omit for every app.

### rank_check (read)

Live keyword-rank check (calls the storefronts now). LIVE one-off check: queries Apple (iTunes Search), Google Play (search page) and the Microsoft Store search endpoint RIGHT NOW and returns this app's 1-based position per keyword x country x store, plus the top-5…

- `app` (required): string. app slug
- `keywords`: array of string. up to 10 search terms. Default: this app's tracked keywords.
- `countries`: array of string. ISO-3166 alpha-2, e.g. ['us','ca']. Default: this app's configured countries.
- `stores`: array of "apple" | "play" | "ms". which store legs to query. Default: every store the app is actually listed on.

### store_search (read)

Live storefront search (top results for a keyword, as the public store paints them). LIVE: runs one keyword through the PUBLIC Apple App Store (iTunes Search) and/or Google Play search for one country and returns the top results IN ORDER with rank, app_id (Apple trackId / Play package), name, developer,…

- `keyword` (required): string, ≤ 100 chars. the search term, exactly as a user would type it
- `country` (required): string, ≤ 2 chars. ISO-3166 alpha-2 storefront, e.g. 'us'
- `store`: "apple" | "play" | "both". apple | play | both (default both)
- `limit`: integer 1-25. top N per store, default 10, max 25

### store_app_lookup (read)

Public listing facts for ONE app (competitor listing audit). LIVE: reads one app's PUBLIC product listing — Apple via the iTunes Lookup API (app id or bundle id), Google Play via the product page — for one country and returns name, developer, rating, ratings_count, price,…

- `store` (required): "apple" | "play". apple | play
- `id_or_package` (required): string. Apple numeric app id (or bundle id), or the Play package name
- `country` (required): string, ≤ 2 chars. ISO-3166 alpha-2 storefront, e.g. 'us'

### rank_history (read)

Keyword-rank history from the warehouse. Warehouse-only (no network): tracked keyword positions over the last N days per store leg x keyword x country, with latest / best / worst / change.

- `app` (required): string. app slug
- `keyword`: string. single keyword; omit for all tracked keywords
- `store`: "apple" | "play" | "ms". apple | play | ms; omit for all
- `days`: integer 1-365. lookback window in days (default 30)

### rank_keywords_get (read)

Read the tracked keyword sets. The keyword sets the ranks adapter tracks, per app (the Keywords page).

- `app`: string. app slug; omit for every app

### rank_keywords_set (write)

Replace one app's tracked keyword set. Replaces (not merges) the keywords and countries tracked for one app and saves the set.

- `app` (required): string. app slug
- `keywords` (required): array of string, ≤ 100 chars. full replacement set, at most 25; [] removes the app from tracking
- `countries`: array of string. ISO-3166 alpha-2, at most 10; defaults to us/ca/gb
- `apply`: boolean. true = commit for real. Requires the confirmToken returned by the preceding dry-run of this exact call.
- `confirmToken`: string. The confirm_token from the dry-run preview (single use, 10-minute TTL).
