LTV cohorts
A single endpoint returns per-acquisition-cohort lifetime value as a paginated object: "list" — GET /api/v1/apps/:app/ltv/cohorts. Each row is one install month, with the cohort's customer count and its average / median / total lifetime revenue. Monetary values share one base payout currency, declared once on the envelope.
LTV cohorts
Per-acquisition-cohort lifetime value — one row per install month, with the cohort's customer count and its average / median / total lifetime revenue. This is the cohort detail behind the aggregate ltv metric: LTV per shop is SUM(gross_amount) over its real paid transactions, and the cohort is the month a shop first installed. Rows are ordered oldest cohort first.
Monetary values share one base payout currency, declared once on the envelope's currency key (never per row). Only pagination parameters are accepted — the breakdown is a full computed set.
Path parameter
- Name
app- Type
- string
- Description
The team app's ULID.
Query parameters
- Name
page- Type
- integer
- Description
Page to return. Default
1.
- Name
per_page- Type
- integer
- Description
Cohorts per page. Default
100, maximum100.
Request
curl https://ranksyapp.com/api/v1/apps/01JN4HKQX0000000000000000/ltv/cohorts \
-H "Authorization: Bearer rk_live_..."
Response
{
"object": "list",
"url": "/api/v1/apps/01JN4HKQX0000000000000000/ltv/cohorts",
"currency": "USD",
"page": 1,
"per_page": 100,
"total_count": 18,
"has_more": false,
"metadata": {
"summary": { "total_cohorts": 18 },
"value_note": "LTV is per-shop SUM(gross_amount) over real paid transactions; cohort = the month a shop first installed. Amounts are in the app's base payout currency."
},
"data": [
{
"cohort": "2026-01",
"customers": 42,
"average_ltv": 184.5,
"median_ltv": 96.0,
"total_revenue": 7749.0
},
{
"cohort": "2026-02",
"customers": 51,
"average_ltv": 132.4,
"median_ltv": 72.0,
"total_revenue": 6752.4
}
]
}
LTV cohorts decompose the aggregate ltv series in the revenue metrics.
Response shape
The standard object: "list" envelope. This list is not date-windowed in the series sense, so there is no coverage block. Unlike subscriptions and transactions, the currency key sits on the envelope and applies to every row.
- Name
object- Type
- string
- Description
Always
"list".
- Name
url- Type
- string
- Description
The endpoint path.
- Name
currency- Type
- string
- Description
Base-currency ISO code. Applies to every row's monetary values.
- Name
page- Type
- integer
- Description
Current page number.
- Name
per_page- Type
- integer
- Description
Rows per page.
- Name
total_count- Type
- integer
- Description
Total cohorts across all pages.
- Name
has_more- Type
- boolean
- Description
Whether further pages exist.
- Name
metadata- Type
- object
- Description
Carries
summary.total_cohortsand avalue_notedescribing the calculation.
- Name
data- Type
- array
- Description
The cohort rows for this page.
Cohort row
- Name
cohort- Type
- string
- Description
The install month (
YYYY-MM) the shops in this cohort first installed.
- Name
customers- Type
- integer
- Description
Number of shops in the cohort.
- Name
average_ltv- Type
- number
- Description
Average lifetime revenue per shop, in the envelope
currency.
- Name
median_ltv- Type
- number
- Description
Median lifetime revenue per shop, in the envelope
currency.
- Name
total_revenue- Type
- number
- Description
Total lifetime revenue for the cohort, in the envelope
currency.
Pagination
Walk the result set with page and per_page (default 100, maximum 100), and check has_more / total_count to know when to stop. The envelope currency and metadata apply to every page.
Errors
Beyond the universal 401 / 403 / 422 / 429 (see Errors), this endpoint returns:
409— the app's Shopify Partner integration isn't usable:partner_integration_not_connected,partner_integration_unauthorized(reconnect in Ranksy), orpartner_integration_pending(first sync still running — retry shortly).
A bad query parameter (out-of-range per_page) surfaces as a 422.