The insufficient balance error means your available Paystack balance is lower than the transfer amount. Your total balance might look large enough, but Paystack holds unsettled funds (payments from the last 1 to 3 business days) until they clear. Only the available balance can be used for transfers. Check your available balance via GET /balance before initiating transfers. If you need funds immediately, you can fund your Paystack balance via bank transfer from your company account.
When you call the Paystack Transfer API and your available balance is too low, you get this response:
{
"status": false,
"message": "Your balance is not enough to fulfil this request"
}
The HTTP status code is 400. The transfer is not created at all. Unlike the OTP required response, this is a hard rejection. Nothing is queued, nothing is pending. You cannot proceed until you have enough available balance.
This error can also appear when calling the bulk transfer endpoint with a total that exceeds your available balance. Paystack checks the sum of all transfers in the batch against your available balance before processing any of them.
Paystack tracks two separate balance figures for your account:
The gap between total and available balance is your pending settlement. These are payments that customers have made, but the funds have not yet moved from the customer's bank to your Paystack balance.
Settlement timing depends on your account configuration:
A common scenario: you collect KES 500,000 in payments on Friday. On Saturday morning, your total balance shows KES 500,000 but your available balance is KES 0 because nothing has settled over the weekend. Your transfer of KES 100,000 fails with insufficient balance even though the money is "there."
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
Always check your available balance before initiating a transfer. This lets you handle low-balance situations before hitting the error.
async function checkBalance() {
const response = await fetch('https://api.paystack.co/balance', {
headers: {
Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
},
});
const data = await response.json();
if (!data.status) {
throw new Error('Failed to fetch balance');
}
// Paystack returns balances per currency
// Each entry has 'currency' and 'balance' (available balance in kobo/pesewas)
return data.data.map(entry => ({
currency: entry.currency,
available: entry.balance / 100, // Convert from kobo/pesewas to main unit
total: (entry.balance + (entry.pending || 0)) / 100,
pending: (entry.pending || 0) / 100,
}));
}
// Example output:
// [{ currency: 'NGN', available: 450000, total: 950000, pending: 500000 }]
Now build the balance check into your transfer flow:
async function safeTransfer(recipientCode, amount, currency, reference, reason) {
// Step 1: Check balance
const balances = await checkBalance();
const currencyBalance = balances.find(b => b.currency === currency);
if (!currencyBalance) {
return {
success: false,
reason: 'no_balance_for_currency',
message: `No ${currency} balance found on your Paystack account.`,
};
}
if (currencyBalance.available < amount) {
return {
success: false,
reason: 'insufficient_balance',
available: currencyBalance.available,
required: amount,
pending: currencyBalance.pending,
message: `Available balance is ${currency} ${currencyBalance.available.toLocaleString()}. Transfer requires ${currency} ${amount.toLocaleString()}. Pending settlement: ${currency} ${currencyBalance.pending.toLocaleString()}.`,
};
}
// Step 2: Initiate transfer
const response = await fetch('https://api.paystack.co/transfer', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
source: 'balance',
amount: amount * 100,
recipient: recipientCode,
reference,
reason,
currency,
}),
});
const data = await response.json();
if (data.status) {
return { success: true, data: data.data };
}
return { success: false, message: data.message };
}
The balance check is not a lock. Between checking and transferring, another process might initiate a transfer that reduces your balance. For high-volume systems, add error handling that catches the insufficient balance error even after the check passes.
If your available balance is too low but you know funds will settle soon, queue the transfer and retry after settlement.
// Simplified transfer queue logic
async function queueOrTransfer(transfer) {
const { recipientCode, amount, currency, reference, reason } = transfer;
const result = await safeTransfer(recipientCode, amount, currency, reference, reason);
if (result.success) {
await saveTransferResult(reference, 'completed', result.data);
return result;
}
if (result.reason === 'insufficient_balance') {
// Queue for retry after settlement
await saveToQueue({
...transfer,
queuedAt: new Date().toISOString(),
retryAfter: getNextSettlementTime(),
attempts: 0,
});
console.log(
`Transfer ${reference} queued. Available: ${result.available}, Required: ${amount}. Pending: ${result.pending}`
);
return {
success: false,
queued: true,
message: 'Transfer queued. Will retry after next settlement.',
};
}
return result;
}
function getNextSettlementTime() {
const now = new Date();
const hour = now.getHours();
const day = now.getDay();
// Paystack typically settles in the morning on business days
// If it is before 10 AM on a weekday, settlement might happen today
if (day >= 1 && day <= 5 && hour < 10) {
const settlement = new Date(now);
settlement.setHours(10, 0, 0, 0);
return settlement.toISOString();
}
// Otherwise, next business day at 10 AM
const next = new Date(now);
if (day === 5) next.setDate(next.getDate() + 3); // Friday -> Monday
else if (day === 6) next.setDate(next.getDate() + 2); // Saturday -> Monday
else next.setDate(next.getDate() + 1);
next.setHours(10, 0, 0, 0);
return next.toISOString();
}
Run a cron job that processes the queue after expected settlement times. Check the balance before each retry. If the balance is still too low after multiple retries, alert your team.
If you need to make transfers but your collected payments have not settled, you can fund your Paystack balance directly from your bank account.
To do this:
This is useful in several scenarios:
Keep in mind that funding your Paystack balance is a bank transfer. It is subject to your bank's transfer limits and processing times. Plan ahead rather than trying to fund your balance 30 minutes before payroll runs.
When you use the bulk transfer endpoint (POST /transfer/bulk), Paystack checks whether your available balance covers the total sum of all transfers in the batch. If any single transfer would push you over, the entire batch is rejected.
async function safeBulkTransfer(transfers, currency) {
// Calculate total needed
const totalNeeded = transfers.reduce((sum, t) => sum + t.amount, 0);
// Check balance
const balances = await checkBalance();
const currencyBalance = balances.find(b => b.currency === currency);
if (!currencyBalance || currencyBalance.available < totalNeeded) {
const available = currencyBalance?.available || 0;
const shortfall = totalNeeded - available;
return {
success: false,
reason: 'insufficient_balance',
totalNeeded,
available,
shortfall,
message: `Bulk transfer needs ${currency} ${totalNeeded.toLocaleString()} but only ${currency} ${available.toLocaleString()} is available. Shortfall: ${currency} ${shortfall.toLocaleString()}`,
};
}
// Proceed with bulk transfer
const response = await fetch('https://api.paystack.co/transfer/bulk', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
source: 'balance',
currency,
transfers: transfers.map(t => ({
amount: t.amount * 100,
recipient: t.recipientCode,
reference: t.reference,
reason: t.reason,
})),
}),
});
const data = await response.json();
return { success: data.status, data: data.data, message: data.message };
}
For payroll or batch vendor payments, run the balance check early in the process. If the balance is short, you have time to fund it or split the batch into smaller groups that fit within the available balance.
The best way to handle insufficient balance errors is to prevent them from happening in the first place.
For more on Paystack transfers and troubleshooting:
Building reliable payout systems requires understanding settlement flows and balance management. The McTaba Full-Stack Software and AI Engineering course covers building production payment systems end to end, including automated payouts.
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