Customer LTV

Your paying shops ranked by estimated lifetime value, each row carrying current MRR, tenure, plan, and first-touch acquisition channel. GET /api/v1/apps/:app/ltv/customers returns a paginated object: "list", sorted by ltv descending by default, and requires the metrics:read scope.


GET/api/v1/apps/:app/ltv/customers

Customer LTV

Returns the app's paying shops as a paginated object: "list", highest estimated lifetime value first by default. Each row pairs the shop's identity with its current_mrr, installed_at, lifetime_months, ltv, plan_name, and first_touch_channel, so you can see who your most valuable merchants are and how they were acquired.

total_count is the total number of paying shops and has_more tells you when to fetch the next page. Amounts are expressed in the envelope's top-level currency.

Path parameter

  • Name
    app
    Type
    string
    Description

    The team app's ULID, e.g. 01JN4HKQX0000000000000000.

Query parameters

  • Name
    sort_by
    Type
    string
    Description

    Sort column: ltv (default), current_mrr, or installed_at. Any other value returns a 422.

  • Name
    cursor
    Type
    string
    Description

    Opt-in cursor for cursor pagination. Opaque, maximum 64 characters. See Pagination.

  • Name
    page
    Type
    integer
    Description

    The page to return. Minimum 1.

  • Name
    per_page
    Type
    integer
    Description

    Rows per page, 1–100.

Request

GET
/api/v1/apps/:app/ltv/customers
curl -G https://ranksyapp.com/api/v1/apps/01JN4HKQX0000000000000000/ltv/customers \
  -H "Authorization: Bearer rk_live_..." \
  -d sort_by=ltv \
  -d per_page=25

Response

{
  "object": "list",
  "url": "/api/v1/apps/01JN4HKQX0000000000000000/ltv/customers",
  "currency": "USD",
  "page": 1,
  "per_page": 25,
  "total_count": 96,
  "has_more": true,
  "metadata": {},
  "data": [
    {
      "shop_domain": "skinnupweightloss.myshopify.com",
      "shop_name": "Skinny Up!",
      "plan_name": "Advanced",
      "current_mrr": 9.99,
      "installed_at": "2023-08-15T10:00:00+00:00",
      "lifetime_months": 34,
      "ltv": 339.66,
      "first_touch_channel": "Organic Search"
    }
  ]
}

Response fields

The list is the shared object: "list" envelope; each item in data is a customer LTV row.

  • Name
    object
    Type
    string
    Description

    Always "list".

  • Name
    url
    Type
    string
    Description

    The endpoint path for this resource.

  • Name
    currency
    Type
    string
    Description

    The currency all amounts in the response are expressed in, e.g. USD.

  • Name
    page
    Type
    integer
    Description

    The current page number (1-based). null in cursor mode.

  • Name
    per_page
    Type
    integer
    Description

    Rows per page.

  • Name
    total_count
    Type
    integer
    Description

    Total paying shops across all pages. null in cursor mode.

  • Name
    has_more
    Type
    boolean
    Description

    Whether further pages exist.

  • Name
    next_cursor
    Type
    string
    Description

    Cursor mode only — the cursor to pass on the next request, or null when the list is exhausted.

  • Name
    metadata
    Type
    object
    Description

    Reserved metadata object.

  • Name
    data
    Type
    array
    Description

    The customer LTV rows for this page. Each row has:

  • Name
    shop_domain
    Type
    string
    Description

    The shop's *.myshopify.com domain, or null when unknown.

  • Name
    shop_name
    Type
    string
    Description

    Shop name, or null when unknown.

  • Name
    plan_name
    Type
    string
    Description

    The shop's current plan name, or null when unknown.

  • Name
    current_mrr
    Type
    number
    Description

    Monthly recurring revenue in the plan currency, or null when unknown.

  • Name
    installed_at
    Type
    string
    Description

    ISO-8601 install timestamp, or null.

  • Name
    lifetime_months
    Type
    integer
    Description

    Months the shop has been a customer, or null when unknown.

  • Name
    ltv
    Type
    number
    Description

    Estimated lifetime value, or null when unknown.

  • Name
    first_touch_channel
    Type
    string
    Description

    The acquisition channel of the shop's first touch, or null when unknown.


Pagination

By default the list uses offset pagination: walk it with page and per_page (1–100), and check has_more / total_count to know when to stop.

You can opt into cursor pagination by passing the cursor parameter (start with it empty). Each response then returns a next_cursor; pass it on the next request and repeat until next_cursor is null (equivalently, has_more is false). In cursor mode page and total_count come back null.

Cursor mode is stable under concurrent inserts and is not subject to the page-mode row cap, so it is the recommended way to export the full list into a warehouse (e.g. BigQuery).


Errors

Beyond the universal 401 / 403 / 422 / 429 (see Errors):

  • The list never 404s — an app with no configured Partner API integration, or no paying customers, returns an empty data list (a 200).
  • An unknown sort_by surfaces as 422 validation_failed.

Was this page helpful?