ARPU—Average Revenue Per User—is critical for SaaS. Your Stripe dashboard shows total revenue, but not per-customer. You need to query your customers and charges to see which segments are actually driving value. This is how you build that number.
Fetch Customers and Sum Their Revenue
Start by pulling your customer list and all succeeded charges, then group revenue by customer.
Paginate through all customers
Use stripe.customers.list() to fetch customers 100 at a time. The response includes a has_more flag—loop until it's false.
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
let allCustomers = [];
let startingAfter = undefined;
let hasMore = true;
while (hasMore) {
const response = await stripe.customers.list({
limit: 100,
starting_after: startingAfter,
});
allCustomers = allCustomers.concat(response.data);
hasMore = response.has_more;
startingAfter = response.data[response.data.length - 1]?.id;
}
console.log(`Total customers: ${allCustomers.length}`);
Group charges by customer
Call stripe.charges.list() with status: 'succeeded' to get only completed payments. Loop through all pages and sum the amount for each customer.
const revenueByCustomer = {};
let chargesStartingAfter = undefined;
let chargesHasMore = true;
while (chargesHasMore) {
const response = await stripe.charges.list({
limit: 100,
starting_after: chargesStartingAfter,
status: 'succeeded',
});
response.data.forEach(charge => {
if (charge.customer) {
revenueByCustomer[charge.customer] = (revenueByCustomer[charge.customer] || 0) + charge.amount;
}
});
chargesHasMore = response.has_more;
chargesStartingAfter = response.data[response.data.length - 1]?.id;
}
console.log('Revenue by customer (in cents):', revenueByCustomer);
Calculate ARPU
Sum all revenues and divide by the number of customers who generated revenue. Convert from cents to dollars.
const customerIds = Object.keys(revenueByCustomer);
const totalRevenue = Object.values(revenueByCustomer).reduce((sum, amt) => sum + amt, 0);
const arpu = totalRevenue / customerIds.length / 100;
console.log(`Active customers: ${customerIds.length}`);
console.log(`Total revenue: $${(totalRevenue / 100).toFixed(2)}`);
console.log(`ARPU: $${arpu.toFixed(2)}`);
created timestamp on charges.Calculate Period ARPU (Monthly, Quarterly)
All-time ARPU is useful, but monthly or quarterly ARPU is more actionable. Use date filters to isolate a time period.
Filter charges by date range
Pass a created object with gte and lte to stripe.charges.list(). Use Unix timestamps in seconds (not milliseconds).
// Get charges for January 2024
const startOfJan = Math.floor(new Date('2024-01-01').getTime() / 1000);
const endOfJan = Math.floor(new Date('2024-02-01').getTime() / 1000);
const charges = await stripe.charges.list({
limit: 100,
status: 'succeeded',
created: {
gte: startOfJan,
lte: endOfJan,
},
});
console.log(`${charges.data.length} charges in January`);
Sum revenue and count unique customers
Loop through charges in the period and track unique customer IDs. Divide total revenue by unique customer count.
const periodRevenue = {};
const uniqueCustomers = new Set();
let pageStartingAfter = undefined;
let pageHasMore = true;
while (pageHasMore) {
const pageCharges = await stripe.charges.list({
limit: 100,
starting_after: pageStartingAfter,
status: 'succeeded',
created: { gte: startOfJan, lte: endOfJan },
});
pageCharges.data.forEach(charge => {
if (charge.customer) {
periodRevenue[charge.customer] = (periodRevenue[charge.customer] || 0) + charge.amount;
uniqueCustomers.add(charge.customer);
}
});
pageHasMore = pageCharges.has_more;
pageStartingAfter = pageCharges.data[pageCharges.data.length - 1]?.id;
}
const totalPeriodRevenue = Object.values(periodRevenue).reduce((sum, amt) => sum + amt, 0);
const periodArpu = totalPeriodRevenue / uniqueCustomers.size / 100;
console.log(`January ARPU: $${periodArpu.toFixed(2)}`);
Segment ARPU by Subscription Plan
ARPU differs by plan tier. Store plan info in customer metadata and calculate ARPU separately for each.
Tag customers with their plan
When creating or updating a customer, add a metadata object with their plan name. Use stripe.customers.create() or stripe.customers.update().
// Create a new customer with plan metadata
await stripe.customers.create({
email: '[email protected]',
metadata: {
plan: 'pro',
team: 'acme',
},
});
// Update existing customer
await stripe.customers.update('cus_abc123', {
metadata: {
plan: 'enterprise',
},
});
Calculate ARPU per plan
Fetch all customers, filter by metadata.plan, then sum charges for each plan segment. Compare ARPU across tiers.
const plans = ['free', 'starter', 'pro', 'enterprise'];
const arpuByPlan = {};
// Fetch all customers once
let allCustomersForSegment = [];
let startingAfter = undefined;
let hasMore = true;
while (hasMore) {
const response = await stripe.customers.list({
limit: 100,
starting_after: startingAfter,
});
allCustomersForSegment = allCustomersForSegment.concat(response.data);
hasMore = response.has_more;
startingAfter = response.data[response.data.length - 1]?.id;
}
for (const plan of plans) {
const customersOnPlan = allCustomersForSegment.filter(c => c.metadata?.plan === plan);
const planRevenue = {};
for (const customer of customersOnPlan) {
const charges = await stripe.charges.list({ customer: customer.id, status: 'succeeded' });
planRevenue[customer.id] = charges.data.reduce((sum, ch) => sum + ch.amount, 0);
}
const totalRevenue = Object.values(planRevenue).reduce((sum, amt) => sum + amt, 0);
arpuByPlan[plan] = totalRevenue / customersOnPlan.length / 100;
}
console.log(arpuByPlan);
Common Pitfalls
- Forgetting to paginate—Stripe limits results to 100 per page. Miss pagination and you're calculating ARPU on incomplete data.
- Using milliseconds instead of Unix seconds—Stripe's
createdfilter expects seconds. Divide JavaScript timestamps by 1000. - Including free or zero-revenue customers—ARPU should be total revenue ÷ paying customers, not all customers.
- Not filtering by
status: 'succeeded'—Refunds and failed charges skew ARPU down. Only count completed payments.
Wrapping Up
You now know how to calculate ARPU by period and plan directly from Stripe. Track it monthly to catch revenue trends early. If you want to calculate this automatically across all your tools and build dashboards without API queries, Product Analyst can help.