ARR (Annual Recurring Revenue) is your revenue normalized to a 12-month basis—critical for SaaS metrics. Stripe doesn't calculate this automatically, so you need to query subscription data and normalize for billing frequency.
Query active subscriptions via the Stripe API
First, pull all active subscriptions. Stripe's API returns paginated results, so handle pagination if you have thousands of subscriptions.
Fetch active subscriptions from Stripe
Use the Subscriptions endpoint (or the Node SDK) to list all subscriptions with status: 'active'. This filters out canceled or past-due subscriptions.
const stripe = require('stripe')('sk_test_...');
const subscriptions = await stripe.subscriptions.list({
status: 'active',
limit: 100,
});
console.log(subscriptions.data);Handle pagination for large subscription lists
If you have more than 100 subscriptions, use the starting_after parameter to paginate through results.
let allSubscriptions = [];
let hasMore = true;
let startingAfter = undefined;
while (hasMore) {
const batch = await stripe.subscriptions.list({
status: 'active',
limit: 100,
starting_after: startingAfter,
});
allSubscriptions = allSubscriptions.concat(batch.data);
hasMore = batch.has_more;
if (batch.data.length > 0) {
startingAfter = batch.data[batch.data.length - 1].id;
}
}created[gte]: timestamp if you only want subscriptions created after a certain date.Calculate ARR from billing amounts and cycles
ARR depends on the billing cycle. Stripe subscriptions can be monthly, annual, weekly, or daily. Normalize everything to a 12-month basis.
Extract the subscription amount and billing interval
Each subscription has an items array with pricing details. The price object contains recurring data: interval (month, year, week, day) and interval_count (e.g., 2 for every 2 months).
const sub = subscriptions.data[0];
const priceItem = sub.items.data[0];
const { amount, currency } = priceItem.price;
const { interval, interval_count } = priceItem.price.recurring;
console.log(`Amount: $${(amount / 100).toFixed(2)} ${currency}, Billing: every ${interval_count} ${interval}(s)`);Calculate ARR using the billing interval
Convert the subscription amount to a 12-month basis by multiplying by the number of billing periods per year. The formula varies by interval type—the function below handles all cases.
function calculateArrForSubscription(subscription) {
const priceItem = subscription.items.data[0];
const amount = priceItem.price.amount;
const { interval, interval_count } = priceItem.price.recurring;
const intervalsPerYear = {
'day': 365,
'week': 52,
'month': 12,
'year': 1,
};
const arr = amount * (intervalsPerYear[interval] / interval_count);
return arr / 100; // Convert cents to dollars
}
const arr = calculateArrForSubscription(sub);
console.log(`ARR: $${arr.toFixed(2)}`);items array. Account for discounts with discount.coupon.percent_off if you need net ARR.Sum total ARR and handle edge cases
Once you have ARR per subscription, aggregate to get total company ARR. Filter out trials and paused subscriptions.
Calculate total ARR across all subscriptions
Loop through all subscriptions, sum their ARR values, and convert from cents to dollars.
let totalArr = 0;
allSubscriptions.forEach(sub => {
const arr = calculateArrForSubscription(sub);
totalArr += arr;
});
console.log(`Total ARR: $${totalArr.toFixed(2)}`);Filter out trial subscriptions if needed
Stripe subscriptions with a trial have a trial_end date in Unix timestamp. Exclude active trials by checking if trial_end > now.
let arr = 0;
const now = Math.floor(Date.now() / 1000);
allSubscriptions
.filter(sub => !sub.trial_end || sub.trial_end < now) // Exclude active trials
.forEach(sub => {
arr += calculateArrForSubscription(sub);
});
console.log(`ARR (excl. trials): $${arr.toFixed(2)}`);status: 'past_due'). Check separately if you want to track revenue at risk.Common Pitfalls
- Forgetting to multiply by 12—if a subscription is $100/mo, ARR is $1,200, not $100
- Not normalizing custom billing intervals (e.g., bi-weekly)—calculate based on frequency per year, not just interval type
- Including paused or past_due subscriptions—these shouldn't count as active recurring revenue until resolved
- Ignoring discounts and prorations—use invoice data if you need net ARR after discounts or one-time adjustments
Wrapping Up
ARR in Stripe requires summing subscription amounts and normalizing billing cycles. The query is straightforward with the Stripe API, but the calculation gets tricky with custom intervals and discounts. If you want to track this automatically across tools, Product Analyst can help.