Your Stripe dashboard shows individual subscriptions, but not your total annual recurring revenue at a glance. ARR is the metric that actually matters for SaaS forecasting and board reporting. Here's how to extract it directly from Stripe and surface it where your team needs it.
Extract Active Subscriptions from Stripe
The foundation is querying all active subscriptions and pulling their pricing information. Stripe's API makes this straightforward.
List all active subscriptions via the API
Use the Subscriptions endpoint to fetch active subscriptions. You'll need your Secret Key (found in Settings > API keys in the Stripe dashboard). Filter by status: 'active' to exclude trials, paused, or canceled subscriptions.
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const subscriptions = await stripe.subscriptions.list({
status: 'active',
limit: 100,
});
subscriptions.data.forEach(sub => {
console.log(`${sub.customer}: $${sub.items.data[0].price.unit_amount / 100} per ${sub.items.data[0].price.recurring.interval}`);
});Handle pagination for large subscription counts
The API returns up to 100 items by default. If you have more subscriptions, use the starting_after parameter with the has_more flag to fetch all pages. This is critical for accurate ARR calculations.
let allSubscriptions = [];
let hasMore = true;
let startingAfter = null;
while (hasMore) {
const response = await stripe.subscriptions.list({
status: 'active',
limit: 100,
starting_after: startingAfter,
});
allSubscriptions = allSubscriptions.concat(response.data);
hasMore = response.has_more;
if (hasMore) {
startingAfter = response.data[response.data.length - 1].id;
}
}
console.log(`Total subscriptions: ${allSubscriptions.length}`);Calculate ARR from Subscription Pricing
Once you have subscriptions, normalize them to annual revenue. Different subscriptions use different billing intervals (monthly, yearly, custom), so you need to convert everything to an annual figure.
Convert all billing intervals to annual value
Stripe subscriptions are billed on different intervals. Multiply monthly amounts by 12, keep yearly amounts as-is. Skip one-time charges and focus only on recurring revenue items.
const subscriptions = await stripe.subscriptions.list({ status: 'active' });
let arr = 0;
subscriptions.data.forEach(sub => {
sub.items.data.forEach(item => {
if (!item.price.recurring) return; // skip one-time charges
const amount = item.price.unit_amount / 100; // convert cents to dollars
const interval = item.price.recurring.interval;
if (interval === 'month') {
arr += amount * 12;
} else if (interval === 'year') {
arr += amount;
}
});
});
console.log(`Gross ARR: $${arr.toFixed(2)}`);Apply discounts and coupons to get net ARR
Many subscriptions have discounts applied via coupons. Check the discount field on each subscription and subtract both percentage and fixed-amount discounts. This gives you actual revenue, not list price.
let arr = 0;
subscriptions.data.forEach(sub => {
let subAnnualValue = 0;
sub.items.data.forEach(item => {
if (!item.price.recurring) return;
const amount = item.price.unit_amount / 100;
const interval = item.price.recurring.interval;
subAnnualValue += interval === 'month' ? amount * 12 : amount;
});
// Apply discount if present
if (sub.discount && sub.discount.coupon) {
if (sub.discount.coupon.amount_off) {
subAnnualValue -= (sub.discount.coupon.amount_off / 100);
} else if (sub.discount.coupon.percent_off) {
subAnnualValue -= (subAnnualValue * sub.discount.coupon.percent_off / 100);
}
}
arr += Math.max(subAnnualValue, 0); // prevent negative values
});
console.log(`Net ARR (after discounts): $${arr.toFixed(2)}`);Expose ARR in Your Dashboard or Alerts
Now expose the calculated ARR to your team. Cache the result rather than querying Stripe on every page load—the API will rate-limit you if you calculate ARR too frequently.
Build a cached API endpoint for ARR
Create a simple endpoint that returns ARR and caches the result. Update it on a schedule (e.g., hourly via a cron job) rather than on every request. This keeps your Stripe API quota low and response times fast.
// Express.js example
let cachedArr = null;
let lastCalculated = null;
app.get('/api/metrics/arr', (req, res) => {
res.json({ arr: cachedArr, lastUpdated: lastCalculated });
});
// Calculate ARR on a schedule (e.g., every hour)
setInterval(async () => {
const subscriptions = await stripe.subscriptions.list({ status: 'active' });
let arr = 0;
subscriptions.data.forEach(sub => {
sub.items.data.forEach(item => {
if (!item.price.recurring) return;
const amount = item.price.unit_amount / 100;
arr += item.price.recurring.interval === 'month' ? amount * 12 : amount;
});
});
cachedArr = arr;
lastCalculated = new Date().toISOString();
console.log(`ARR updated: $${arr.toFixed(2)}`);
}, 60 * 60 * 1000); // every hourExport to a BI tool or data warehouse
For deeper analysis, export subscription data to Google Sheets, Looker, Metabase, or your data warehouse. Pull data on a schedule and compute ARR trends, cohort analysis, and churn impact alongside other metrics.
// Export subscriptions to structured format for BI tools
const subscriptions = await stripe.subscriptions.list({ status: 'active', limit: 100 });
const exportData = subscriptions.data.map(sub => ({
subscription_id: sub.id,
customer_id: sub.customer,
plan_name: sub.items.data[0].price.nickname,
amount_cents: sub.items.data[0].price.unit_amount,
interval: sub.items.data[0].price.recurring.interval,
status: sub.status,
created_date: new Date(sub.created * 1000).toISOString(),
discount_percent: sub.discount?.coupon?.percent_off || 0,
}));
// Send to your BI tool via webhook or database insert
console.log(JSON.stringify(exportData, null, 2));Common Pitfalls
- Forgetting to filter by
status: 'active'—trials, paused, and canceled subscriptions will inflate your ARR calculation. - Not normalizing billing intervals—mixing monthly and yearly subscriptions without converting to annual values produces meaningless numbers.
- Ignoring applied discounts and coupons—your calculated ARR will be higher than actual revenue if you skip the discount calculation.
- Querying Stripe on every page load—you'll hit rate limits. Cache the result and update it on a schedule instead.
Wrapping Up
You now have a repeatable way to calculate ARR directly from Stripe and expose it to your team. This gives you a real-time view of recurring revenue, critical for forecasting and investor reporting. If you want to track this automatically across Stripe and other payment tools, Product Analyst can help.