API Reference
The Sonar REST API provides programmatic access to keyword research, rank tracking, and competitor analysis. All endpoints require a paid subscription API key.
Authentication
Include your API key in the Authorization header:
Authorization: Bearer aso_your_api_key_hereAPI keys are managed at /developers in the dashboard. Keys use the prefix aso_ followed by 64 hex characters. The full key is shown once at creation time — we store only the SHA-256 hash.
Error responses
| Status | Code | Meaning |
|---|---|---|
| 401 | unauthorized | Missing, invalid, or revoked API key |
| 403 | forbidden | Valid key but subscription inactive or trial expired |
| 400 | bad_request | Missing or invalid query parameters |
| 404 | not_found | Resource not found or not tracked by your org |
| 429 | rate_limited | Daily rate limit exceeded |
| 500 | internal_error | Server error |
{
"error": {
"code": "unauthorized",
"message": "Missing or invalid Authorization header. Use: Bearer aso_xxx"
}
}Rate Limits
All subscribers get 1,000 requests per day. Resets at midnight UTC. Rate limit headers are included in every response:
| Header | Description |
|---|---|
| X-RateLimit-Limit | Total daily limit (1,000) |
| X-RateLimit-Remaining | Requests remaining today |
| X-RateLimit-Reset | Unix timestamp when limit resets |
Response Format
Single resource
{ "data": { ... } }List
{ "data": [ ... ] }Paginated list
{
"data": [ ... ],
"pagination": {
"next_cursor": "uuid-of-last-item",
"has_more": true
}
}Pass ?cursor=<next_cursor> to fetch the next page. When has_more is false, you've reached the end.
Endpoints
List Apps
List all tracked apps with the latest snapshot.
curl -H "Authorization: Bearer aso_xxx" \
https://your-domain.com/api/v1/apps{
"data": [
{
"id": "6a2b1e60-fa88-4816-a0e3-c0d3c042f478",
"store": "ios",
"store_id": "com.buzzfeed.tasty",
"name": "Tasty: Recipes, Cooking Videos",
"developer": "BuzzFeed",
"category": "Food & Drink",
"icon_url": "https://...",
"is_own": false,
"added_at": "2026-02-12T12:56:14.318Z",
"latest_snapshot": {
"rating": 4.90836,
"review_count": 431859,
"version": "3.39.1",
"installs": null,
"measured_at": "2026-02-15"
}
}
]
}installs is only populated for Android apps. latest_snapshot is null if no snapshots exist yet.
Get App
App metadata with snapshot history (last 90 days).
curl -H "Authorization: Bearer aso_xxx" \
https://your-domain.com/api/v1/apps/6a2b1e60-...{
"data": {
"id": "6a2b1e60-...",
"store": "ios",
"store_id": "com.buzzfeed.tasty",
"name": "Tasty: Recipes, Cooking Videos",
"developer": "BuzzFeed",
"category": "Food & Drink",
"icon_url": "https://...",
"metadata": { "url": "...", "free": true, "price": 0, "rating": 4.9, "reviews": 431859 },
"is_own": false,
"added_at": "2026-02-12T12:56:14.318Z",
"last_scraped_at": "2026-02-15T04:40:59.481Z",
"snapshots": [
{ "rating": 4.90836, "review_count": 431859, "version": "3.39.1", "installs": null, "measured_at": "2026-02-15" }
]
}
}Returns 404 if the app is not tracked by your organization.
App Keywords
Tracked keywords for an app with latest metrics. Cursor paginated.
| Param | Type | Default | Description |
|---|---|---|---|
| cursor | string | — | Pagination cursor from previous response |
| limit | integer | 50 | Results per page (1–200) |
{
"data": [
{
"id": "tracked-kw-uuid",
"keyword_id": "kw-uuid",
"keyword": "recipe app",
"store": "ios",
"country": "us",
"added_at": "2026-01-20T00:00:00.000Z",
"difficulty": 77,
"popularity": null,
"results_count": 10
}
],
"pagination": { "next_cursor": "next-uuid", "has_more": true }
}popularity is null unless an Apple Search Ads account is connected (iOS) or Google Ads data is available (Android).
App Rankings
Rank history for an app's tracked keywords.
| Param | Type | Default | Description |
|---|---|---|---|
| days | integer | 30 | History window (1–365) |
| keyword_id | string | — | Filter to a specific keyword |
{
"data": [
{
"keyword_id": "kw-uuid",
"keyword": "recipe app",
"history": [
{ "rank": 12, "measured_at": "2026-02-14" },
{ "rank": 14, "measured_at": "2026-02-13" }
]
}
]
}Keyword Search
Keyword research. Returns autocomplete suggestions with difficulty scores and popularity estimates.
| Param | Type | Default | Description |
|---|---|---|---|
| q | string | required | Search query |
| store | string | required | ios or android |
| country | string | us | Country code |
curl -H "Authorization: Bearer aso_xxx" \
"https://your-domain.com/api/v1/keywords/search?q=recipe+app&store=ios"{
"data": [
{ "keyword": "recipe app", "store": "ios", "country": "us", "difficulty": 77, "popularity": null, "results_count": 10 },
{ "keyword": "free recipe app", "store": "ios", "country": "us", "difficulty": 70, "popularity": null, "results_count": 10 }
]
}Returns up to 10 suggestions. difficulty is 0\u2013100 based on top-10 competitor strength. popularity requires an Apple Search Ads account (iOS) or is estimated from install data (Android). Results are cached for 6 hours.
Keyword Rankings
SERP history for a keyword \u2014 which apps rank for it and how positions change.
| Param | Type | Default | Description |
|---|---|---|---|
| days | integer | 30 | History window (1–365) |
{
"data": {
"keyword_id": "kw-uuid",
"keyword": "recipe app",
"store": "ios",
"country": "us",
"entries": [
{
"rank": 1,
"store_id": "com.buzzfeed.tasty",
"app_name": "Tasty: Recipes, Cooking Videos",
"app_icon_url": "https://...",
"measured_at": "2026-02-14"
}
]
}
}Entries are sorted by date (newest first), then by rank. Returns 404 if the keyword ID doesn't exist.
Suggestions
Raw autocomplete suggestions from the store. Lighter than /keywords/search \u2014 no difficulty calculation.
| Param | Type | Default | Description |
|---|---|---|---|
| q | string | required | Seed term |
| store | string | required | ios or android |
| country | string | us | Country code |
{
"data": [
{ "term": "recipe keeper", "priority": 0 },
{ "term": "recipes app free", "priority": 0 },
{ "term": "recipe book", "priority": 0 }
]
}priority is the autocomplete priority score (0\u201310000). Higher values indicate more popular suggestions.
Competitor Keywords
Keywords a competitor ranks for, with optional gap analysis against your app. Cursor paginated.
| Param | Type | Default | Description |
|---|---|---|---|
| app_id | string | — | Your app ID for gap analysis |
| cursor | string | — | Pagination cursor |
| limit | integer | 50 | Results per page (1–200) |
{
"data": [
{
"keyword_id": "kw-uuid",
"keyword": "recipe app",
"store": "ios",
"country": "us",
"competitor_rank": 3,
"own_rank": 15,
"gap": null,
"difficulty": 72,
"popularity": null
},
{
"keyword_id": "kw-uuid-2",
"keyword": "cooking videos",
"store": "ios",
"country": "us",
"competitor_rank": 5,
"own_rank": null,
"gap": "missing",
"difficulty": 45,
"popularity": null
}
],
"pagination": { "next_cursor": null, "has_more": false }
}gap is "missing" when the competitor ranks but your app doesn't. null when both rank. own_rank is only populated when app_id is provided.