Traffic & installs
Four BigQuery/GA4-backed endpoints sit alongside the partner Metrics: classified traffic sources as a period series, traffic by keyword, keyword performance (scraped rank plus a BigQuery install overlay), and attributed installs grouped by source, keyword, or category. Every endpoint returns the shared object: "list" envelope — paginated, with coverage and metadata — and a default trailing 90-day window when you pass no dates.
These endpoints read the app's BigQuery / GA4 export, not the Shopify Partner integration. Until that export is connected they return a 409 whose error.code is bigquery_integration_not_connected (never connected), bigquery_integration_unauthorized (access revoked or rejected — reconnect in Ranksy), or bigquery_integration_pending (first export still loading — retry shortly). This is distinct from the partner_integration_* 409 on the metric endpoints. The one exception is GET /keywords, which is backed by scraped ranking data and never returns a 409 — see Errors and the Errors reference.
Common parameters
Every endpoint takes the app ULID in the path and the same date / pagination query parameters. The per-endpoint sections only call out the extra parameters specific to that endpoint (granularity, group_by, filter[keyword], sort).
Path parameter
- Name
app- Type
- string
- Description
The team app's ULID, e.g.
01JN4HKQX0000000000000000.
Query parameters
- Name
start_date- Type
- string
- Description
YYYY-MM-DD. Start of the window. Defaults to 90 days ago when omitted.
- Name
end_date- Type
- string
- Description
YYYY-MM-DD. Must be greater than or equal tostart_date. Defaults to today when omitted.
- Name
page- Type
- integer
- Description
The page to return. Default
1.
- Name
per_page- Type
- integer
- Description
Rows per page. Default
50, maximum200.
Request
curl https://ranksyapp.com/api/v1/apps/01JN4HKQX0000000000000000/traffic/sources \
-H "Authorization: Bearer rk_live_..."
Traffic
Traffic sources
Classified traffic to the app's listing, broken into fixed-length periods. Each point carries the period's start_date/end_date, the period totals (sessions, page_views, installs), and a sources array — the per-source breakdown with each source's sessions, installs, and percentage (its share of the period's installs, 57.9 = 57.9%).
Additional parameters
- Name
granularity- Type
- string
- Description
Period length:
week(7-day, default) orbiweekly(14-day). BigQuery traffic is aggregated into fixed-length periods, so unlike the metric series there is nodayormonth.
Request
curl -G https://ranksyapp.com/api/v1/apps/01JN4HKQX0000000000000000/traffic/sources \
-H "Authorization: Bearer rk_live_..." \
-d start_date=2026-05-01 \
-d end_date=2026-05-31 \
-d granularity=week
Response
{
"object": "list",
"url": "/api/v1/apps/01JN4HKQX0000000000000000/traffic/sources",
"page": 1,
"per_page": 50,
"total_count": 5,
"has_more": false,
"coverage": {
"requested": { "start": "2026-05-01", "end": "2026-05-31" },
"available": { "start": "2026-05-01", "end": "2026-05-28" }
},
"metadata": { "bigquery_sync_status": "complete", "granularity": "week" },
"data": [
{
"start_date": "2026-05-01",
"end_date": "2026-05-07",
"sessions": 1240,
"page_views": 1985,
"installs": 38,
"sources": [
{
"source": "Shopify App Store search",
"sessions": 720,
"installs": 22,
"percentage": 57.9
}
]
}
]
}
Traffic by keyword
Per-keyword traffic from the BigQuery search-ranking data: each row splits installs and pageviews into organic vs ad and reports the average organic / ad rank, the total_installs, and the organic / ad share of those installs (79.1 = 79.1%). avg_organic_rank / avg_ad_rank are null when the keyword never ranked that way.
Additional parameters
- Name
filter[keyword]- Type
- string
- Description
Case-insensitive substring match on the keyword (max 100 chars).
- Name
sort- Type
- string
- Description
Column to sort by, always descending:
total_installs(default),organic_installs, orad_installs.
Request
curl -G https://ranksyapp.com/api/v1/apps/01JN4HKQX0000000000000000/traffic/keywords \
-H "Authorization: Bearer rk_live_..." \
-d "filter[keyword]=email" \
-d sort=organic_installs
Response
{
"object": "list",
"url": "/api/v1/apps/01JN4HKQX0000000000000000/traffic/keywords",
"page": 1,
"per_page": 50,
"total_count": 18,
"has_more": false,
"coverage": {
"requested": { "start": "2026-03-01", "end": "2026-05-31" },
"available": { "start": "2026-03-01", "end": "2026-05-31" }
},
"metadata": { "bigquery_sync_status": "complete" },
"data": [
{
"keyword": "email marketing",
"organic_installs": 34,
"organic_pageviews": 512,
"avg_organic_rank": 4.2,
"ad_installs": 9,
"ad_pageviews": 140,
"avg_ad_rank": 2.0,
"total_installs": 43,
"organic_share": 79.1,
"ad_share": 20.9
}
]
}
Keywords
Keyword performance
The app's organic + sponsored App Store ranking for every keyword it ranks for — scraped daily and always available — with a BigQuery install/view overlay layered on top. The scraped half (organic_rank/organic_page/organic_change, sponsored_rank/sponsored_page/sponsored_change) is null where the app does not rank that way; positive *_change means the rank improved. The overlay (views, installs, organic_installs, ad_installs) is scoped to the date window and zero-filled when BigQuery is not connected.
Because the scraped surface stands on its own, this endpoint never returns 409 — it degrades gracefully to a zero overlay. The date window only scopes the BigQuery overlay.
Additional parameters
- Name
sort- Type
- string
- Description
Sort column:
rank(best organic rank ascending, default),installs(BigQuery installs descending), orkeyword(alphabetical).
Request
curl -G https://ranksyapp.com/api/v1/apps/01JN4HKQX0000000000000000/keywords \
-H "Authorization: Bearer rk_live_..." \
-d sort=installs
Response
{
"object": "list",
"url": "/api/v1/apps/01JN4HKQX0000000000000000/keywords",
"page": 1,
"per_page": 50,
"total_count": 64,
"has_more": true,
"coverage": {
"requested": { "start": "2026-03-01", "end": "2026-05-31" },
"available": { "start": "2026-03-01", "end": "2026-05-31" }
},
"metadata": { "bigquery_sync_status": "complete", "installs_available": true },
"data": [
{
"keyword": "inventory sync",
"organic_rank": 4,
"organic_page": 1,
"organic_change": 2,
"sponsored_rank": 1,
"sponsored_page": 1,
"sponsored_change": -1,
"views": 320,
"installs": 18,
"organic_installs": 12,
"ad_installs": 6
}
]
}
Installs
Attributed installs
BigQuery-attributed installs over the window — distinct from /metrics/installs, which is the Partner-reported count. The group_by dimension selects the bucket shape, and each row carries only the keys for its dimension:
source(default) —source,installs,share(percentage of total attributed installs).keyword—keyword,views,started_installs("Add app" clicks),completed_installs,conversion_rate(completed ÷ views, %), plus organic/ad install + pageview splits (nullwhen no ranking row exists for the keyword).category—category,views,started_installs,completed_installs,completion_rate(completed ÷ started, %;nullwhen no started installs).
The dimension is echoed in metadata.group_by.
Additional parameters
- Name
group_by- Type
- string
- Description
Attribution dimension:
source(default),keyword, orcategory.
Request
curl https://ranksyapp.com/api/v1/apps/01JN4HKQX0000000000000000/installs \
-H "Authorization: Bearer rk_live_..."
group_by=source
{
"object": "list",
"url": "/api/v1/apps/01JN4HKQX0000000000000000/installs",
"page": 1,
"per_page": 50,
"total_count": 2,
"has_more": false,
"coverage": {
"requested": { "start": "2026-03-01", "end": "2026-05-31" },
"available": { "start": "2026-03-01", "end": "2026-05-31" }
},
"metadata": { "bigquery_sync_status": "complete", "group_by": "source" },
"data": [
{ "source": "organic", "installs": 142, "share": 68.5 },
{ "source": "ad", "installs": 65, "share": 31.5 }
]
}
group_by=keyword
{
"object": "list",
"url": "/api/v1/apps/01JN4HKQX0000000000000000/installs",
"page": 1,
"per_page": 50,
"total_count": 12,
"has_more": false,
"coverage": {
"requested": { "start": "2026-05-01", "end": "2026-05-31" },
"available": { "start": "2026-05-01", "end": "2026-05-31" }
},
"metadata": { "bigquery_sync_status": "complete", "group_by": "keyword" },
"data": [
{
"keyword": "inventory sync",
"views": 320,
"started_installs": 40,
"completed_installs": 18,
"conversion_rate": 5.63,
"organic_installs": 12,
"ad_installs": 6,
"organic_pageviews": 280,
"ad_pageviews": 40
}
]
}
Response shapes
All four endpoints share the object: "list" envelope; only the data row shape differs.
List envelope (object: "list")
- Name
object- Type
- string
- Description
Always
"list".
- Name
url- Type
- string
- Description
The endpoint path for this request.
- Name
page- Type
- integer
- Description
The current page number (1-based).
- Name
per_page- Type
- integer
- Description
Rows per page.
- Name
total_count- Type
- integer
- Description
Total rows across every page.
- Name
has_more- Type
- boolean
- Description
Whether further pages exist.
- Name
coverage- Type
- object
- Description
What was
requestedvs what wasavailable.availableisnullwhen no data fell in range.
- Name
metadata- Type
- object
- Description
bigquery_sync_statusplus per-endpoint keys — e.g.granularity(sources),group_by(installs),installs_available(keywords).
- Name
data- Type
- array
- Description
The rows; shape depends on the endpoint (see below).
Traffic-source point
- Name
start_date- Type
- string
- Description
Inclusive period start (
YYYY-MM-DD), ornullwhen there are no periods.
- Name
end_date- Type
- string
- Description
Inclusive period end (
YYYY-MM-DD), ornullwhen there are no periods.
- Name
sessions- Type
- integer
- Description
Total listing sessions in the period, across all sources.
- Name
page_views- Type
- integer
- Description
Total listing page views in the period, across all sources.
- Name
installs- Type
- integer
- Description
Total installs attributed in the period, across all sources.
- Name
sources- Type
- array
- Description
Per-source breakdown — each entry has
source,sessions,installs, andpercentage(share of the period's installs).
Traffic-keyword row
- Name
keyword- Type
- string
- Description
The search keyword.
- Name
organic_installs- Type
- integer
- Description
Installs from organic (non-sponsored) appearances.
- Name
organic_pageviews- Type
- integer
- Description
Page views from organic appearances.
- Name
avg_organic_rank- Type
- number
- Description
Average organic rank (1 = top), or
nullwhen never ranked organically.
- Name
ad_installs- Type
- integer
- Description
Installs from sponsored (ad) appearances.
- Name
ad_pageviews- Type
- integer
- Description
Page views from sponsored appearances.
- Name
avg_ad_rank- Type
- number
- Description
Average ad rank (1 = top), or
nullwhen never shown as an ad.
- Name
total_installs- Type
- integer
- Description
Total installs (organic + ad).
- Name
organic_share- Type
- number
- Description
Organic share of installs, as a percentage.
- Name
ad_share- Type
- number
- Description
Ad share of installs, as a percentage.
Keyword-performance row
- Name
keyword- Type
- string
- Description
The search keyword, or
nullwhen the scraped row had no associated keyword.
- Name
organic_rank- Type
- integer
- Description
Latest scraped organic rank, or
nullwhen the app does not rank organically.
- Name
organic_page- Type
- integer
- Description
Results page the organic rank falls on, or
null.
- Name
organic_change- Type
- integer
- Description
Day-over-day organic rank change (positive = improved), or
null.
- Name
sponsored_rank- Type
- integer
- Description
Latest scraped sponsored (ad) rank, or
nullwhen the app has no ad placement.
- Name
sponsored_page- Type
- integer
- Description
Results page the sponsored rank falls on, or
null.
- Name
sponsored_change- Type
- integer
- Description
Day-over-day sponsored rank change (positive = improved), or
null.
- Name
views- Type
- integer
- Description
Page-view sessions attributed to the keyword in the window (BigQuery overlay);
0when BigQuery is not connected.
- Name
installs- Type
- integer
- Description
Completed installs attributed to the keyword in the window (BigQuery overlay);
0when not connected.
- Name
organic_installs- Type
- integer
- Description
Organic-sourced installs (BigQuery overlay);
0when not connected.
- Name
ad_installs- Type
- integer
- Description
Ad-sourced installs (BigQuery overlay);
0when not connected.
Install bucket
The bucket carries only the keys for its group_by dimension.
- Name
source- Type
- string
- Description
group_by=source— attribution source for this bucket. Paired withinstallsandshare(percentage of total attributed installs).
- Name
keyword- Type
- string
- Description
group_by=keyword— the keyword. Paired withviews,started_installs,completed_installs,conversion_rate(completed ÷ views, %), and organic/ad install + pageview splits (nullwhen no ranking row exists).
- Name
category- Type
- string
- Description
group_by=category— App Store category. Paired withviews,started_installs,completed_installs, andcompletion_rate(completed ÷ started, %;nullwhen no started installs).
Coverage
coverage tells the caller how much data backed the response, so a short or empty result is never ambiguous.
coverage
"coverage": {
"requested": { "start": "2026-03-01", "end": "2026-05-31" },
"available": { "start": "2026-03-01", "end": "2026-05-28" }
}
Pagination
Responses are paginated. Use page and per_page (default 50, max 200) to walk the result set, and check has_more / total_count to know when to stop. The metadata on the envelope applies to every page.
Errors
Beyond the universal 401 / 403 / 422 / 429 (see Errors), these endpoints return:
409— the app's BigQuery / GA4 export isn't usable.error.codeisbigquery_integration_not_connected(never connected),bigquery_integration_unauthorized(access revoked or rejected — reconnect in Ranksy), orbigquery_integration_pending(first export still loading — retry shortly). Returned bytraffic/sources,traffic/keywords, andinstalls. This is the BigQuery counterpart to thepartner_integration_*409 on the metric endpoints.- No
409onGET /keywords. It is backed by scraped ranking data, so it always returns rows; the BigQuery install overlay is simply zero-filled when the export is absent.
A bad query parameter (e.g. an unknown group_by, granularity, or sort value, or end_date before start_date) surfaces as 422 validation_failed with error.param naming the offending parameter.
Things to keep in mind
- BigQuery, not Partner. These four read the GA4/BigQuery export; the metric endpoints read the Shopify Partner integration. The 409 codes are correspondingly different (
bigquery_integration_*vspartner_integration_*). - Percentages are percentages here.
share,percentage,organic_share,ad_share, and the*_ratefields are already in percent (57.9= 57.9%) — unlike the metric ratios, which are fractions. traffic/sourcesis a period series. It buckets byweekorbiweeklyonly; each point spansstart_date..end_daterather than carrying a singledate.- Attributed ≠ Partner installs.
/installscounts BigQuery-attributed installs;/metrics/installsis the Partner-reported count. They will not match. /keywordsalways answers. Scraped rank/page/change are always present where the app ranks; the install overlay is zero-filled (not 409) when BigQuery is missing — checkmetadata.installs_available.- Default window is 90 days. Omit
start_date/end_datefor the trailing 90 days;coverageis your source of truth for what actually backed the response.