# Get Apple charts

Read the latest stored Apple free, paid or grossing chart for one storefront and category.

## Contract status

Preview contract: these routes and tools are implemented in the shared contract, but the public host, customer API access and OAuth flow have not been deployment verified.

## Endpoint: GET /v1/charts

- Method: `GET`
- Path: `/v1/charts`
- Authentication: Bearer API credential or OAuth access token
- Required scope: `context:read`
- MCP tool: `get_apple_charts`

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `country` | query | ^[a-z]{2}$ | no | Apple storefront. Defaults to us. Coverage is us, gb, fr, de, br, be. |
| `categoryId` | query | all or digits | no | Overall (all) or an Apple category id. Defaults to all. |
| `chartType` | query | free | paid | grossing | no | Apple chart type. Defaults to free. Grossing is a position, not dollars. |
| `view` | query | rankings | gainers | losers | new-entrants | new-releases | no | Leaderboard slice. Defaults to rankings. |
| `limit` | query | integer, 1–100 | no | Page size. Defaults to 100. |

## Known errors

- 400 invalid_request for an invalid filter, identity or limit.
- 401 authentication_required for a missing or invalid credential.
- 402 subscription_required when access is not entitled.
- 403 authorization_required for a missing scope or unavailable preview access.
- 404 not_found when the record is outside the accessible catalogue.
- 429 rate_limited; honor Retry-After when present.

## Request

Send country, categoryId, chartType, view and limit as query parameters. This GET never calls Apple.

```
curl --get 'https://api.peekpanda.com/v1/charts' \
  --header "Authorization: Bearer $PEEKPANDA_TOKEN" \
  --header 'x-genviral-tenancy: personal' \
  --data-urlencode 'country=us' \
  --data-urlencode 'categoryId=all' \
  --data-urlencode 'chartType=free' \
  --data-urlencode 'view=rankings' \
  --data-urlencode 'limit=100'
```

## Response

Matches appleChartPageSchema. snapshot.day is a date. releaseDate is a date or null. iconUrl and appUrl are URLs when present. estimates are optional. 0000000000 is a synthetic store ID.

```
{
  "query": {
    "country": "us",
    "categoryId": "all",
    "chartType": "free",
    "view": "rankings",
    "limit": 100
  },
  "coverage": {
    "countries": [
      {
        "code": "us",
        "name": "United States"
      },
      {
        "code": "gb",
        "name": "United Kingdom"
      },
      {
        "code": "fr",
        "name": "France"
      },
      {
        "code": "de",
        "name": "Germany"
      },
      {
        "code": "br",
        "name": "Brazil"
      },
      {
        "code": "be",
        "name": "Belgium"
      }
    ],
    "categories": [
      {
        "id": "all",
        "name": "Overall"
      }
    ],
    "chartTypes": [
      "free",
      "paid",
      "grossing"
    ]
  },
  "snapshot": {
    "id": "chart-snapshot-example",
    "day": "2026-09-06",
    "sourceUpdatedAt": "2026-09-06T00:00:00.000Z",
    "fetchedAt": "2026-09-06T00:15:00.000Z",
    "depth": 100,
    "previousDay": "2026-09-05",
    "stale": false
  },
  "summary": {
    "trackedApps": 1,
    "rankedApps": 1,
    "gainers": 0,
    "losers": 0,
    "newEntrants": 0,
    "newReleases": 0
  },
  "insights": {
    "categoryMix": [
      {
        "categoryId": "6013",
        "name": "Health & Fitness",
        "count": 1
      }
    ],
    "releaseAges": [
      {
        "label": "1-3years",
        "count": 1
      }
    ],
    "categoryLeaders": [
      {
        "categoryId": "6013",
        "name": "Health & Fitness",
        "storeId": "0000000000",
        "appName": "Example Calorie Tracker",
        "iconUrl": "https://example.com/icon.png",
        "day": "2026-09-06",
        "depth": 100
      }
    ],
    "storefronts": [
      {
        "country": "us",
        "day": "2026-09-06",
        "depth": 100
      },
      {
        "country": "gb",
        "day": "2026-09-06",
        "depth": 100
      },
      {
        "country": "fr",
        "day": "2026-09-06",
        "depth": 100
      },
      {
        "country": "de",
        "day": "2026-09-06",
        "depth": 100
      },
      {
        "country": "br",
        "day": "2026-09-06",
        "depth": 100
      },
      {
        "country": "be",
        "day": "2026-09-06",
        "depth": 100
      }
    ]
  },
  "data": [
    {
      "storeId": "0000000000",
      "name": "Example Calorie Tracker",
      "developer": "Example Labs",
      "iconUrl": "https://example.com/icon.png",
      "appUrl": "https://apps.apple.com/us/app/id0000000000",
      "categoryId": "6013",
      "categoryName": "Health & Fitness",
      "releaseDate": "2024-05-01",
      "price": 0,
      "currency": "USD",
      "rating": 4.7,
      "ratingCount": 12450,
      "metadataUpdatedAt": "2026-09-06T00:15:00.000Z",
      "history": [
        {
          "day": "2026-09-06",
          "rank": 1
        }
      ],
      "rank": 1,
      "previousRank": 2,
      "movement": 1,
      "status": "ranked",
      "inCatalogue": true,
      "estimates": {
        "observedAt": "2026-09-06T00:00:00.000Z",
        "previousObservedAt": "2026-09-05T00:00:00.000Z",
        "monthlyInstalls": 84000,
        "previousMonthlyInstalls": 72000,
        "monthlyRevenue": null,
        "previousMonthlyRevenue": null,
        "currency": "USD"
      }
    }
  ],
  "updatedAt": "2026-09-06T00:15:00.000Z"
}

```

## Source

Apple ranking RSS / modern RSS. Coverage storefronts are us, gb, fr, de, br, be. Grossing is a position, not dollars. Chart GET never calls Apple.

## Refresh

Trigger task peekpanda-apple-charts-refresh cron */15 * * * * POSTs Nest /internal/app-charts/refresh-scheduled. Nest fetches at most 10 unfinished country×category×type lists for the UTC day (APPLE_CHART_REFRESH_POLICY.batchSize=10). Completed lists are skipped. The 15-min clock is a drip/retry, not 96 copies of the same chart. Chart pacer 1s, separate from ASO 4s.
