Transfer Balance Checks Before Initiating Payouts
Call GET /balance to check your available Paystack balance before initiating transfers. The response returns an array of balances per currency. Compare the available balance against your transfer amount plus estimated fees. For batch payouts, sum all transfers and check before sending the batch to avoid a full-batch rejection.
The Balance API Endpoint
Paystack exposes your balance through a simple GET endpoint. The response contains an array of balance objects, one per currency your account operates in.
const getBalance = async () => {
const response = await fetch('https://api.paystack.co/balance', {
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
},
});
const data = await response.json();
return data.data;
};
// Example response
// [
// { currency: "NGN", balance: 5000000 },
// { currency: "KES", balance: 250000 }
// ]
// Amounts are in the smallest unit (kobo, cents)
The balance value is in the smallest currency unit. So 5000000 in NGN is 50,000 Naira, and 250000 in KES is 2,500 Kenyan Shillings. Apply the same conversion you use for transfer amounts.
This endpoint is lightweight and fast. Call it freely before transfers. There is no cost to checking your balance, and skipping the check costs you a failed transfer and a support ticket.
Available Balance vs Total Balance
This is the distinction that trips up most developers when they first work with Paystack transfers. Your total balance includes all money in your Paystack account, but your available balance only includes the portion that has completed settlement and is ready to transfer.
When a customer pays you, the money enters your Paystack account, but it is not immediately available. Paystack settles funds to your available balance on a schedule that depends on your account configuration and market. Until settlement completes, those funds are "pending" and cannot be used for transfers.
If your dashboard shows a total balance of 500,000 NGN but your available balance is only 200,000 NGN, you can only transfer up to 200,000 NGN (minus fees). The remaining 300,000 NGN is still being settled from recent customer payments.
async function getAvailableBalance(currency) {
var balances = await getBalance();
for (var i = 0; i < balances.length; i++) {
if (balances[i].currency === currency) {
return {
total: balances[i].balance,
currency: currency,
// Convert for display
displayAmount: balances[i].balance / 100,
};
}
}
return { total: 0, currency: currency, displayAmount: 0 };
}
Plan your payout schedules around settlement timing. If your settlement cycle is T+1 (next business day), payments received on Friday might not be available until Monday. Schedule payouts for Tuesday to ensure Friday's payments have settled.
Pre-Flight Check for Single Transfers
async function canAffordTransfer(amountMinorUnit, currency) {
var balance = await getAvailableBalance(currency);
// Estimate the fee (this is a rough estimate)
// Check Paystack pricing for actual fee structure
var estimatedFee = Math.max(1000, Math.round(amountMinorUnit * 0.01));
var totalNeeded = amountMinorUnit + estimatedFee;
return {
canAfford: balance.total >= totalNeeded,
available: balance.total,
transferAmount: amountMinorUnit,
estimatedFee: estimatedFee,
totalNeeded: totalNeeded,
shortfall: Math.max(0, totalNeeded - balance.total),
};
}
// Usage
var check = await canAffordTransfer(500000, 'NGN'); // 5,000 NGN
if (!check.canAfford) {
console.error(
'Insufficient balance. Need: ' + (check.totalNeeded / 100)
+ ' NGN, Available: ' + (check.available / 100) + ' NGN'
);
// Alert finance team or queue for later
}
The fee estimate in the code above is a placeholder. Paystack's actual transfer fees depend on the amount, destination type, and your specific account terms. Check the Paystack pricing page or your dashboard for the current fee structure, and update your estimation logic accordingly.
Pre-Flight Check for Batch Payouts
Batch payouts are where balance checks matter most. If the batch total exceeds your balance, Paystack rejects the entire batch. You do not get a partial execution where some transfers go through.
async function validateBatchPayout(payouts, currency) {
// Sum all payout amounts
var totalAmount = 0;
for (var i = 0; i < payouts.length; i++) {
totalAmount += payouts[i].amountMinorUnit;
}
// Estimate fees per transfer
var totalEstimatedFees = 0;
for (var j = 0; j < payouts.length; j++) {
totalEstimatedFees += Math.max(1000, Math.round(payouts[j].amountMinorUnit * 0.01));
}
var totalNeeded = totalAmount + totalEstimatedFees;
var balance = await getAvailableBalance(currency);
var result = {
payoutCount: payouts.length,
totalPayoutAmount: totalAmount,
estimatedFees: totalEstimatedFees,
totalNeeded: totalNeeded,
available: balance.total,
canAfford: balance.total >= totalNeeded,
shortfall: Math.max(0, totalNeeded - balance.total),
};
if (!result.canAfford) {
// Calculate how many payouts we can afford
var sorted = payouts.slice().sort(function(a, b) {
return a.amountMinorUnit - b.amountMinorUnit;
});
var runningTotal = 0;
var affordableCount = 0;
for (var k = 0; k < sorted.length; k++) {
var withFee = sorted[k].amountMinorUnit
+ Math.max(1000, Math.round(sorted[k].amountMinorUnit * 0.01));
if (runningTotal + withFee <= balance.total) {
runningTotal += withFee;
affordableCount++;
} else {
break;
}
}
result.affordablePayouts = affordableCount;
result.suggestion = 'Can afford ' + affordableCount + ' of '
+ payouts.length + ' payouts. Fund balance with at least '
+ (result.shortfall / 100) + ' ' + currency + ' to cover all.';
}
return result;
}
This function not only tells you whether you can afford the batch, but also suggests how many payouts you could process with the current balance if you prioritize by amount (smallest first). This is useful when your finance team needs to decide: fund the balance and wait, or process a partial batch now.
Setting Up Low-Balance Alerts
// Run this as a cron job, e.g., every hour or 4 hours before scheduled payouts
async function checkBalanceAlerts() {
var currencies = ['NGN', 'KES', 'GHS'];
var thresholds = {
NGN: 10000000, // Alert if below 100,000 NGN
KES: 500000, // Alert if below 5,000 KES
GHS: 200000, // Alert if below 2,000 GHS
};
for (var i = 0; i < currencies.length; i++) {
var currency = currencies[i];
var balance = await getAvailableBalance(currency);
var threshold = thresholds[currency] || 0;
if (balance.total < threshold) {
await sendAlert({
type: 'low_balance',
currency: currency,
currentBalance: balance.total / 100,
threshold: threshold / 100,
message: currency + ' balance is ' + (balance.total / 100)
+ '. Threshold is ' + (threshold / 100)
+ '. Fund the account before the next payout run.',
});
}
}
}
// Scheduled payout pre-check
async function prePayoutBalanceCheck(scheduledPayouts) {
var byCurrency = {};
// Group payouts by currency
for (var i = 0; i < scheduledPayouts.length; i++) {
var p = scheduledPayouts[i];
if (!byCurrency[p.currency]) {
byCurrency[p.currency] = [];
}
byCurrency[p.currency].push(p);
}
var issues = [];
for (var currency in byCurrency) {
var validation = await validateBatchPayout(byCurrency[currency], currency);
if (!validation.canAfford) {
issues.push({
currency: currency,
shortfall: validation.shortfall / 100,
payoutCount: validation.payoutCount,
suggestion: validation.suggestion,
});
}
}
if (issues.length > 0) {
await sendAlert({
type: 'payout_funding_needed',
issues: issues,
message: 'Upcoming payout run has funding gaps in '
+ issues.length + ' currencies.',
});
}
return issues;
}
Run the pre-payout check 4-6 hours before your scheduled payout window. This gives your finance team time to fund the balance through a bank transfer or by adjusting the payout queue. Surprises at payout time lead to stressed operations teams and delayed vendor payments.
Multi-Currency Considerations
If you operate in multiple Paystack markets (Nigeria, Kenya, Ghana), you maintain separate balances per currency. NGN payments go to your NGN balance, KES payments to your KES balance. You cannot transfer NGN from a KES balance or vice versa.
This means your balance check must match the currency of the transfer. A common bug: checking the NGN balance before initiating a KES transfer. The check passes (your NGN balance is healthy), but the transfer fails because your KES balance is empty.
async function validateTransferCurrency(amount, currency, recipientType) {
var balance = await getAvailableBalance(currency);
if (balance.total === 0) {
return {
valid: false,
error: 'No ' + currency + ' balance available. '
+ 'Ensure you have received payments in ' + currency
+ ' or funded your ' + currency + ' balance.',
};
}
if (balance.total < amount) {
return {
valid: false,
error: currency + ' balance (' + (balance.total / 100)
+ ') is insufficient for transfer of ' + (amount / 100) + '.',
};
}
return { valid: true };
}
For the broader guide on how transfers work across currencies and countries, see the transfers and payouts complete guide.
Key Takeaways
- ✓The GET /balance endpoint returns your balance per currency. Each entry shows the total balance and the available (transferable) balance.
- ✓Available balance is not the same as total balance. Funds pending settlement from recent payments are not yet available for transfer.
- ✓Always check your balance before initiating transfers, especially before bulk payouts where an insufficient balance rejects the entire batch.
- ✓Factor in transfer fees when comparing balance against payout amounts. The fee is deducted on top of the transfer amount.
- ✓Build automated low-balance alerts that notify your finance team before a scheduled payout run when the balance might fall short.
- ✓For multi-currency operations, check the balance for each currency separately. You cannot transfer NGN from a KES balance.
Frequently Asked Questions
- Does checking the balance count against my rate limit?
- The balance endpoint is subject to the same rate limits as other Paystack API calls. However, it is a lightweight GET request, and checking it before each payout or a few times per hour is well within normal usage. You would need to poll it aggressively (every second) to run into rate limit issues.
- Can I fund my Paystack balance programmatically?
- Paystack does not provide an API for depositing money into your Paystack balance. You fund your balance either by receiving customer payments (which settle into your balance) or by making a bank transfer to the dedicated balance funding account shown in your Paystack dashboard. See the funding guide for details.
- Why does my available balance not match my total balance?
- The difference is pending settlements. When customers pay you, the money enters your total balance immediately but is not available for transfers until Paystack completes settlement. Settlement timing depends on your account configuration and market. The gap between total and available represents payments that are still being processed.
Ready to build real-world apps?
Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.
Apply to the McTaba Marathon