List app store bundles
const url = 'https://qinto-api.blotout.io/app-store/bundles?cursorCreatedAt=2026-11-05T12%3A00%3A00.000Z&cursorId=2b7e4d10-8a3c-4f56-9e21-7c0d5b8f3a64&limit=50';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url 'https://qinto-api.blotout.io/app-store/bundles?cursorCreatedAt=2026-11-05T12%3A00%3A00.000Z&cursorId=2b7e4d10-8a3c-4f56-9e21-7c0d5b8f3a64&limit=50' \ --header 'Authorization: Bearer <token>'Lists the bundles a team can buy from the app store, newest first: each one’s price, trial, site limit, services, usage terms, the apps it holds right now and how its price compares with buying them separately. Call it to show a team what is on offer.
How it works
Only Live public bundles that can be sold right now are listed, so a page can hold fewer bundles than limit. Page with cursorCreatedAt and cursorId from the previous response’s nextCursor; a null cursor means the last page.
Required permissions
- None beyond a valid token; requires an OAuth token whose user belongs to a team
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Query Parameters
Section titled “Query Parameters”The nextCursor.createdAt of the previous page.
Example
2026-11-05T12:00:00.000ZThe nextCursor.createdAt of the previous page.
The nextCursor.id of the previous page.
Example
2b7e4d10-8a3c-4f56-9e21-7c0d5b8f3a64The nextCursor.id of the previous page.
Page size, 1–200 (default 50).
Example
50Page size, 1–200 (default 50).
Responses
Section titled “Responses”The page of bundles.
object
One page of the bundles on offer, newest first.
A bundle a team can buy: several apps on one line of its bill, for the sites it chooses.
object
Unique id of the bundle.
Display name of the bundle.
One-line description of what a team gets. Null when not set.
Longer description with its line breaks kept. Null when not set.
Display name of the organization that sells the bundle.
public is offered in the app store; private is reached only through a bundle link.
How the bundle charges: fixed is a base price, fixed_usage a base price and usage, usage only usage, and free nothing.
Base price per team, in US cents. Null when the model has none.
How often the base price is billed. Null when the model has none.
Length of the free trial in days. Null or 0 when there is none.
Price of each site past the limit in US cents, billed like the base price. Null when extra sites aren’t sold.
The services the bundle price covers.
A service the developer delivers, which the bundle price covers.
object
Name of the service. Empty only on a bundle that isn’t published yet.
What the team gets. Empty when not set.
What the service costs on its own, in US cents. Null only on a bundle that isn’t published yet.
How the price on its own is billed.
The most usage can add to a team’s bill each month, in US cents. Null when the model has no usage.
The priced meters. Any other meter the apps report is free.
What one meter of one app costs on a bundle. A meter with no term is free.
object
Id of the app that reports the meter.
Key of the meter in that app. The same key in two apps is two terms.
Name of the meter on the bundle.
Plural unit, as the bill shows it. Empty only on a bundle that isn’t published yet.
Units included each month for the whole team, across its sites.
Price past the included amount, in US micro-dollars.
How many units that price covers. Null only on a bundle that isn’t published yet.
The apps the bundle holds right now, A–Z by name.
An app a bundle holds right now, with what it costs on its own.
object
Unique id of the app.
The app’s slug, to pass as appSlug to the app store app operations.
Display name of the app.
How the app charges on its own.
The app’s own monthly price per site in US cents; 0 when it has no base price.
The legal links a buyer accepts when buying the bundle.
object
The developer’s terms for the app.
The developer’s privacy policy for the app.
object
The pricing model has no base price to compare.
object
The bundle holds no app and no priced monthly service.
object
A one-time price, which is not compared with recurring prices.
What the one-time services in the bundle cost on their own, in US cents, which no period includes.
object
Unlimited sites, so there is no fixed sum to compare.
What the apps cost on their own for one site over the period, in US cents.
How many months the compared period spans: 12 for a yearly price, else 1.
What the one-time services in the bundle cost on their own, in US cents, which no period includes.
object
The price against the apps at the site limit plus the monthly services.
The site limit the comparison uses.
How many months the compared period spans: 12 for a yearly price, else 1.
What the apps cost on their own at the site limit over the period, in US cents.
What the monthly services cost on their own over the period, in US cents.
appsCents plus servicesCents.
separatelyCents minus the bundle price, in US cents; 0 or less when the bundle is not cheaper. Null while the bundle has no price.
The saving as a whole percent of separatelyCents; 0 when there is none.
What the one-time services in the bundle cost on their own, in US cents, which no period includes.
Pass both fields as cursorCreatedAt / cursorId for the next page; null on the last page.
object
Creation instant of the last listed bundle.
Id of the last listed bundle.
Example
{ "bundles": [ { "bundleId": "2b7e4d10-8a3c-4f56-9e21-7c0d5b8f3a64", "name": "Growth Stack", "description": "Everything a store needs.", "longDescription": "Who the bundle is for.", "publisher": "Acme Dev", "audience": "public", "pricingModel": "fixed", "basePriceCents": 2900, "billingInterval": "monthly", "trialDays": 14, "siteLimit": 3, "extraSitePriceCents": 500, "services": [ { "name": "Priority support", "detail": "4-hour response, shared Slack channel", "priceCents": 20000, "basis": "monthly" } ], "usageCapCents": 10000, "meterTerms": [ { "appId": "7f3a1c92-4b6d-4e8a-b1d3-5c9e2f7a6b10", "key": "events", "name": "Events", "unit": "events", "includedAmount": 100000, "priceMicros": 500000, "perUnits": 1000 } ], "apps": [ { "appId": "7f3a1c92-4b6d-4e8a-b1d3-5c9e2f7a6b10", "slug": "facebook-capi", "name": "Facebook CAPI", "pricingModel": "fixed", "basePriceCents": 1900, "legal": { "termsUrl": "https://acme.example/terms", "privacyUrl": "https://acme.example/privacy" } } ], "comparison": { "kind": "fixed_sites", "sites": 3, "months": 1, "appsCents": 5700, "servicesCents": 0, "separatelyCents": 5700, "savingCents": 2800, "savingPercent": 49, "oneTimeServicesCents": 50000 } } ], "nextCursor": { "createdAt": "2026-11-05T12:00:00.000Z", "id": "2b7e4d10-8a3c-4f56-9e21-7c0d5b8f3a64" }}invalid_body — The JSON body is unparseable or failed schema validation; details pinpoints the offending fields when the body could be parsed.
object
Machine-readable error slug; switch on this value rather than on the HTTP status alone.
Field-level validation errors: a tree keyed by input property, each node carrying an errors array of human-readable messages. Render it to help the caller correct the request.
object
Example
{ "error": "invalid_body", "details": { "errors": [], "properties": { "teamId": { "errors": [ "Invalid UUID" ] } } }}unauthenticated — The Authorization: Bearer token is missing, malformed, or no longer valid. Generate a team API token or obtain a new OAuth access token.
object
Machine-readable error slug; switch on this value rather than on the HTTP status alone.
Example
{ "error": "unauthenticated"}forbidden — The bearer credential lacks the required permission or belongs to another team, or its user lacks the organization role a developer operation needs. Update its policies, use a credential bound to the addressed team, or ask an organization owner for the role; message states the reason when one is known.
object
Machine-readable error slug; switch on this value rather than on the HTTP status alone.
Human-readable reason for this specific failure, intended for display — do not parse it programmatically.
Example
{ "error": "forbidden", "message": "The Cloudflare API token is invalid or expired."}internal — Unexpected server error. Nothing is guaranteed to have been persisted; idempotent operations are safe to retry.
object
Machine-readable error slug; switch on this value rather than on the HTTP status alone.
Example
{ "error": "internal"}