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_here

API 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

StatusCodeMeaning
401unauthorizedMissing, invalid, or revoked API key
403forbiddenValid key but subscription inactive or trial expired
400bad_requestMissing or invalid query parameters
404not_foundResource not found or not tracked by your org
429rate_limitedDaily rate limit exceeded
500internal_errorServer 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:

HeaderDescription
X-RateLimit-LimitTotal daily limit (1,000)
X-RateLimit-RemainingRequests remaining today
X-RateLimit-ResetUnix 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

GET/api/v1/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

GET/api/v1/apps/:id

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

GET/api/v1/apps/:id/keywords

Tracked keywords for an app with latest metrics. Cursor paginated.

ParamTypeDefaultDescription
cursorstringPagination cursor from previous response
limitinteger50Results 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

GET/api/v1/apps/:id/rankings

Rank history for an app's tracked keywords.

ParamTypeDefaultDescription
daysinteger30History window (1–365)
keyword_idstringFilter 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" }
      ]
    }
  ]
}

GET/api/v1/keywords/search

Keyword research. Returns autocomplete suggestions with difficulty scores and popularity estimates.

ParamTypeDefaultDescription
qstringrequiredSearch query
storestringrequiredios or android
countrystringusCountry 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

GET/api/v1/keywords/:id/rankings

SERP history for a keyword \u2014 which apps rank for it and how positions change.

ParamTypeDefaultDescription
daysinteger30History 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

GET/api/v1/keywords/suggestions

Raw autocomplete suggestions from the store. Lighter than /keywords/search \u2014 no difficulty calculation.

ParamTypeDefaultDescription
qstringrequiredSeed term
storestringrequiredios or android
countrystringusCountry 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

GET/api/v1/competitors/:id/keywords

Keywords a competitor ranks for, with optional gap analysis against your app. Cursor paginated.

ParamTypeDefaultDescription
app_idstringYour app ID for gap analysis
cursorstringPagination cursor
limitinteger50Results 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.