---
title: "Top free/paid/grossing chart with day-over-day movement"
method: GET
path: "/api/v1/charts/top"
tags: ["Charts"]
---

# Top free/paid/grossing chart with day-over-day movement

`GET /api/v1/charts/top`

## Request

- Base URL: `https://api.trysonar.app`
- URL: `https://api.trysonar.app/api/v1/charts/top`
- Auth: one of:
  - none
  - HTTP bearer

Market-wide store chart (no tracked apps needed). 1 credit. The first request for a (store, country, chart, category) combo on a given day fetches it live and snapshots it; later requests that day are served from the snapshot. `summary`, `movers` and `droppedApps` always describe the whole top 200 — `limit` truncates `entries` only. A combo requested for the first time has no previous snapshot, so `previousMeasuredAt` is null and every `delta` is null. A live fetch is awaited only until the request deadline (API_REQUEST_DEADLINE_MS, or `X-Sonar-Time-Budget-Ms` minus 2s); past it the newest stored snapshot (≤7 days old) is served with `stale: true` and its `measuredAt` date while the fetch finishes in the background, so the next request is fresh. With nothing stored the endpoint returns 503 `refresh_in_progress` (Retry-After 20).

## Query parameters

- `store` 'ios' | 'android'
- `country` string
- `chart` 'free' | 'paid' | 'grossing'
- `category` string
- `limit` integer

## Headers

- `X-Sonar-Time-Budget-Ms` integer

## Response `200`

Chart entries with movement, summary, movers and dropped apps

- object
  - `data` object
    - `store` string
    - `country` string
    - `chart` string
    - `category` string
    - `measuredAt` string, date
    - `previousMeasuredAt` string, date, nullable: Date movement is computed against
    - `stale` boolean: True when an older snapshot (≤7 days) was served instead of today's — the live fetch failed or didn't finish before the request deadline. `measuredAt` is the served snapshot's date.
    - `summary` object
      - `total` integer
      - `newToday` integer
      - `dropped` integer
    - `entries` object[]
      - `rank` integer
      - `storeId` string
      - `numericId` integer, nullable: iOS only — numeric Apple id (storeId is the bundle id)
      - `name` string
      - `iconUrl` string, nullable
      - `developer` string, nullable
      - `rating` number, nullable
      - `delta` integer, nullable: previousRank - rank (positive = moved up); null when new or no previous snapshot
      - `isNew` boolean
    - `movers` object[]: Up to 10 biggest movers (|delta| >= 3) across the whole chart
      - `storeId` string
      - `name` string
      - `iconUrl` string, nullable
      - `rank` integer
      - `delta` integer
    - `droppedApps` object[]: Apps in the previous snapshot but not in this one
      - `storeId` string
      - `name` string
      - `iconUrl` string, nullable
      - `previousRank` integer

## Other responses

- `400`: Missing or invalid request parameters
- `401`: Missing or invalid API key
- `429`: Rate limited — see `error.reason`: daily_limit (Indie plan: 1,000 requests/day, resets 00:00 UTC; see `reset_at`), anonymous_daily_limit / batch_too_large (keyless free tier), store_rate_limited or concurrency_limit (transient, honor `retry_after_seconds`)
- `500`: Unexpected server error
- `503`: No snapshot ≤7 days old is stored and today's can't be served yet. `reason: refresh_in_progress` (code unavailable, Retry-After 20): the first fetch for this combo is still running in the background — retry and it's a cache hit. `reason: store_unavailable` (Retry-After 30): the store fetch failed. Both are refunded.

---

[API](https://skmtc.dev/trysonar/apis/sonar-api.md) · [All operations](https://skmtc.dev/trysonar/apis/sonar-api/llms.txt) · [OpenAPI document](https://skmtc.dev/trysonar/apis/sonar-api/revisions/73fe8bc46522?raw)
