---
title: "Get an Apple App Store chart"
method: GET
path: "/app_store/app-list"
tags: ["app_store"]
---

# Get an Apple App Store chart

`GET /app_store/app-list`

## Request

- Base URL: `https://www.socialcrawl.dev/v1`
- URL: `https://www.socialcrawl.dev/v1/app_store/app-list`
- Auth: API key in header `x-api-key`

Returns a unified AppList for an Apple App Store chart (top free, top paid, top grossing, and the three new-apps charts), optionally scoped to a category. Each item is the same canonical `App` shape as app-search. Use it to track ranking movements or build a category leaderboard. iPad-specific charts are not available from this source. Apple publishes a chart for most but not all categories: the Newsstand-era `magazines_*` values plus `catalogs`, `games_dice` and `games_educational` return an empty list on `top_free_ios` at 0 credits (measured 06/09/2026), and several of them do populate on the `new_ios` charts.

## Query parameters

- `app_collection` string, required
- `app_category` 'books' | 'business' | 'catalogs' | 'education' | 'entertainment' | 'finance' | 'food_and_drink' | 'games' | 'games_action' | 'games_adventure' | 'games_arcade' | 'games_board' | 'games_card' | 'games_casino' | 'games_dice' | 'games_educational' | 'games_family' | 'games_music' | 'games_puzzle' | 'games_racing' | 'games_role_playing' | 'games_simulation' | 'games_sports' | 'games_strategy' | 'games_trivia' | 'games_word' | 'health_and_fitness' | 'lifestyle' | 'magazines_and_newspapers' | 'magazines_arts' | 'magazines_automotive' | 'magazines_weddings' | 'magazines_business' | 'magazines_children' | 'magazines_computer' | 'magazines_food' | 'magazines_crafts' | 'magazines_electronics' | 'magazines_entertainment' | 'magazines_fashion' | 'magazines_health' | 'magazines_history' | 'magazines_home' | 'magazines_literary' | 'magazines_men' | 'magazines_movies_and_music' | 'magazines_politics' | 'magazines_outdoors' | 'magazines_family' | 'magazines_pets' | 'magazines_professional' | 'magazines_regional' | 'magazines_science' | 'magazines_sports' | 'magazines_teens' | 'magazines_travel' | 'magazines_women' | 'medical' | 'music' | 'navigation' | 'news' | 'photo_and_video' | 'productivity' | 'reference' | 'shopping' | 'social_networking' | 'sports' | 'travel' | 'utilities' | 'weather'
- `country` string
- `language` string
- `depth` integer

## Headers

- `Cache-Control` string
- `Idempotency-Key` string

## Response `200`

Successful response

- object
  - `success` boolean, required: Whether the request succeeded
  - `platform` string, required: Platform name
  - `endpoint` string, required: API endpoint path
  - `data` object, required: Platform-specific response data
    - `items` object[]: Array of canonical app wrappers ({ app })
      - `app` object: Canonical App object (unified across app marketplaces)
        - `id` string: Store app ID (Google Play package name / App Store numeric id)
        - `store` string: App marketplace ("google_play" or "app_store")
        - `url` string, nullable: String at app.url
        - `title` string, nullable: App title
        - `subtitle` string, nullable: String at app.subtitle
        - `icon` string, nullable: String at app.icon
        - `description` string, nullable: String at app.description
        - `developer` object: Developer block (name, id, url; email/address for Google)
          - `id` string, nullable: String at app.developer.id
          - `name` string, nullable: String at app.developer.name
          - `url` string, nullable: String at app.developer.url
          - `email` string, nullable: String at app.developer.email
          - `address` string, nullable: String at app.developer.address
          - `website` string, nullable: String at app.developer.website
        - `rating` object: Aggregate rating (value, max, number of ratings)
          - `value` integer, nullable: Numeric at app.rating.value
          - `max` integer, nullable: Numeric at app.rating.max
          - `count` integer, nullable: Numeric at app.rating.count
        - `price` object: Price block (current, original, currency, is_free, displayed)
          - `current` integer, nullable: Numeric at app.price.current
          - `original` integer, nullable: Numeric at app.price.original
          - `currency` string, nullable: String at app.price.currency
          - `is_free` boolean, nullable: Boolean at app.price.is_free
          - `displayed` string, nullable: String at app.price.displayed
        - `reviews_count` integer, nullable: Numeric at app.reviews_count
        - `category` string, nullable: String at app.category
        - `categories` string[], nullable: Array at app.categories
        - `installs` object, nullable: Install signal (Google only): { display "1,000,000,000+", count }; null on Apple
          - `display` string, nullable: String at app.installs.display
          - `count` integer, nullable: Numeric at app.installs.count
        - `version` string, nullable: String at app.version
        - `minimum_os_version` string, nullable: String at app.minimum_os_version
        - `size` string, nullable: String at app.size
        - `released_at` union: Union: string | integer | null
          - string
          - integer
        - `updated_at` union: Union: string | integer | null
          - string
          - integer
        - `update_notes` string, nullable: String at app.update_notes
        - `image_urls` string[], nullable: Screenshot URLs
        - `video_urls` string[], nullable: Array at app.video_urls
        - `languages` string[], nullable: Array at app.languages
        - `advisories` string[], nullable: Apple age/content advisories (null on Google)
        - `genres` string[], nullable: Google Play genres (null on Apple)
        - `tags` string[], nullable: Array at app.tags
        - `similar_apps` object[], nullable: Array at app.similar_apps
          - `id` string, nullable: String at app.similar_apps.id
          - `title` string, nullable: String at app.similar_apps.title
          - `url` string, nullable: String at app.similar_apps.url
        - `more_by_developer` object[], nullable: Array at app.more_by_developer
          - `id` string, nullable: String at app.more_by_developer.id
          - `title` string, nullable: String at app.more_by_developer.title
          - `url` string, nullable: String at app.more_by_developer.url
        - `ext` object, nullable: Nested object: app.ext
          - `appdata` string, nullable: Leaf at app.ext.appdata
    - `next_cursor` string, nullable: Opaque cursor for the next page. Pass it back as a query parameter on endpoints that support pagination. Present only when the upstream reports more results.
    - `total` integer, nullable: Total number of matching results, when the upstream provides a count. Omitted otherwise.
    - `dropped` integer, required: Number of upstream list items dropped because they could not be repaired to the endpoint schema. Valid list responses include 0.
    - `_warnings` string[]: Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.
  - `credits_used` integer, required: Number of credits consumed
  - `credits_remaining` integer, nullable, required: Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.
  - `request_id` string, required: Unique request identifier for support
  - `cached` boolean, required: Whether the response was served from cache
  - `idempotent_replay` boolean: True only when this response is an idempotency replay
  - `meta` object: Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.
    - `hint` object: One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.
      - `code` string: creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat
      - `message` string: One sentence for a person or an agent to read.
      - `method` string: GET or POST.
      - `path` string: The call the hint points to.
  - `pagination` object, required: Cursor state for this page. Present on every list response.
    - `next_cursor` string, nullable, required: Opaque token to send back as `cursor` for the next page, or null at end-of-list. Pass it back verbatim; never decode or trim it.
    - `has_more` boolean, required: Explicit stop signal. Prefer this over inspecting next_cursor or comparing against total.
    - `page_size` integer, required: Number of items in THIS page.
    - `stopped_at` 'since' | 'known_id' | 'end' | 'null', nullable: Present only when the request sent `since` or `stop_at_id`. Why the walk stopped: `since` (this page reached a post older than the date), `known_id` (this page reached the post you already hold), `end` (the list ran out first), or null (not yet, keep walking with `next_cursor`).

## Other responses

- `400`: Invalid request - missing or malformed parameters
- `401`: Unauthorized - missing or invalid API key
- `402`: Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit
- `404`: Not found - the endpoint does not exist, or the requested resource was not found upstream
- `405`: Method not allowed - wrong HTTP verb for this endpoint
- `409`: Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation
- `410`: Gone - the discover run's retention passed and its rows were deleted; start a new run
- `413`: Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling
- `422`: Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)
- `429`: Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.
- `500`: Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit
- `502`: Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge
- `503`: Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge
- `504`: Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After

---

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