StoreTally for developers

The site's figures, as JSON: listings and their history, reviews and review search, charts and climbers, topics, categories, store statistics and the weekly edition. Read-only and free. The same reads are an MCP server for AI agents.

Limits and keys

Per minutePer day (UTC)
Without a key (per address)30500
With a key12020,000 (unless raised for your key)

Keys are free: create an account (an email and a password - nothing is sent to the address) and make up to 3 keys at your API keys. A key is shown once; StoreTally keeps only a hash of it. Send it in a header, never in the URL: Authorization: Bearer st_... or X-Api-Key: st_....

A key that is wrong, revoked or belongs to a disabled account - or an Authorization header in another scheme - is refused with 401 and WWW-Authenticate: it never quietly falls back to the anonymous limits. To use those, send no key. More than 30 invalid keys in a minute from one address are refused with 429 before any key is read.

Counting, errors and retries

  • Every call that passes the per-minute limit counts against the day - a 304 or an error included; a call refused per minute does not. The day's count restarts at 00:00 UTC.
  • Every v1 answer that reaches the limits carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (the day's: seconds until 00:00 UTC), and RateLimit-Policy naming both windows. Past either limit the answer is 429 with Retry-After.
  • Searches - /listings with q, /reviews/search, and a listing's reviews with q - share a small pool across every caller; when it is full the answer is 429 with Retry-After: 5.
  • Some figures are computed in the background, never while a call waits. Until they are (for a short while after a restart) these answer 503 with Retry-After: 60: /categories/{store}, /stores/{store}/stats, /week before the first edition is built, and /reviews/search with a category, which is matched against the store's category list. If the day's usage cannot be checked for a moment, any call is 503 with Retry-After: 5.
  • An answer cut short by its time budget says "partial": true and is not cacheable; narrow the request or try again later.
  • Errors are problem JSON (application/problem+json, RFC 9457) whose detail says what to change: 400 for a parameter that is not valid, 404 for an unknown store, listing, topic or address, 405 (with Allow) for a real address asked with a method other than GET.
  • Answers carry a weak ETag and Cache-Control: private, max-age=60; send the ETag back as If-None-Match for a 304.

Endpoints

All are GETs under https://storetally.com/api/v1; the full description, every parameter and shape, is the OpenAPI document (/api/v1/openapi.json, free of keys and limits). Stores are chrome, edge, firefox, apple and googleplay. A listing's size is its users or installs - on Apple, its ratings.

  • GET /listings - search and browse, one store or every store: store, category (a name from /categories/{store}), tag, minSize/maxSize (these need a store, since size means different things on Apple), sort (installs, ratingcount, rating, growth7d, growthpct7d, growth30d, ratinggrowth7d, updated, created, firstseen, name, and rankclimb with a store; the store's size by default), order (largest first by default; name sorts A to Z), q (2+ characters), limit (1 to 100, 50 by default), cursor.
  • GET /listings/{store}/{id} - figures, growth windows, store and category ranks with the 7-day climb, topics, star histogram, chart positions.
  • GET /listings/{store}/{id}/history - one snapshot per UTC day (the day's latest) and the daily store ranks, oldest first: days 1 to 730 (365 by default). A window with more than 2,000 crawls keeps its newest days and says "truncated": true.
  • GET /listings/{store}/{id}/reviews - stored reviews, newest first, 20 a page: stars (all, critical, positive), q. Reviews are not collected for Chrome.
  • GET /reviews/search - listings with 3 or more matching reviews, ranked by matches, 50 a page: q (required: words, "a phrase", OR, prefix*), store, stars (critical by default), tag, category (a name or the site's slug; needs a store).
  • GET /charts/{store} - the store's charts (chart, category), including the live climbers chart on every store.
  • GET /tags, GET /tags/{slug}/{store} - topics with their live listings per store, and a topic's listings.
  • GET /categories/{store}, GET /stores/{store}/stats, GET /week, GET /week/{id} (an ISO week, like 2026-W40).

Lists are paged with a cursor: pass the answer's nextCursor as cursor with the same filters and the same limit (a cursor carries the page size it was issued for; another limit is a 400). It is null on the last page offered: up to 200 pages of listings, 10 of a listing's reviews, 20 of review search.

curl -H "Authorization: Bearer st_..." \
  "https://storetally.com/api/v1/listings?store=chrome&sort=growth7d&limit=10"

curl "https://storetally.com/api/v1/reviews/search?q=crash&store=googleplay"

MCP server

https://storetally.com/mcp (Streamable HTTP) offers the same reads as tools for AI agents: search_listings, get_listing, get_listing_history, top_charts, climbers, search_reviews, list_tags, tag_listings and store_stats. Send your key the same way as to the API, in a header. Each tool call counts as one call against the day's allowance; every request to it counts against the minute's.

It does not use OAuth: there is no sign-in flow or OAuth discovery (/.well-known/oauth-... answers 404). A key goes in the Authorization: Bearer or X-Api-Key header of your client's configuration.

Use a key with an MCP client. Without one, the anonymous limits are per address - and a client that calls from a shared address shares them with everyone else there. Claude's custom connectors (Claude desktop and claude.ai) call from Anthropic's servers, so every keyless connector user shares one anonymous allowance: 30 calls a minute and 500 a day across all of them. Use one of the configurations below with your key instead.

Claude Code

claude mcp add --transport http storetally https://storetally.com/mcp \
  --header "Authorization: Bearer st_..."

Claude desktop (claude_desktop_config.json, through mcp-remote, so your key is sent from your machine)

{
  "mcpServers": {
    "storetally": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://storetally.com/mcp",
        "--header",
        "Authorization:${AUTH_HEADER}"
      ],
      "env": { "AUTH_HEADER": "Bearer st_..." }
    }
  }
}

The header value is passed through env, with no space after Authorization:, because some clients (Claude desktop on Windows among them) break arguments that contain spaces. A custom connector added in Claude's settings with just the URL works too, but at the shared anonymous allowance described above.

Cursor (.cursor/mcp.json)

{
  "mcpServers": {
    "storetally": {
      "url": "https://storetally.com/mcp",
      "headers": { "Authorization": "Bearer st_..." }
    }
  }
}

VS Code and other clients (.vscode/mcp.json, or the client's own equivalent)

{
  "servers": {
    "storetally": {
      "type": "http",
      "url": "https://storetally.com/mcp",
      "headers": { "Authorization": "Bearer st_..." }
    }
  }
}

Review text in answers is written by store users: the API and the tools return it as data, and agents should treat it so.