App Store catalog
The catalog endpoints expose Ranksy's view of the public Shopify App Store — the same scraped data that powers the public site, not your team's private partner metrics. GET /api/v1/shopify/apps searches and lists the whole catalog; GET /api/v1/categories/:handle returns one category (with optional review insights); and GET /api/v1/categories/:handle/rankings returns the scraped ranked apps inside a category. These are cross-app — you can read any app or category in the store, not just your own.
Catalog endpoints are not team-scoped — they read public App Store data, so there is no :app path parameter and no Shopify Partner connection involved (no 409). A valid API key carrying the catalog:read scope is still required. Categories are resolved by their handle (slug); an unknown or untracked handle returns a 404 resource_not_found, never an empty result.
Search the catalog
List and filter the Shopify App Store catalog. This is a standard offset-paginated collection — data plus a meta block (total, current_page, last_page, per_page) — not the object: "list" envelope used by series endpoints. Each row is a full catalog app.
Query parameters
- Name
page- Type
- integer
- Description
Page to return.
1-based, default1.
- Name
per_page- Type
- integer
- Description
Rows per page. Minimum
1, maximum1000.
- Name
state- Type
- string
- Description
Filter by listing state:
Active,Delisted, orDeleted.
- Name
category- Type
- string
- Description
Restrict to apps in a category, by category handle (e.g.
store-management).
- Name
min_installs- Type
- integer
- Description
Only apps with at least this many estimated installs. Minimum
0.
- Name
updated_since- Type
- string
- Description
ISO 8601 date-time. Only apps whose catalog record changed at or after this instant.
Request
curl -G https://ranksyapp.com/api/v1/shopify/apps \
-H "Authorization: Bearer rk_live_..." \
-d category=store-management \
-d state=Active \
-d min_installs=500 \
-d per_page=2
Response
{
"data": [
{
"id": "48213.acme-reviews",
"token": "acme-reviews",
"platform": "shopify",
"name": "Acme Reviews",
"description": "Collect and display product reviews.",
"state": "Active",
"categories": ["store-management", "marketing"],
"plans": [
{ "name": "Free", "monthly_cost_cents": 0 },
{ "name": "Pro", "monthly_cost_cents": 1999 }
],
"installs": 12840,
"installs_30d": null,
"installs_90d": null,
"review_count": 1284,
"reviews_30d": null,
"reviews_90d": null,
"average_rating": 4.9,
"vendor_name": "Acme Labs",
"vendor_email": null,
"vendor_website": "https://acmelabs.io",
"app_store_url": "https://apps.shopify.com/acme-reviews",
"icon_url": "https://cdn.shopify.com/.../icon.png",
"created_at": "2023-04-11T00:00:00+00:00",
"free_trial_days": 14
}
],
"meta": {
"total": 1820,
"current_page": 1,
"last_page": 910,
"per_page": 2
}
}
Retrieve a category
A single App Store category, addressed by its handle (slug). The response is a single-object envelope (object: "category") carrying category meta — slug, name, apps_count — plus a review-insight payload selected by insight_type.
The default overview payload adds stats, rating_distribution, and sentiment_distribution. The complaints and features variants replace those with their own keys (pain points / missing features, common / loved features); the overview-only keys are then omitted entirely.
Path parameter
- Name
handle- Type
- string
- Description
The category slug, e.g.
store-management. Unknown / untracked handles return a404 resource_not_found.
Query parameters
- Name
insight_type- Type
- string
- Description
Which insight payload to attach:
overview(default — rating + sentiment distributions),complaints(pain points / missing features), orfeatures(common + loved features).
Request
curl https://ranksyapp.com/api/v1/categories/store-management \
-H "Authorization: Bearer rk_live_..."
Response (overview)
{
"object": "category",
"slug": "store-management",
"name": "Store management",
"apps_count": 1820,
"insight_type": "overview",
"stats": {
"total_reviews": 184203,
"average_rating": 4.6
},
"rating_distribution": [
{ "rating": 5, "count": 142018, "percentage": 77.1 },
{ "rating": 4, "count": 21884, "percentage": 11.9 }
],
"sentiment_distribution": [
{ "sentiment": "positive", "count": 151204, "percentage": 82.1 },
{ "sentiment": "negative", "count": 12940, "percentage": 7.0 }
]
}
Category rankings
The scraped ranked apps within a category — App Store category rank plus the movement since the previous scrape, one ranking row per app. Returned as the object: "list" envelope. The underlying scrape is top-50: that slice is fetched once and paginated client-side, so total_count never exceeds 50.
The category summary, the scrape source / data_date, the echoed sort, and a category block (slug, name, total_apps) ride in the envelope metadata. There is no coverage block — catalog lists are not date-windowed.
Path parameter
- Name
handle- Type
- string
- Description
The category slug, e.g.
store-management. Unknown / untracked handles return a404 resource_not_found.
Query parameters
- Name
sort- Type
- string
- Description
Ordering:
rank(scraped category rank ascending — the default),rating(descending), orreviews(descending).
- Name
page- Type
- integer
- Description
Page to return.
1-based, default1.
- Name
per_page- Type
- integer
- Description
Rows per page. Minimum
1, maximum50.
Request
curl -G https://ranksyapp.com/api/v1/categories/store-management/rankings \
-H "Authorization: Bearer rk_live_..." \
-d sort=rank \
-d per_page=2
Response
{
"object": "list",
"url": "/api/v1/categories/store-management/rankings",
"page": 1,
"per_page": 2,
"total_count": 50,
"has_more": true,
"metadata": {
"source": "scraped",
"data_date": "2026-05-29",
"sort": "rank",
"category": {
"slug": "store-management",
"name": "Store management",
"total_apps": 1820
},
"summary": {}
},
"data": [
{
"rank": 1,
"change": 0,
"app": {
"slug": "acme-reviews",
"name": "Acme Reviews",
"rating": 4.9,
"reviews_count": 1284,
"developer": "Acme Labs",
"built_for_shopify": true
}
},
{
"rank": 2,
"change": 1,
"app": {
"slug": "bolt-bulk-editor",
"name": "Bolt Bulk Editor",
"rating": 4.8,
"reviews_count": 932,
"developer": "Bolt Apps",
"built_for_shopify": false
}
}
]
}
Response shapes
Catalog app
A row of GET /shopify/apps. Cross-app public catalog data; the installs_30d / installs_90d / reviews_30d / reviews_90d / vendor_email fields are reserved and always null in v1.
- Name
id- Type
- string
- Description
Stable catalog id,
{numericId}.{slug}(e.g.48213.acme-reviews).
- Name
token- Type
- string
- Description
The app's App Store slug.
- Name
platform- Type
- string
- Description
Always
"shopify".
- Name
name- Type
- string | null
- Description
Listing name.
- Name
description- Type
- string | null
- Description
The listing introduction / tagline.
- Name
state- Type
- string
- Description
Listing state:
Active,Delisted, orDeleted.
- Name
categories- Type
- string[]
- Description
Category handles this app is listed under. Present only when expanded.
- Name
plans- Type
- array
- Description
Pricing plans, sorted by display order: each
{ name, monthly_cost_cents }. Present only when expanded.
- Name
installs- Type
- integer | null
- Description
Estimated total installs.
- Name
review_count- Type
- integer
- Description
Total review count.
- Name
average_rating- Type
- number | null
- Description
Average star rating, rounded to one decimal.
- Name
vendor_name- Type
- string | null
- Description
Developer name. Present only when expanded.
- Name
vendor_website- Type
- string | null
- Description
Developer website. Present only when expanded.
- Name
app_store_url- Type
- string
- Description
Canonical App Store listing URL.
- Name
icon_url- Type
- string
- Description
App icon URL.
- Name
created_at- Type
- string | null
- Description
Listing launch date, ISO 8601.
- Name
free_trial_days- Type
- integer | null
- Description
Longest free-trial length across the app's plans, or
null. Present only when plans are expanded.
Category (object: "category")
The single-object response of GET /categories/:handle. The head is always present; the trailing three keys appear only for insight_type=overview.
- Name
object- Type
- string
- Description
Always
"category".
- Name
slug- Type
- string
- Description
The category slug (the
:handlepath param).
- Name
name- Type
- string
- Description
Human-readable category name.
- Name
apps_count- Type
- integer
- Description
Number of apps listed in this category.
- Name
insight_type- Type
- string
- Description
Which insight payload this response carries:
overview,complaints, orfeatures.
- Name
stats- Type
- object
- Description
Overview only. Aggregate review stats:
{ total_reviews, average_rating }.
- Name
rating_distribution- Type
- array
- Description
Overview only. Review count + share per star rating, 5 down to 1:
{ rating, count, percentage }.
- Name
sentiment_distribution- Type
- array
- Description
Overview only. Review count + share per sentiment bucket (positive, neutral, mixed, negative):
{ sentiment, count, percentage }.
Ranking row
One row of GET /categories/:handle/rankings.
- Name
rank- Type
- integer
- Description
The app's scraped rank within this category for the latest data date.
- Name
change- Type
- integer
- Description
Rank movement vs the previous scrape (positive = improved toward #1);
0when there is no prior data point.
- Name
app- Type
- object
- Description
Card for the ranked app:
{ slug, name, rating, reviews_count, developer, built_for_shopify }.
Errors
Beyond the universal 401 / 403 / 422 / 429 (see Errors), the catalog endpoints return:
404 resource_not_found— oncategories/:handleandcategories/:handle/rankingsonly: the addressed category does not exist or is not tracked (inactive categories404too).error.paramishandle. The catalog search (/shopify/apps) never404s — an over-filtered query returns an emptydataarray instead.
Catalog endpoints never read a Shopify Partner integration, so they never return a 409.