Performance
Optimize your Wajub API calls with pagination, caching, and concurrent requests.
A performant integration improves user experience and reduces your rate limit consumption. Apply these principles for efficient API calls.
Pagination
All list endpoints (GET /payments, GET /transfers, etc.) are paginated.
https://api.wajub.com/payments| Parameter | Type | Default value | Maximum |
|---|---|---|---|
page | integer | 1 | — |
limit | integer | 15 | 100 |
from | ISO 8601 | — | — |
to | ISO 8601 | — | — |
Use cursors rather than pages
For large exports, prefer from/to filters and iterate
by date range rather than page by page. This is more predictable and avoids duplicates if
data is inserted between two calls.
Efficient pagination by date range
async function fetchAllPayments(from, to) {
const results = [];
let page = 1;
const limit = 100;
while (true) {
const { data, meta } = await wajub.payments.list({
from: from.toISOString(),
to: to.toISOString(),
page,
limit,
});
results.push(...data);
if (meta.currentPage >= meta.lastPage) break;
page++;
}
return results;
}Caching static resources
Some data changes rarely and can be cached:
| Resource | Recommended TTL | Justification |
|---|---|---|
Exchange rates (GET /rates) | 1 hour | Updated periodically |
| List of available operators | 24 hours | Rarely changes |
| Account configuration | 1 hour | Manually modified |
| Payment status | Do not cache | Source of truth, can change at any time |
const cache = new Map();
async function withCache(key, ttlMs, fn) {
const entry = cache.get(key);
if (entry && Date.now() - entry.ts < ttlMs) {
return entry.data;
}
const data = await fn();
cache.set(key, { data, ts: Date.now() });
return data;
}
// Usage
const rates = await withCache('rates', 3600_000, () => wajub.rates.list());Never cache transaction statuses
A payment or transfer status can change at any time. Always query the API or rely on webhooks for transactional data.
Concurrent requests
When you need to verify multiple transactions or initiate several independent operations, parallelize the calls:
// Instead of:
for (const ref of references) {
await wajub.payments.verify(ref); // sequential, slow
}
// Do:
const results = await Promise.all(
references.map(ref => wajub.payments.verify(ref))
);
// With concurrency limit (respecting rate limit):
const results = await pLimit(10)(
references.map(ref => () => wajub.payments.verify(ref))
);Use p-limit or semaphore
Limit concurrency to 5–10 simultaneous requests to respect the production rate limit (60 req/min) while benefiting from parallelism.
Optimizing initialization calls
Payment initialization is on the user's critical path. Every millisecond counts:
async function checkout(order) {
// 1. Validate input server-side before the API call
if (!order.amount || order.amount <= 0) {
throw new Error('Invalid amount');
}
// 2. Generate the reference before the call
const reference = `ORDER-${order.id}-T${Date.now()}`;
// 3. Aggressive timeout: the user is waiting
const { transaction, authorization_url } = await wajub.payments.initialize(
{
amount: order.amount,
currency: order.currency,
customer: { email: order.email, name: order.name },
description: order.description,
reference,
callback: `${process.env.BASE_URL}/payment/return`,
},
{ timeout: 10000 } // 10s max
);
// 4. Save reference in DB (non-blocking recommended)
await order.update({ trx: transaction.reference });
return authorization_url;
}Monitoring and alerts
Beyond code, a performant integration is measured:
- Response time: track the p95 of your Wajub API calls.
- Error rate: alert if the
5xxor429rate exceeds a threshold. - Webhook latency: measure the delay between the event and its processing.
Konsole for diagnostics
Use Konsole to inspect the latency of each orchestration step and identify bottlenecks.