---
title: "Find apps in a store by keyword"
method: GET
path: "/api/v1/apps/search"
tags: ["Apps"]
---

# Find apps in a store by keyword

`GET /api/v1/apps/search`

## Request

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

For finding apps and their store IDs, not for checking ranks. On iOS the list follows the App Store's own search order when Sonar can read it, with the iTunes Search API's relevance order as the fallback (which can sit dozens of places away from the App Store's real ranking below the top 10), and the response doesn't say which one served it, so the list order is not a rank. For an app's position on a keyword, track the keyword and read `/api/v1/apps/{id}/rankings` or `/api/v1/keywords/{id}/rankings`.

## Query parameters

- `q` string, required
- `store` 'ios' | 'android', required
- `country` string
- `num` integer

## Response `200`

Matching apps (list order is not a rank — see the operation description). When the store is throttling, the newest stored top 10 for this exact term and storefront with `meta.stale` + `meta.partial` (entries carry rank, store, storeId, name, developer, iconUrl only).

- object
  - `data` App[]
    - `storeId` string
    - `name` string
    - `developer` string
    - `rating` number, nullable: Official storefront rating; null when unavailable.
    - `reviews` integer, nullable: Official rating count, not written review count; null when unavailable.
    - `category` string
    - `iconUrl` string, uri
  - `meta` StaleMeta: Envelope `meta`, present only when the store was throttling and the endpoint answered from Sonar's stored copy instead of failing (apps/lookup, apps/search, apps/reviews, keywords/suggestions). Live answers carry no `meta`. A stored copy is billed like any 200.
    - `stale` true, required
    - `stale_as_of` string, nullable, required: When the stored copy was captured: ISO 8601 timestamp or YYYY-MM-DD date; null when unknown.
    - `partial` boolean, required: The copy holds less than a live call would: fewer results or fields (stored search results stop at the top 10, name/developer/icon only), or a different order (stored suggestions are ranked by popularity).
    - `stale_country` string, nullable: App lookup only: the storefront the stored copy came from, when it differs from the request.
    - `retry_after_seconds` integer, required: When live data is expected again.
    - `note` string, required: One sentence explaining what was served — safe to show to a user.

## 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

---

[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)
