McTaba Labs logo
Software EngineeringThe WhiteboardProjectsEventsMasterclassesWebinarsLearn
Book Call

500+ developers already in the community

Get answers, find accountability partners, and stay ahead

Join Discord →
McTaba Labs logo

Educating 1 million students in tech by 2030.

Programs

  • HighSchool Graduates Launchpad
  • AI & Business Automation
  • Corporate Training
  • McTaba Story & Mission
  • Full-Stack Curriculum
  • Course Pricing & Plans
  • Frequently Asked Questions

Resources

  • Join Discord Community
  • Tech Events in Nairobi
  • Masterclasses
  • Free Resources
  • Learn
  • Campus
  • Africa Tech Talent Report
  • Partner With Us
  • Start a Project
  • Our Work

Contact Us

  • 124 Manyani East Road, Lavington, Nairobi
  • +254727795603
  • Book a Discovery Call

Office Hours:

Mon-Fri: 9am - 2pm EAT

Sat: 10am - 12pm EAT

© 2027 McTaba Labs - A product of Edtech Industries. All rights reserved.

Privacy PolicyTerms of ServiceRefund Policy
  1. Learn
  2. Paystack for African Developers
  3. Metered Billing and Overages on Paystack
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 20, 2026

Metered Billing and Overages on Paystack

Metered billing on Paystack uses a hybrid approach: the Subscriptions API charges the fixed base fee automatically, and charge_authorization handles overage charges at the end of the billing period. Track usage throughout the period, calculate overages when the period ends, and charge the difference. Two charges per period, one managed and one manual.

In this article

  1. 1. The Metered Billing Model
  2. 2. Tracking Usage Against the Quota
  3. 3. Calculating and Charging Overages
  4. 4. Communicating Overages to Customers
  5. 5. Suggesting Plan Upgrades Instead of Overages
  6. 6. Handling Overage Charge Failures

The Metered Billing Model

Metered billing is a hybrid between fixed subscriptions and pure usage-based billing. The customer pays a predictable base fee that includes a quota. Usage beyond the quota incurs overage charges.

Example: A messaging platform charges 3,000 NGN/month for the Pro plan. That includes 5,000 messages. Each message beyond 5,000 costs 1 NGN. A customer who sends 7,200 messages pays 3,000 NGN (base) + 2,200 NGN (overage) = 5,200 NGN.

This model works well because it gives customers a predictable base cost while scaling with heavy usage. It is common in API platforms, communication tools, cloud services, and data processing products.

On Paystack, implement this with two charges per period:

  1. Base fee: Handled by the Subscriptions API. Charges automatically on the billing date.
  2. Overage charge: Calculated at the end of the period and charged via charge_authorization.

This article is part of the subscriptions and recurring billing guide.

Tracking Usage Against the Quota

async function trackAndCheckQuota(userId, eventType, quantity) {
  // Record the usage event
  await db.query(
    'INSERT INTO usage_events (user_id, event_type, quantity, recorded_at) VALUES ($1, $2, $3, NOW())',
    [userId, eventType, quantity]
  );

  // Check current usage against quota
  var billing = await getCurrentBilling(userId);
  var periodStart = billing.current_period_start;

  var totalUsage = await db.query(
    'SELECT COALESCE(SUM(quantity), 0) as total FROM usage_events WHERE user_id = $1 AND event_type = $2 AND recorded_at >= $3',
    [userId, eventType, periodStart]
  );

  var used = parseFloat(totalUsage.rows[0].total);
  var quota = await getIncludedQuota(billing.plan_id, eventType);
  var percentUsed = Math.round((used / quota) * 100);

  // Send alerts at thresholds
  if (percentUsed >= 100 && !await alertSent(userId, eventType, 100)) {
    sendQuotaAlert(userId, eventType, used, quota, 'exceeded');
    markAlertSent(userId, eventType, 100);
  } else if (percentUsed >= 80 && !await alertSent(userId, eventType, 80)) {
    sendQuotaAlert(userId, eventType, used, quota, 'approaching');
    markAlertSent(userId, eventType, 80);
  }

  return { used: used, quota: quota, percentUsed: percentUsed };
}

Get weekly developer tips

Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.

No spam. Unsubscribe anytime.

Calculating and Charging Overages

async function processOverageCharges() {
  // Find accounts whose billing period just ended
  var accounts = await db.query(
    'SELECT ba.id, ba.user_id, ba.plan_id, ba.payment_method_id, ba.current_period_start, ba.current_period_end, u.email FROM billing_accounts ba JOIN users u ON ba.user_id = u.id WHERE ba.status = $1 AND ba.current_period_end <= NOW() AND ba.billing_type = $2',
    ['active', 'metered']
  );

  for (var i = 0; i < accounts.rows.length; i++) {
    var account = accounts.rows[i];

    // Get all usage types with quotas for this plan
    var pricingRules = await db.query(
      'SELECT event_type, included_units, price_per_unit FROM usage_pricing WHERE plan_id = $1',
      [account.plan_id]
    );

    var totalOverage = 0;
    var overageItems = [];

    for (var j = 0; j < pricingRules.rows.length; j++) {
      var rule = pricingRules.rows[j];

      var usage = await db.query(
        'SELECT COALESCE(SUM(quantity), 0) as total FROM usage_events WHERE user_id = $1 AND event_type = $2 AND recorded_at >= $3 AND recorded_at < $4',
        [account.user_id, rule.event_type, account.current_period_start, account.current_period_end]
      );

      var usedUnits = parseFloat(usage.rows[0].total);
      var overageUnits = Math.max(0, usedUnits - rule.included_units);

      if (overageUnits > 0) {
        var overageAmount = Math.round(overageUnits * rule.price_per_unit);
        totalOverage = totalOverage + overageAmount;
        overageItems.push({
          eventType: rule.event_type,
          used: usedUnits,
          included: rule.included_units,
          overage: overageUnits,
          amount: overageAmount,
        });
      }
    }

    if (totalOverage === 0) {
      console.log('No overages for user ' + account.user_id);
      continue;
    }

    // Charge the overage
    var authCode = await getAuthorizationCode(account.payment_method_id);
    var reference = 'OVERAGE_' + account.user_id + '_' + account.current_period_start.toISOString().slice(0, 7);

    var result = await chargeAuthorization(account.email, totalOverage, authCode, reference);

    if (result.status === 'success') {
      await createOverageInvoice(account.user_id, overageItems, totalOverage, reference);
      console.log('Overage charged: ' + totalOverage + ' for user ' + account.user_id);
    } else {
      await handleOverageFailure(account.user_id, totalOverage, result.gateway_response);
    }
  }
}

Run this after the subscription renewal has processed. The base fee is charged by Paystack automatically. The overage charge runs in your billing job, typically within hours of the period ending.

Communicating Overages to Customers

Overage charges surprise customers. Reduce friction with clear communication.

Before the charge: Send a usage summary email when the billing period ends and before the overage charge hits. "Your usage this month: 7,200 messages. Your included quota: 5,000 messages. Overage: 2,200 messages at 1 NGN each = 2,200 NGN. This will be charged to your Visa ending in 4081."

After the charge: Send a detailed receipt with line items showing the overage breakdown.

During the period: Show real-time usage in the customer dashboard. Send alerts when they hit 80% and 100% of their quota.

// Usage dashboard API
router.get('/api/billing/metered-usage', async function(req, res) {
  var userId = req.user.id;
  var billing = await getCurrentBilling(userId);

  var pricingRules = await db.query(
    'SELECT event_type, included_units, price_per_unit FROM usage_pricing WHERE plan_id = $1',
    [billing.plan_id]
  );

  var usageSummary = [];

  for (var i = 0; i < pricingRules.rows.length; i++) {
    var rule = pricingRules.rows[i];
    var usage = await db.query(
      'SELECT COALESCE(SUM(quantity), 0) as total FROM usage_events WHERE user_id = $1 AND event_type = $2 AND recorded_at >= $3',
      [userId, rule.event_type, billing.current_period_start]
    );

    var used = parseFloat(usage.rows[0].total);
    var overage = Math.max(0, used - rule.included_units);

    usageSummary.push({
      type: rule.event_type,
      used: used,
      included: rule.included_units,
      overage: overage,
      overageCost: Math.round(overage * rule.price_per_unit),
      percentUsed: Math.round((used / rule.included_units) * 100),
    });
  }

  res.json({
    usage: usageSummary,
    periodStart: billing.current_period_start,
    periodEnd: billing.current_period_end,
  });
});

Suggesting Plan Upgrades Instead of Overages

If a customer consistently exceeds their quota, an upgrade to a higher plan with a larger quota might be cheaper than paying overages. Calculate and suggest this proactively.

async function checkUpgradeSuggestion(userId) {
  var billing = await getCurrentBilling(userId);
  var currentPlan = await getPlan(billing.plan_id);
  var nextPlan = await getNextHigherPlan(currentPlan.tier);

  if (!nextPlan) return null; // Already on the highest plan

  // Check last 3 months of overages
  var recentOverages = await db.query(
    'SELECT SUM(amount) as total FROM invoices WHERE billing_account_id = $1 AND invoice_type = $2 AND created_at > NOW() - INTERVAL '3 months'',
    [billing.id, 'overage']
  );

  var avgMonthlyOverage = Math.round(parseFloat(recentOverages.rows[0].total || 0) / 3);
  var upgradeCostDifference = nextPlan.base_amount - currentPlan.base_amount;

  if (avgMonthlyOverage > upgradeCostDifference) {
    return {
      suggest: true,
      currentPlan: currentPlan.name,
      suggestedPlan: nextPlan.name,
      currentCost: currentPlan.base_amount + avgMonthlyOverage,
      upgradedCost: nextPlan.base_amount,
      savings: (currentPlan.base_amount + avgMonthlyOverage) - nextPlan.base_amount,
      message: 'Based on your usage, upgrading to ' + nextPlan.name + ' would save you about ' + formatAmount((currentPlan.base_amount + avgMonthlyOverage) - nextPlan.base_amount) + ' per month.',
    };
  }

  return null;
}

Display upgrade suggestions in the billing dashboard and in overage notification emails. This is a win-win: the customer saves money and you get more predictable revenue.

Handling Overage Charge Failures

Overage charges can fail just like any recurring charge. The base subscription might succeed (Paystack handles that), but the overage charge (your charge_authorization call) might fail.

Options for handling this:

  • Add to next month's bill. Carry the overage amount forward and include it in the next billing period's overage calculation.
  • Separate dunning for overages. Start a dunning sequence specifically for the overage charge, independent of the subscription status.
  • Restrict overage-generating features. If the overage charge fails, cap the customer at their included quota for the next period until the overage is paid.

Choose the approach that matches your business. For low-value overages, carrying forward is simplest. For significant amounts, dedicated dunning is worth the engineering effort.

For the complete dunning implementation, see handling failed recurring charges.

Key Takeaways

  • ✓Metered billing charges a base fee (fixed) plus overage charges (variable). Paystack handles the base fee via Subscriptions API. You handle overages via charge_authorization.
  • ✓Track usage in your database. At the end of the billing period, compare usage to the included quota. Charge for any excess.
  • ✓The overage charge happens separately from the subscription renewal. Customers may see two charges in one billing period.
  • ✓Communicate the metered model clearly. Customers should understand their included quota, the overage rate, and how to monitor their usage.
  • ✓Set up usage alerts at 80% and 100% of the included quota. Nobody wants a surprise overage charge.
  • ✓Consider offering a plan upgrade as an alternative to overages. If a customer consistently exceeds their quota, upgrading to a higher plan may be cheaper.

Frequently Asked Questions

Will customers see two charges on their card statement?
Yes. The base subscription charge (via Subscriptions API) and the overage charge (via charge_authorization) are separate transactions. Communicate this clearly in your billing documentation so customers expect two charges.
What if the base subscription charge succeeds but the overage charge fails?
The subscription remains active (base fee was paid). The overage amount becomes outstanding. You can carry it forward to the next period, start a separate dunning sequence, or restrict overage-generating features until the overage is paid.
Should I charge overages at the end of the period or in real-time?
End-of-period is simpler and more common. Real-time overage charging (charging as the customer exceeds the quota) is complex and can result in many small charges. Most products bill overages once at the end of the billing period.
How do I handle tiered overage pricing?
Tiered pricing means the overage rate changes at different usage levels (first 1,000 extra at 1 NGN each, next 5,000 at 0.80 NGN each). Store tier boundaries in your usage_pricing table and calculate the charge by applying each tier's rate to the appropriate quantity range.

Learn Paystack Integration

KES 2,999

The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.

Start Paystack Course

Not ready? Create Free Account

Keep reading

Paystack Subscriptions and Recurring Billing: Complete Guide

Everything you need to build recurring billing on Paystack. Covers the subscriptions API, plans, authorization codes for manual charges, dunning, lifecycle webhooks, cancellation, and real-world patterns for SaaS, memberships, school fees, and loan collection across Africa.

Read more

Usage-Based Billing with Paystack

Usage-based billing charges customers for what they consume: API calls, messages sent, storage used, transactions processed. Paystack does not have a native usage billing feature, so you build it on authorization codes. This guide covers usage tracking, billing period calculation, the charge flow, and handling the edge cases that arise when amounts vary every cycle.

Read more

Building Your Own Billing Engine on Paystack Authorizations

When the Subscriptions API is too rigid for your billing model, you build your own engine on top of Paystack authorization codes. This guide walks through the database schema, charge scheduler, billing state machine, retry queue, invoice generation, and reconciliation process you need to run reliable recurring billing.

Read more

Charging a Saved Card with Paystack Authorization Codes

A complete walkthrough of the Paystack charge_authorization endpoint. Learn how to charge a customer's saved card without them re-entering card details, handle success and failure responses, build idempotent charges, and avoid the common mistakes that cause double charges or missed payments.

Read more

Building a SaaS Billing Page on Paystack

Your SaaS billing page is where customers choose plans, manage subscriptions, view invoices, and update payment methods. This guide covers the complete implementation: Paystack plan setup, the pricing page, the checkout flow, the account billing dashboard, upgrade/downgrade logic, and the APIs that connect it all.

Read more

Explore related topics

M-Pesa & African Stack

M-Pesa Daraja API tutorials, USSD development, and WhatsApp Business API guides. The African Stack that global bootcamps do not teach.

Learn to Code in Kenya

Everything a Kenyan needs to know before writing their first line of code. Career paths, real costs, hardware requirements, and honest answers about what it takes.

Learning to Code

Roadmaps, language guides, and practical advice for learning to code and launching a programming career.

Breaking Into Tech

Practical, no-nonsense guides for anyone looking to start or change their career in tech. From choosing your first language to acing interviews.

The African Stack: Skills for This Market

The skills that make Kenyan developers hard to replace: M-Pesa, USSD, WhatsApp, fintech, and building for the African market.

Bonaventure Ogeto

Bonaventure Ogeto

Founder, McTaba Labs

Bonaventure is the founder of McTaba Labs, a coding bootcamp in Nairobi, Kenya. He has spent years building products for the African market and mentoring the next generation of developers across the continent.