For developers
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.
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/appsA key can read everything on your account, including revenue. Keep it server-side.
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.
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.
One endpoint — https://bestappify.app/api/mcp — over streamable HTTP, authenticated with the same key, on every plan including Free. 60 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
Base URL https://bestappify.app. Every {app} accepts the handle, the listing URL, or an id from /api/v1/apps.
/api/v1/meYour 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
/api/v1/appsList 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
/api/v1/apps/{app}/keywordsKeyword 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.
GET /api/v1/apps/fast-bundle/keywords
/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.
GET /api/v1/apps/fast-bundle/keywords/product%20bundles
/api/v1/apps/{app}/opportunitiesKeyword opportunities
Keywords one of your tracked competitors ranks for and this app does not, with the competitor's position.
GET /api/v1/apps/fast-bundle/opportunities
/api/v1/apps/{app}/competitorsCompetitors and keyword overlap
Your tracked competitor set with current rank and rank change, plus the keyword overlap matrix showing where each of you places.
GET /api/v1/apps/fast-bundle/competitors
/api/v1/apps/{app}/reviewsReview 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.
GET /api/v1/apps/fast-bundle/reviews?recent=25
/api/v1/apps/{app}/analyticsListing 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.
GET /api/v1/apps/fast-bundle/analytics?days=30
/api/v1/apps/{app}/searchReal 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.
GET /api/v1/apps/fast-bundle/search?days=30&device=mobile
/api/v1/apps/{app}/search/opportunitiesKeyword 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.
GET /api/v1/apps/fast-bundle/search/opportunities?limit=20
/api/v1/apps/{app}/search/weeklyWeek-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.
GET /api/v1/apps/fast-bundle/search/weekly
/api/v1/apps/{app}/installs/shopsWhich 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.
GET /api/v1/apps/fast-bundle/installs/shops?shop_url=acme.myshopify.com
/api/v1/apps/{app}/revenueRevenue 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.
GET /api/v1/apps/fast-bundle/revenue?months=24
/api/v1/apps/{app}/intelligenceApp 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.
GET /api/v1/apps/fast-bundle/intelligence
/api/v1/forum/mentionsShopify 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.
GET /api/v1/forum/mentions?filter=unread
/api/v1/apps/changesWhat 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.
GET /api/v1/apps/changes?days=30
/api/v1/market/appsSearch 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.
GET /api/v1/market/apps?q=bundle
/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.
GET /api/v1/market/apps/judge-me-reviews?include=intelligence
/api/v1/market/leaderboardTop apps by review volume
The App Store leaderboard, overall or for one category. Ranked by review count, because Shopify publishes no install numbers.
GET /api/v1/market/leaderboard?category=Product%20bundles
/api/v1/market/moversWeek-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.
GET /api/v1/market/movers
/api/v1/market/keywordsThe 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.
GET /api/v1/market/keywords?sort=difficulty&limit=50
/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.
GET /api/v1/market/keywords/product-bundles
/api/v1/market/nichesCategory 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.
GET /api/v1/market/niches
/api/v1/market/categoriesList 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
/api/v1/market/rank-driversWhat listing edits actually do to rank
Across every app we crawl, what each kind of listing edit measurably did to search rank: how many edits of that kind were measured, the median places moved, and how the outcomes split between better, worse and no change. Correlational with small samples — read `coverage` and each field's `changes_measured` before quoting a figure, and carry the `caveat` into whatever you write.
GET /api/v1/market/rank-drivers?days=120
/api/v1/market/keywords/{keyword}/rank-causesWho moved on this term, and what they had just changed
Every app we saw move at least N places on one search term in the window, with the listing edits we logged for it shortly before the move. An empty `listing_changes_before` means we logged no edit, not that nothing happened.
GET /api/v1/market/keywords/product-bundles/rank-causes
/api/v1/market/apps/{app}/change-impactOne app's edits and what each one did
Any catalog app's logged listing edits, each with the rank effect measured on the keywords it ranked for either side of the edit. Works for apps you do not own, which is how you read a competitor's playbook. `keywords_entered`/`keywords_exited` are null when `crawl_coverage_comparable` is false — our crawl volume differed too much either side for those counts to mean anything.
GET /api/v1/market/apps/fast-bundle/change-impact
/api/v1/apps/{app}/listing/reviewSenior review of a listing change you are about to make
Send the change you intend to ship — new name, tagline or description, the terms it targets, and your reasoning — and get back deterministic checks plus a prioritised critique. `checks` is computed from the database and is authoritative; `review` is judgement over the same evidence. Paid plans only, capped per account per day; an identical body is served from cache and does not count against the cap.
POST /api/v1/apps/fast-bundle/listing/review
/api/v1/apps/{app}/screenshots/headlinesWhat rivals write ON their screenshots
The marketing copy painted on the listing screenshots — yours, the apps ranked above you on your category boards, and the most-reviewed apps in the App Store — transcribed off the images by a vision model, with the layout each uses. The alt-text caption is in the listing HTML anyone can fetch; the headline is not, and it is the one a merchant scrolling the gallery actually reads. Free on every plan: it returns transcriptions that already exist and never calls a model.
GET /api/v1/apps/fast-bundle/screenshots/headlines?scope=category
/api/v1/apps/{app}/screenshots/headlinesRewrite your screenshot headlines to the leaders' pattern
Names the headline formula the leading apps share — counted from their transcribed headlines, every example quoted verbatim — says whether your gallery follows it, and rewrites the slots that don't in your own voice. Paid plans only and capped per day on the same budget as the listing reviewer; an uncached run transcribes unread rival galleries and writes a review, so expect up to ninety seconds. If your client times out waiting, retry — the review is written before the response is sent, so the retry is a cache hit. Without `refresh` a cached review is served in under a second and does not count against the cap.
POST /api/v1/apps/fast-bundle/screenshots/headlines
/api/v1/apps/{app}/listing/historyWhat we told you before, and whether it worked
Every review and proposal we have given for this app, each matched to the listing changes that landed afterwards, whether the live copy matches ours or your draft or neither, which flagged checks the live copy still fails, and the rank effect measured either side. Free on every plan — reading your own history spends no completion. Call it before starting a listing cycle; the review endpoint reads it too and will flag a repeat of advice that was ignored.
GET /api/v1/apps/fast-bundle/listing/history
/api/v1/apps/{app}/listing/proposalHave us write the listing rewrite
Picks the terms worth going after from this app's rank data and its gaps against competitors, writes a name, tagline and description opening around them, states what the measured history suggests, and names the risks. The output is run back through the reviewer's own checks, so a failing check is authoritative over the copy. Paid plans only, same daily cap as the review endpoint.
POST /api/v1/apps/fast-bundle/listing/proposal
| Code | HTTP | Meaning |
|---|---|---|
| missing_api_key | 401 | No Authorization header or X-API-Key header was sent. |
| invalid_api_key | 401 | The key does not exist or has been revoked. |
| rate_limited | 429 | More than 120 requests in the last minute. Retry-After says when to try again. |
| daily_limit_reached | 429 | More than 10,000 requests in the last 24 hours. |
| app_not_found | 404 | The app reference is not one of your tracked apps. |
| keyword_not_tracked | 404 | That keyword is not tracked for the given app. |
| keyword_not_found | 404 | No keyword in the public directory matched that term or slug. |
| category_not_found | 404 | No category matched the id or title given. |
| missing_query | 400 | A required query parameter was absent. |
| app_not_in_catalog | 409 | The app is tracked but our crawler has not matched it to a listing yet. |
| service_unavailable | 503 | The API is misconfigured or the database is unreachable. Safe to retry. |
| internal_error | 500 | An unexpected failure on our side. |
| upgrade_required | 402 | The listing reviewer is on the paid plans. The data behind it is open on every plan. |
| review_limit_reached | 429 | The account's daily AI review allowance is spent. Identical requests are served from cache and do not count. |
| nothing_to_review | 400 | The review body carried no draft and no plan to critique. |
| invalid_body | 400 | The POST body was not valid JSON. |
Keys take a few seconds to create, and the same one drives the API and the MCP server.
Create an API key