PeekPanda docs
Get market signals
Read chart, paid-ad, organic and keyword signals for up to 600 iOS apps in one batch.
Download Markdown ↓GET/v1/market/signalsBearer API credential or OAuth access tokenScope context:readMCP get_market_signalsRequest
Send country and storeIds as query parameters. storeIds is one comma-separated value of numeric Apple store IDs; MCP clients may send an array instead. This GET never starts collection.
curl --get 'https://api.peekpanda.com/v1/market/signals' \
--header "Authorization: Bearer $PEEKPANDA_TOKEN" \
--header 'x-genviral-tenancy: personal' \
--data-urlencode 'country=us' \
--data-urlencode 'storeIds=1018368216,555376968'Response
The response echoes the country and the eight weekly dates, says when keyword positions were observed and when organic views were counted, and lists apps keyed by storeId. Look entries up by storeId, not by position. placement, paid, organic and keywords are each null when that stream was never collected for the app, never zero. The second app in the example has no collected ads or organic posts.
{
"country": "us",
"weeks": [
"2026-07-20",
"2026-07-27",
"2026-08-03",
"2026-08-10",
"2026-08-17",
"2026-08-24",
"2026-08-31",
"2026-09-07"
],
"keywordsObservedAt": "2026-09-10T08:40:00.000Z",
"organicViewsCountedAt": "2026-05-29T00:00:00.000Z",
"apps": [
{
"storeId": "1018368216",
"placement": {
"rank": 13,
"previousRank": 10,
"chartType": "free",
"categoryId": "6008",
"categoryName": "Photo & Video",
"day": "2026-09-10"
},
"paid": {
"totalAds": 30,
"liveAds": 30,
"startedLast7Days": 5,
"medianDaysLive": 29,
"longestDaysLive": 38,
"formats": {
"video": 29,
"dynamic": 0,
"image": 1
},
"topCallToAction": "Install now",
"destinations": {
"appStore": 29,
"web": 1
},
"weeklyStarts": [
0,
0,
11,
13,
0,
0,
3,
3
]
},
"organic": {
"matchedPosts": 10,
"views": 29765621,
"medianViews": 1991019,
"bestPostViews": 7700000,
"postsOverMillion": 7,
"newestPublishedAt": "2026-07-29T00:00:00.000Z",
"platforms": {
"TikTok": 8,
"Threads": 2
},
"matchedBy": "product_name"
},
"keywords": {
"ranked": 20,
"top1": 1,
"top3": 1,
"top10": 4,
"top50": 9,
"best": {
"query": "chai",
"position": 63,
"popularity": 78
},
"top": [
{
"query": "chai",
"position": 63,
"popularity": 78
},
{
"query": "character ai",
"position": 47,
"popularity": 75
},
{
"query": "talkie",
"position": 68,
"popularity": 74
}
]
}
},
{
"storeId": "555376968",
"placement": {
"rank": 1,
"previousRank": 1,
"chartType": "free",
"categoryId": "6004",
"categoryName": "Sports",
"day": "2026-09-10"
},
"paid": null,
"organic": null,
"keywords": {
"ranked": 29,
"top1": 8,
"top3": 15,
"top10": 17,
"top50": 20,
"best": {
"query": "espn fantasy",
"position": 1,
"popularity": 83
},
"top": [
{
"query": "espn fantasy",
"position": 1,
"popularity": 83
},
{
"query": "espn",
"position": 2,
"popularity": 78
},
{
"query": "yahoo fantasy",
"position": 2,
"popularity": 78
}
]
}
}
]
}
Parameters
countryquery^[a-z]{2}$NoApple storefront as two lowercase letters. Defaults to us.storeIdsquerycomma-separated numeric IDs, 1–600 uniqueYesApple store IDs to annotate, such as the rows of one chart page. Duplicates are removed before the 600 limit applies.Known errors
400invalid_request for an unknown query parameter, an invalid country, or storeIds that are missing, non-numeric or more than 600 unique IDs.401authentication_required for a missing or invalid credential.402subscription_required when access is not entitled.403authorization_required for a missing context:read scope or unavailable preview access.429rate_limited; wait for the Retry-After seconds, then send the same request again.Source
placement is the best rank across every stored App Store chart of the storefront on the latest chart day, with the previous day’s rank. Paid ads are collected per chosen app, so an app without ads has not been collected. Its paid signal is null. Organic posts are matched by product name, not account ownership, and their views are as last counted. organicViewsCountedAt says when. Keyword positions are observed App Store search result positions, not search volume.
Refresh
This GET reads the same stored evidence as GET /v1/market/overview and never starts collection. To ask for paid-ad or organic evidence on one app, record a data request for that competitor.
Rate limits
Market reads share the standard read budget of 60 requests per minute with every other PeekPanda read. During the preview the count is kept per client network address, not per plan, and it resets at the end of each one-minute window. A 429 response carries Retry-After when the server can say how long to wait: wait that many seconds, then send the same request again. Peak and Panda quotas for these reads are not enforced yet, and stored reads do not use your refresh allowance.
Batches
This read has no cursor. Send at most 600 unique store IDs per request and split a longer list into batches of 600. Each app carries at most 5 top keywords, and every weekly series has exactly 8 Monday-start weeks, oldest first, ending with the current, partial week.