Subscription health for an app: active subscribers total and per plan, movements inside the window (new subscriptions, cancellations, reactivations, plan changes with from/to pairs), and the churn rate.
Request
curl "https://www.vevee.org/api/v1/subscribers/stats?window=30" \
-H "Authorization: Bearer sk_live_YOUR_KEY"Parameters
| Name | Type | Description | |
|---|---|---|---|
window | optional | number | Window size in days, 1 to 365. Defaults to 30. |
Response
data matches the SubscriberStats shape used internally. byPlan only counts subscriptions active as of now (not ended); the window block counts transitions that happened inside the requested window, read from the append-only subscription_events log.
{
"ok": true,
"data": {
"windowDays": 30,
"activeSubscribers": 842,
"byPlan": [
{ "planId": "plan_free", "planName": "Free", "count": 601 },
{ "planId": "plan_pro", "planName": "Pro", "count": 219 },
{ "planId": "plan_team", "planName": "Team", "count": 22 }
],
"window": {
"newSubscriptions": 74,
"canceled": 31,
"reactivated": 6,
"planChanges": 18,
"changes": [
{
"fromPlanId": "plan_free",
"fromPlanName": "Free",
"toPlanId": "plan_pro",
"toPlanName": "Pro",
"count": 13
},
{
"fromPlanId": "plan_pro",
"fromPlanName": "Pro",
"toPlanId": "plan_free",
"toPlanName": "Free",
"count": 5
}
]
},
"churnRate": 0.0355
}
}How churn is computed
churnRate is canceled / (activeSubscribers + canceled), using the window's canceled count and the current-as-of-now activeSubscribers total - not a canceled-at-window-start denominator. It is 0 when the denominator is 0.
“Canceled” here means a canceled row in subscription_events, which only exists for apps using the hard-cancel pattern. Vevee supports two cancellation patterns per app, and only one shows up in this number:
- Apps with a free plan call
upsertSubscriptionwithplanId: 'free'on cancel. This logs aplan_changedrow, not acanceledrow - the end user keeps using the app under free limits, and this movement counts towardplanChanges, notcanceled. - Apps without a free plan call
cancelSubscription, which setsends_atand logs acanceledrow. Onceends_atpasses, the end user is blocked. This is the patternchurnRateis actually measuring.
If an app mixes both patterns for different users, churnRate only reflects the hard-cancel half of the picture - the free-plan downgrades are still visible in window.changes.
Errors
invalid_key(401) - bad or revoked API key.requires_secret_key(403) - apk_*key was used.invalid_request(400) -windowoutside 1 to 365.
get_subscriber_stats - see MCP server.