ARPU is one of the most telling metrics for a subscription business. In Stripe, you calculate it by dividing your total revenue by your customer count. The challenge isn't the math—it's reliably pulling the right numbers from Stripe's API and keeping them fresh.
Calculate ARPU from Stripe's API
Start by fetching your invoices and customer counts directly from Stripe.
Fetch all paid invoices for a time period
Use the invoices endpoint to get all invoices in a date range. Filter by status: paid to only count revenue that's actually landed. Use created to limit to a specific month or year.
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const invoices = await stripe.invoices.list({
status: 'paid',
created: {
gte: Math.floor(new Date('2025-01-01').getTime() / 1000),
lte: Math.floor(new Date('2025-12-31').getTime() / 1000)
},
limit: 100
});
const totalRevenue = invoices.data.reduce((sum, inv) => sum + inv.total, 0) / 100;
console.log(`Total revenue: $${totalRevenue}`);
// Handle pagination
let allInvoices = invoices.data;
let hasMore = invoices.has_more;
let lastId = invoices.data[invoices.data.length - 1]?.id;
while (hasMore) {
const page = await stripe.invoices.list({
status: 'paid',
created: { gte: Math.floor(new Date('2025-01-01').getTime() / 1000) },
starting_after: lastId,
limit: 100
});
allInvoices = allInvoices.concat(page.data);
hasMore = page.has_more;
lastId = page.data[page.data.length - 1]?.id;
}Count active customers in the same period
Get your customer count for the same timeframe using the customers endpoint or the Customers section in the Stripe Dashboard. For ARPU, count only customers with activity (invoices) in your period.
const customers = await stripe.customers.list({
created: {
gte: Math.floor(new Date('2025-01-01').getTime() / 1000),
lte: Math.floor(new Date('2025-12-31').getTime() / 1000)
},
limit: 100
});
let allCustomers = customers.data;
let hasMore = customers.has_more;
let lastId = customers.data[customers.data.length - 1]?.id;
while (hasMore) {
const page = await stripe.customers.list({
created: { gte: Math.floor(new Date('2025-01-01').getTime() / 1000) },
starting_after: lastId,
limit: 100
});
allCustomers = allCustomers.concat(page.data);
hasMore = page.has_more;
lastId = page.data[page.data.length - 1]?.id;
}
const customerCount = allCustomers.length;
console.log(`Active customers: ${customerCount}`);
const arpu = totalRevenue / customerCount;
console.log(`ARPU: $${arpu.toFixed(2)}`);Track ARPU trends with Stripe Sigma
For continuous monitoring, use Stripe Sigma—your Stripe account's SQL engine. Query your data directly without pagination loops.
Access Stripe Sigma from the Dashboard
Navigate to Developers > Sigma in your Stripe Dashboard. Create a new query to write SQL against your live Stripe data.
Write a SQL query to calculate ARPU
In Sigma, query the invoices and customers tables directly. Sum revenue, count unique customers, and divide to get ARPU by time period.
-- Stripe Sigma SQL: Monthly ARPU
SELECT
DATE_TRUNC('month', invoices.created) AS month,
SUM(invoices.total) / 100 AS total_revenue,
COUNT(DISTINCT invoices.customer) AS customer_count,
ROUND(SUM(invoices.total) / 100.0 / COUNT(DISTINCT invoices.customer), 2) AS arpu
FROM invoices
WHERE invoices.status = 'paid'
AND invoices.created >= DATE '2025-01-01'
GROUP BY DATE_TRUNC('month', invoices.created)
ORDER BY month DESC;Automate ARPU tracking with webhooks
For real-time updates, listen to Stripe webhook events and recalculate ARPU whenever revenue changes.
Listen for invoice payment events
Set up a webhook endpoint to listen for invoice.payment_succeeded and invoice.payment_failed events. Each time an invoice is paid, Stripe pings your endpoint.
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;
const app = express();
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
if (event.type === 'invoice.payment_succeeded') {
const invoice = event.data.object;
console.log(`Invoice ${invoice.id} paid: $${invoice.total / 100}`);
recalculateARPU();
}
res.json({ received: true });
});
function recalculateARPU() {
// Fetch latest invoices and customers, compute new ARPU
// Store result in database or cache
}
app.listen(3000, () => console.log('Webhook server running'));Store ARPU in a database or cache
After each webhook, save your calculated ARPU to a database or Redis cache. This avoids recalculating from scratch and lets you build a historical trend.
Common Pitfalls
- Counting trial customers with no revenue inflates your denominator—filter to customers with at least one paid invoice.
- Using
amount_paidinstead oftotaldouble-counts if a customer has partial payments. - Not paginating API responses means you'll miss invoices and customers beyond the first 100 results.
- Forgetting to convert from cents—all amounts in Stripe's API are in the smallest currency unit (cents for USD).
Wrapping Up
With the API, Sigma, or webhooks, you now have a clear view of revenue per customer. ARPU trends reveal churn, upsell wins, and pricing changes faster than any other metric. If you want to track this automatically across tools and correlate it with behavioral data, Product Analyst can help.