Running a Shopify store?Bestappify for merchants
Best Appify LogoBest Appify

For developers

API & MCP reference

Everything BestAppify knows about your apps is available over HTTP, and over MCP so Claude and Cursor can query it for you. Same data, same numbers as the dashboard — it all reads from one layer.

Authentication

Create a key in Dashboard → API & MCP and send it as a bearer token. Keys are shown once and stored only as a hash, so if you lose one, create another. X-API-Key works too, if a header called Authorization is awkward where you are.

curl -H "Authorization: Bearer ba_live_xxx" \
  https://bestappify.app/api/v1/apps

A key can read everything on your account, including revenue. Keep it server-side.

Responses

Every success is { data, meta } and every failure is { error: { code, message } }. Branch on code, not on the message — codes are stable, wording is not.

{
  "data": {
    "app": { "app": "fast-bundle", "name": "FBP | Fast Bundle Product", "app_id": 6174 },
    "keywords": [
      {
        "keyword": "product bundles",
        "keyword_id": 31,
        "position": 12,
        "last_known_position": 12,
        "previous_position": 17,
        "change": 5,
        "as_of": "2026-07-28",
        "is_current": true,
        "difficulty": 68,
        "competition": 41,
        "demand": 73
      }
    ]
  },
  "meta": { "generated_at": "2026-07-28T09:14:22.031Z", "count": 1 }
}

Three habits worth knowing, because they run through the whole API. A position of null means the app was not in the search results we captured — not that it has no rank. is_current: false means the rank history matched to your app lags the newest crawl, usually by a day, when a crawl saw the listing before matching it to our catalog — position is still the live figure. And where a number is reconstructed rather than reported (MRR, churn, trials, keyword demand), the response says so rather than presenting it as Shopify's own.

Rate limits

120 requests per minute on every plan, and a daily allowance by tier — 100 on Free, 1,000 on Growth, 10,000 on Pro — counted per account across all your keys. Every response carries X-RateLimit-Remaining; a 429 carries Retry-After in seconds and names the plan with more headroom. MCP tool calls draw on the same allowance.

MCP server

One endpoint — https://bestappify.app/api/mcp — over streamable HTTP, authenticated with the same key, on every plan including Free. 40 tools (all read-only unless your key has write scope): your apps, keyword rankings and history, opportunities, competitors and overlap, review intelligence, listing analytics, revenue and churn, real merchant search terms with the positions they saw, search headroom, week-over-week traffic and which store installed, plus forum mentions, catalog search, app details, the leaderboard, movers and niches.

The dialog opens pre-filled. Swap YOUR_API_KEY for a key from your dashboard — free plan included, no card.

Claude Code

claude mcp add --transport http bestappify https://bestappify.app/api/mcp --header "Authorization: Bearer YOUR_API_KEY"

Cursor / Windsurf — ~/.cursor/mcp.json

{
  "mcpServers": {
    "bestappify": {
      "url": "https://bestappify.app/api/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}

Claude Desktop — claude_desktop_config.json

{
  "mcpServers": {
    "bestappify": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://bestappify.app/api/mcp",
        "--header",
        "Authorization: Bearer YOUR_API_KEY"
      ]
    }
  }
}

Then ask things like

  • “Which of my keywords dropped this week, and who took the position?”
  • “Compare my review complaints with my three closest competitors.”
  • “What did churn do after the pricing change in March?”
  • “Find categories with fewer than 40 apps where the leader holds under half the reviews.”

Endpoints

Base URL https://bestappify.app. Every {app} accepts the handle, the listing URL, or an id from /api/v1/apps.

Account

GET/api/v1/me

Your account, apps and rate limits

Confirms the key works and returns the app references every other endpoint accepts, plus how much of your rate limit you have used.

GET /api/v1/me

Your apps

GET/api/v1/apps

List your tracked apps

Every app on your account. The `app` field is the handle used in the paths below; `tracked_app_id` and `app_id` are returned for callers that prefer ids.

GET /api/v1/apps

GET/api/v1/apps/{app}/keywords

Keyword rankings

Your tracked keywords for one app with today's position, the week-over-week change, and the difficulty, competition and demand indices. `position` is the live standing and is null when the app did not appear in the latest crawl. `last_known_position` and `change` come from the rank history keyed to your app in our catalog; `is_current: false` means that history is a little behind the newest crawl, not that the app stopped ranking.

format
(query, optional) Set to csv for a CSV download.
app
(path, required) App handle, listing URL, tracked_app_id or app_id.

GET /api/v1/apps/fast-bundle/keywords

GET/api/v1/apps/{app}/keywords/{keyword}

One keyword, with full rank history

Daily rank history for a single tracked keyword, oldest first, plus its current standing. `{keyword}` is the term (URL-encoded) or its keyword_id.

app
(path, required) App handle or id.
keyword
(path, required) Keyword term, URL-encoded, or its keyword_id.

GET /api/v1/apps/fast-bundle/keywords/product%20bundles

GET/api/v1/apps/{app}/opportunities

Keyword opportunities

Keywords one of your tracked competitors ranks for and this app does not, with the competitor's position.

app
(path, required) App handle or id.

GET /api/v1/apps/fast-bundle/opportunities

GET/api/v1/apps/{app}/competitors

Competitors and keyword overlap

Your tracked competitor set with current rank and rank change, plus the keyword overlap matrix showing where each of you places.

format
(query, optional) Set to csv for a CSV download.
app
(path, required) App handle or id.
include
(query, optional) Set to `timeline` to also return the daily rank timeline for every app.

GET /api/v1/apps/fast-bundle/competitors

GET/api/v1/apps/{app}/reviews

Review intelligence

Rating distribution, monthly review trend, countries, the AI theme analysis (what merchants love, complain about and ask for) and a comparison against your tracked competitors.

format
(query, optional) Set to csv for a CSV download.
app
(path, required) App handle or id.
recent
(query, optional) How many individual recent reviews to include. Default 10, max 50.

GET /api/v1/apps/fast-bundle/reviews?recent=25

GET/api/v1/apps/{app}/analytics

Listing analytics

Listing views, install clicks, installs, click-through and conversion from the Google Analytics property linked to the app, daily and totalled. Returns `connected: false` when no property is linked.

app
(path, required) App handle or id.
days
(query, optional) Window in days. Default 90, max 365.

GET /api/v1/apps/fast-bundle/analytics?days=30

GET/api/v1/apps/{app}/search

Real merchant searches and the positions they saw

The search terms merchants actually typed to reach your listing, the position they actually saw (with Shopify's sponsored slots removed so it matches organic rank), plus device, country, locale and Install clicks per term. Read from the Google Analytics property linked to the app. This is the merchant's own answer, not our crawler's — `/keywords` is the crawler, works for any app including competitors, and needs no setup. Add `?format=csv` for a spreadsheet. Returns `connected: false` when no property is linked.

app
(path, required) App handle or id.
days
(query, optional) Window in days. Default 30, max 365.
device
(query, optional) Filter to one device: desktop, mobile or tablet.
country
(query, optional) Filter to one ISO country code.
search_type
(query, optional) Filter to organic, paid, ai or other.
format
(query, optional) Set to `csv` for a CSV download.

GET /api/v1/apps/fast-bundle/search?days=30&device=mobile

GET/api/v1/apps/{app}/search/opportunities

Keyword headroom, weighted by how beatable the field is

Ranks the terms already sending you traffic by how much more they could send. Estimates the demand behind your current views, asks what position 3 would have yielded, then discounts it by how strong the apps ranked above you are according to our own weekly crawl — `winnability`, `apps_above` and `median_reviews_above` are that discount, and no GA4-only tool can compute them. Scores are relative within one app's keywords, never across apps. Add `?format=csv` for a spreadsheet.

app
(path, required) App handle or id.
days
(query, optional) Window in days. Default 90, max 365.
limit
(query, optional) Rows to return. Default 50, max 200.
format
(query, optional) Set to `csv` for a CSV download.

GET /api/v1/apps/fast-bundle/search/opportunities?limit=20

GET/api/v1/apps/{app}/search/weekly

Week-over-week search traffic, including what stopped

The same comparison as the Tuesday email, as data: views and Install clicks this week against last, per traffic source and per keyword, with `gained`, `lost`, `new` and — the one most worth automating against — `stopped`, the terms that drove traffic last week and none this week. The window is fixed at two seven-day periods ending three days ago because Google keeps revising recent days; `window` gives the exact dates.

app
(path, required) App handle or id.

GET /api/v1/apps/fast-bundle/search/weekly

GET/api/v1/apps/{app}/installs/shops

Which store installed, and how it found you

Installs resolved to the store that made them: shop domain, shop id, date, device, country, and the search term most likely behind it (labelled as the inference it is — Shopify's install event is server-side and carries no page). Pass `shop_url` up to 50 times, or once comma-separated, to check specific stores; `not_found` lists the ones we have no install for. Needs `shop_url` registered as a GA4 custom dimension — when it isn't, the response says so in `note` rather than returning an empty list that looks like zero installs.

app
(path, required) App handle or id.
shop_url
(query, optional) Shop domain to look up. Repeatable, or comma-separated. Up to 50. Omit for every store.
days
(query, optional) Window in days. Default 365, max 365.
limit
(query, optional) Stores to return. Default 200, max 1000.
format
(query, optional) Set to `csv` for a CSV download.

GET /api/v1/apps/fast-bundle/installs/shops?shop_url=acme.myshopify.com

GET/api/v1/apps/{app}/revenue

Revenue and churn

Monthly MRR, churn rate, trials, installs, uninstalls and net revenue from the Shopify Partner API. MRR and churn are derived from the subscription charge lifecycle — the Partner API publishes neither directly. Returns `connected: false` when the app is not linked to a Partner app.

app
(path, required) App handle or id.
months
(query, optional) Months of history. Default 12, max 36.

GET /api/v1/apps/fast-bundle/revenue?months=24

GET/api/v1/apps/{app}/intelligence

App intelligence

Rank trajectory over time, position in each category, nearest competitors by category rank, and how the app's entry price compares with its peers.

app
(path, required) App handle or id.

GET /api/v1/apps/fast-bundle/intelligence

GET/api/v1/forum/mentions

Shopify Community threads that mention your apps

Forum posts matching your apps' names or the keywords you track — most usefully, merchants asking for an app like yours. `is_strong` marks the ones we would email you about (your app named, or the term in a thread title or opening post) as opposed to a term appearing in a mid-thread reply; both are returned so you can apply your own bar. Add ?format=csv for a spreadsheet.

filter
(query, optional) active (default, hides dismissed), unread, dismissed, or all.
limit
(query, optional) 1–100, default 50.
offset
(query, optional) Rows to skip, for paging.
format
(query, optional) Set to csv for a CSV download.

GET /api/v1/forum/mentions?filter=unread

GET/api/v1/apps/changes

What changed on your listings — and your competitors'

Dated App Store listing changes for your apps and the rivals you track: taglines, descriptions, icons, screenshots, integrations, and listings leaving the store. `is_mine` separates yours from theirs. Covers competitors because it reads our own catalog crawl rather than your analytics — so these are events you can line up against your rank history. Only includes changes since we began recording. Add ?format=csv for a spreadsheet.

days
(query, optional) Look-back window, 1–365 (default 30).
limit
(query, optional) Rows to return, 1–300 (default 100).
format
(query, optional) Set to csv for a CSV download.

GET /api/v1/apps/changes?days=30

Market data

GET/api/v1/market/apps

Search the App Store catalog

Search every app we hold by name — not only your own. This is how you find a competitor's handle before looking it up.

q
(query, required) Search term matched against the app name.
limit
(query, optional) Results to return. Default 20, max 50.

GET /api/v1/market/apps?q=bundle

GET/api/v1/market/apps/{app}

One catalog app

Any app in our catalog by handle, listing URL or id: rating, review count, description, categories, pricing plans and listing status.

app
(path, required) App handle, listing URL or id.
include
(query, optional) Set to `intelligence` to add rank trajectory and the pricing benchmark.

GET /api/v1/market/apps/judge-me-reviews?include=intelligence

GET/api/v1/market/leaderboard

Top apps by review volume

The App Store leaderboard, overall or for one category. Ranked by review count, because Shopify publishes no install numbers.

format
(query, optional) Set to csv for a CSV download.
category
(query, optional) Category id or exact title. Omit for the overall board.
limit
(query, optional) Apps to return. Default 25, max 100.

GET /api/v1/market/leaderboard?category=Product%20bundles

GET/api/v1/market/movers

Week-over-week rank movement

Which apps climbed and which fell over the last week, from our daily rank snapshots. `change` is positive for a climb; `rank_a_week_ago: null` marks an app new to the ranking.

category
(query, optional) Category id or exact title.
limit
(query, optional) Apps to return. Default 25, max 100.

GET /api/v1/market/movers

GET/api/v1/market/keywords

The keyword directory

Every keyword merchants search on the App Store, harvested from the store's own autocomplete, with how many apps rank for it, who leads, and the difficulty, competition and demand indices. `demand` and `difficulty` are relative 0-100 indices built from the review volume of each term's top 10 — not search volume, which Shopify publishes nowhere. Add `format=csv` for a spreadsheet.

q
(query, optional) Filter to keywords containing this text.
sort
(query, optional) `demand` (default), `difficulty` (easiest first), `apps` or `newest`.
brand
(query, optional) Brand keywords — searches for a named app — are excluded by default. `only` returns just those, `include` returns both.
limit
(query, optional) Keywords to return. Default 50, max 200 (1,000 for CSV).
offset
(query, optional) Rows to skip, for paging. `meta.total` is the full count.
format
(query, optional) Set to `csv` to download instead of returning JSON.

GET /api/v1/market/keywords?sort=difficulty&limit=50

GET/api/v1/market/keywords/{keyword}

Who ranks for one keyword

Every app ranking for one App Store search, in order, with its rating, review count, entry price, category and the position it held a week earlier. Works for any keyword in the directory, not only ones you track.

keyword
(path, required) The keyword, URL-encoded, or its slug. Both `product bundles` and `product-bundles` resolve.
format
(query, optional) Set to `csv` to download the ranked apps instead of returning JSON.

GET /api/v1/market/keywords/product-bundles

GET/api/v1/market/niches

Category growth and concentration

Every category with its app count, growth and the leader's share of reviews — for judging whether a niche is crowded or open.

limit
(query, optional) Categories to return. Default 30, max 100.

GET /api/v1/market/niches

GET/api/v1/market/categories

List categories

The App Store categories we hold at least five apps for, with counts. These ids and titles are what `?category=` accepts.

GET /api/v1/market/categories

Error codes

CodeHTTPMeaning
missing_api_key401No Authorization header or X-API-Key header was sent.
invalid_api_key401The key does not exist or has been revoked.
rate_limited429More than 120 requests in the last minute. Retry-After says when to try again.
daily_limit_reached429More than 10,000 requests in the last 24 hours.
app_not_found404The app reference is not one of your tracked apps.
keyword_not_tracked404That keyword is not tracked for the given app.
keyword_not_found404No keyword in the public directory matched that term or slug.
category_not_found404No category matched the id or title given.
missing_query400A required query parameter was absent.
app_not_in_catalog409The app is tracked but our crawler has not matched it to a listing yet.
service_unavailable503The API is misconfigured or the database is unreachable. Safe to retry.
internal_error500An unexpected failure on our side.

Ready to pull your data?

Keys take a few seconds to create, and the same one drives the API and the MCP server.

Create an API key