The "Amount too low" error means your transaction amount is below Paystack's minimum for that currency. For NGN the minimum is typically 10000 kobo (100 NGN). For GHS it is 100 pesewas (1 GHS). For ZAR and KES it is 100 cents (1 ZAR/KES). For USD it is 100 cents (1 USD). Remember that Paystack expects amounts in the smallest currency unit, not in the major unit.
When the amount you send to /transaction/initialize is below Paystack's minimum for that currency, you get:
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"status": false,
"message": "Amount cannot be less than 10000"
}
The number in the message (10000) is in the smallest currency unit. For NGN, 10000 kobo is 100 Naira. The exact wording and threshold may vary depending on the currency and account type, but the pattern is the same: a 400 status with a message telling you the amount is below the floor.
Here are the typical minimum transaction amounts Paystack enforces. These values are current as of July 2026 but can change. Always verify against the Paystack documentation for the latest figures.
| Currency | Minor unit name | Minimum (minor unit) | Minimum (major unit) |
|---|---|---|---|
| NGN | kobo | 10000 | 100 NGN |
| GHS | pesewas | 100 | 1 GHS |
| ZAR | cents | 100 | 1 ZAR |
| KES | cents | 100 | 1 KES |
| USD | cents | 100 | 1 USD |
Some account types or business categories may have different minimums. If you are running into a minimum that does not match the table above, check your Paystack dashboard or contact Paystack support for your specific account configuration.
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
This is the number one cause of the "amount too low" error. Paystack expects all amounts in the smallest currency unit. For Nigerian Naira, that is kobo. 1 NGN = 100 kobo. When a developer sends amount: 500 thinking they are charging 500 NGN, they are actually charging 500 kobo, which is 5 NGN.
// WRONG: sending Naira, not kobo
body: JSON.stringify({
email: 'customer@example.com',
amount: 50, // 50 kobo = 0.50 NGN - below minimum
currency: 'NGN',
})
// RIGHT: convert to kobo
const amountInNaira = 500;
body: JSON.stringify({
email: 'customer@example.com',
amount: amountInNaira * 100, // 50000 kobo = 500 NGN
currency: 'NGN',
})
The same rule applies to every currency Paystack supports:
If you are storing prices in the major unit in your database (which is common), the conversion to the minor unit should happen at the point where you build the Paystack API request, not earlier. This keeps your business logic clean and isolates the Paystack-specific conversion to one place.
For a deeper treatment of amount conversion pitfalls including float precision issues, see Paystack Invalid Amount Error and Kobo Confusion.
Discounts can push an otherwise valid amount below the minimum. If your store sells items for 200 NGN and a customer applies a 95% discount coupon, the final price is 10 NGN. That is 1000 kobo, which is below the 10000 kobo minimum for NGN.
Handle this in your pricing logic, not at the Paystack call:
const MINIMUM_AMOUNTS: Record = {
NGN: 10000, // 100 NGN in kobo
GHS: 100, // 1 GHS in pesewas
ZAR: 100, // 1 ZAR in cents
KES: 100, // 1 KES in cents
USD: 100, // 1 USD in cents
};
function calculateFinalAmount(
priceInMajorUnit: number,
discountPercent: number,
currency: string
): number {
const discounted = priceInMajorUnit * (1 - discountPercent / 100);
const amountInMinorUnit = Math.round(discounted * 100);
const minimum = MINIMUM_AMOUNTS[currency] || 100;
if (amountInMinorUnit < minimum) {
throw new Error(
`Final amount ${discounted} ${currency} is below the minimum of ${minimum / 100} ${currency}. ` +
`Adjust the discount or set a minimum order value.`
);
}
return amountInMinorUnit;
}
Some businesses solve this by setting a minimum order value that is higher than the payment gateway minimum. If your minimum order is 500 NGN, no discount can push the amount below the Paystack floor.
Validate the amount on your server before calling Paystack. This gives you a chance to return a clear error message to your user instead of a cryptic API error.
const MINIMUM_AMOUNTS: Record = {
NGN: 10000,
GHS: 100,
ZAR: 100,
KES: 100,
USD: 100,
};
const CURRENCY_NAMES: Record = {
NGN: { major: 'Naira', minor: 'kobo' },
GHS: { major: 'Cedis', minor: 'pesewas' },
ZAR: { major: 'Rand', minor: 'cents' },
KES: { major: 'Shillings', minor: 'cents' },
USD: { major: 'Dollars', minor: 'cents' },
};
function validatePaystackAmount(amountInMinorUnit: number, currency: string): void {
if (!Number.isInteger(amountInMinorUnit)) {
throw new Error(
`Amount must be an integer in ${CURRENCY_NAMES[currency]?.minor || 'minor units'}. ` +
`Received: ${amountInMinorUnit}`
);
}
if (amountInMinorUnit <= 0) {
throw new Error('Amount must be a positive number.');
}
const minimum = MINIMUM_AMOUNTS[currency];
if (minimum && amountInMinorUnit < minimum) {
const majorAmount = minimum / 100;
throw new Error(
`Minimum transaction amount for ${currency} is ${majorAmount} ${CURRENCY_NAMES[currency]?.major || currency}.`
);
}
}
// Usage in your API route
app.post('/api/initialize-payment', async (req, res) => {
const { email, amountInNaira } = req.body;
const amountInKobo = Math.round(amountInNaira * 100);
const currency = 'NGN';
try {
validatePaystackAmount(amountInKobo, currency);
} catch (error) {
return res.status(400).json({ error: error.message });
}
// Proceed with Paystack API call
const response = await fetch('https://api.paystack.co/transaction/initialize', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, amount: amountInKobo, currency }),
});
const data = await response.json();
res.json(data);
});
This validation function catches three problems: non-integer amounts (which Paystack also rejects), negative or zero amounts, and amounts below the currency-specific minimum. Your user sees a clear message like "Minimum transaction amount for NGN is 100 Naira" instead of "Amount cannot be less than 10000".
Add client-side validation too. It does not replace server-side validation (never trust the client), but it gives the user instant feedback before the round trip to your server.
// React example
function PaymentForm({ currency = 'NGN' }: { currency?: string }) {
const [amount, setAmount] = useState('');
const [error, setError] = useState('');
const minimums: Record = {
NGN: 100,
GHS: 1,
ZAR: 1,
KES: 1,
USD: 1,
};
function handleAmountChange(value: string) {
setAmount(value);
const numericValue = parseFloat(value);
if (isNaN(numericValue) || numericValue <= 0) {
setError('Please enter a valid amount.');
return;
}
const minimum = minimums[currency] || 1;
if (numericValue < minimum) {
setError(`Minimum amount is ${minimum} ${currency}.`);
return;
}
setError('');
}
return (
<div>
<label>Amount ({currency})</label>
<input
type="number"
value={amount}
onChange={e => handleAmountChange(e.target.value)}
min={minimums[currency] || 1}
step="0.01"
/>
{error && <p className="text-red-500 text-sm">{error}</p>}
</div>
);
}
Notice that the frontend works in the major unit (Naira, Cedis) because that is what humans think in. The conversion to the minor unit (kobo, pesewas) happens on the server.
Write tests that specifically target the minimum amount boundary. Use Paystack's test mode to confirm the behavior.
// Jest / Vitest test examples
describe('Paystack amount validation', () => {
test('rejects amount below NGN minimum', () => {
expect(() => validatePaystackAmount(9999, 'NGN')).toThrow(
'Minimum transaction amount for NGN is 100 Naira'
);
});
test('accepts amount at NGN minimum', () => {
expect(() => validatePaystackAmount(10000, 'NGN')).not.toThrow();
});
test('rejects non-integer amounts', () => {
expect(() => validatePaystackAmount(100.5, 'NGN')).toThrow(
'Amount must be an integer'
);
});
test('rejects zero amount', () => {
expect(() => validatePaystackAmount(0, 'NGN')).toThrow(
'Amount must be a positive number'
);
});
test('rejects negative amount', () => {
expect(() => validatePaystackAmount(-500, 'NGN')).toThrow(
'Amount must be a positive number'
);
});
test('handles GHS minimum correctly', () => {
expect(() => validatePaystackAmount(99, 'GHS')).toThrow();
expect(() => validatePaystackAmount(100, 'GHS')).not.toThrow();
});
});
Also test your discount flow end-to-end. Apply a discount that should push the amount below the minimum and verify your system either blocks the discount or enforces a floor price.
Confirm the fix is working:
authorization_url.For the related error where the amount format itself is wrong (decimal instead of integer, string instead of number), see Paystack Invalid Amount Error and Kobo Confusion. For the full troubleshooting reference, see Paystack Errors and Troubleshooting: The Complete Reference.
If you want hands-on practice building payment flows that handle all these edge cases, the McTaba Full-Stack Software and AI Engineering self-paced course walks you through a production Paystack integration from first API call to deployment.
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