Daraja is Safaricom's official REST API for M-PESA. You register at developer.safaricom.co.ke, get a Consumer Key and Consumer Secret, exchange them for an OAuth token, and then call the endpoint catalogue: STK Push (checkout), C2B (paybill / till receipts), B2C (payouts), Account Balance, and Transaction Status. Safaricom processed KShs 41.68 trillion in M-PESA volume in FY26 across 40.99 million customers (Source: Safaricom PLC Annual Report FY2026), so Daraja is the plumbing under every modern Kenyan payment integration.
This is the complete reference. For the shorter integration walkthrough that predates this guide, see the original Daraja API integration guide (still current for the Shopify / WooCommerce plugin path).
What Daraja actually is
Daraja (Swahili for "bridge") is a set of REST endpoints hosted by Safaricom that expose M-PESA operations to developers. Daraja 2.0 launched in 2018 as the modern replacement for the older SOAP-based G2 gateway. Every endpoint returns JSON, uses OAuth 2.0 bearer tokens, and follows an asynchronous callback pattern: you POST a request, Safaricom returns a synchronous acknowledgement, then the customer-facing action completes and Safaricom POSTs the final result to a callback URL you own.
Sandbox versus production
| Aspect | Sandbox | Production |
|---|---|---|
| Base URL | sandbox.safaricom.co.ke | api.safaricom.co.ke |
| Credentials | Issued instantly on app creation | Issued after Go-Live approval (1 to 3 business days) |
| Test short code | 174379 (STK Push), 600xxx (C2B) | Your assigned paybill or till |
| Test PIN | 174379 uses PIN 1234 on test numbers 254708374149 | Real customer PIN |
| Money moves | No, simulated only | Yes, real KES |
| Callback URL rules | HTTP or HTTPS, any host | HTTPS only, valid certificate, publicly reachable |
Authentication: OAuth token exchange
Every Daraja call needs a Bearer token in the Authorization header. You get that token by making a Basic-Auth GET against the OAuth endpoint with your Consumer Key and Consumer Secret as the username and password. The token is valid for 3599 seconds (about 1 hour). Cache it.
Sandbox: https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials
Production: https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials
Node.js OAuth token exchange
// Node.js 20+, native fetch
const consumerKey = process.env.MPESA_CONSUMER_KEY;
const consumerSecret = process.env.MPESA_CONSUMER_SECRET;
const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString('base64');
const res = await fetch(
'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials',
{ headers: { Authorization: `Basic ${auth}` } }
);
const { access_token, expires_in } = await res.json();
// Cache access_token for (expires_in - 60) seconds.Python OAuth token exchange
import base64, requests, os
key = os.environ['MPESA_CONSUMER_KEY']
secret = os.environ['MPESA_CONSUMER_SECRET']
auth = base64.b64encode(f"{key}:{secret}".encode()).decode()
r = requests.get(
'https://sandbox.safaricom.co.ke/oauth/v1/generate',
params={'grant_type': 'client_credentials'},
headers={'Authorization': f'Basic {auth}'},
timeout=10,
)
token = r.json()['access_token']PHP OAuth token exchange
<?php
$auth = base64_encode(getenv('MPESA_CONSUMER_KEY') . ':' . getenv('MPESA_CONSUMER_SECRET'));
$ch = curl_init('https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Basic ' . $auth]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = json_decode(curl_exec($ch), true);
$token = $response['access_token'];The endpoint catalogue
| Endpoint | Path | Purpose |
|---|---|---|
| OAuth | /oauth/v1/generate | Exchange Consumer Key + Secret for a Bearer token |
| STK Push | /mpesa/stkpush/v1/processrequest | Trigger a payment prompt on the customer's phone |
| STK Query | /mpesa/stkpushquery/v1/query | Query the status of an STK Push request |
| C2B Register URL | /mpesa/c2b/v1/registerurl | Register Confirmation and Validation URLs for a paybill or till |
| C2B Simulate | /mpesa/c2b/v1/simulate | Sandbox-only: simulate a customer payment |
| B2C | /mpesa/b2c/v1/paymentrequest | Pay money out to a customer M-PESA number |
| Account Balance | /mpesa/accountbalance/v1/query | Query the balance on a paybill, till or B2C float |
| Transaction Status | /mpesa/transactionstatus/v1/query | Look up the status of any M-PESA transaction by ID |
| Reversal | /mpesa/reversal/v1/request | Reverse a completed C2B or B2C transaction |
| Tax Remittance | /mpesa/b2b/v1/remittax | Remit taxes (KRA) from a business account |
For per-endpoint walkthroughs see the sibling guides in this cluster:
Rate limits
Safaricom does not publish exact numbers, but the following limits are consistent across production integrations in 2026:
- OAuth token: approximately 300 requests per minute per Consumer Key.
- STK Push: approximately 100 requests per minute per short code.
- C2B Register URL: single-shot per short code, not rate-limited (call once at deploy).
- B2C: throttled to your paid-out daily limit; a busy PSP hits it before rate limits bite.
- Callback traffic (inbound to you): no explicit limit from Safaricom; size for your own peak.
IP whitelisting and callback security
Callbacks are the security-critical path in Daraja. Safaricom will POST payment results to a URL you own; a naive endpoint that accepts any POST as authoritative can be spoofed by an attacker sending fake "successful payment" JSON. Recommended controls:
- HTTPS with a valid certificate. Safaricom rejects HTTP callbacks in production. Let's Encrypt is fine; self-signed is not.
- Whitelist Safaricom source IPs. Safaricom's callback traffic comes from a small set of IPs in their AS37061 range. Restrict inbound at your firewall or your reverse-proxy layer.
- Validate the payload structure and correlate the CheckoutRequestID.Every callback carries a request ID you generated on the outbound POST. If the ID does not match a pending request in your database, drop the callback.
- Rate-limit and log every callback. Even legitimate callbacks can be replayed by Safaricom on network glitches; make your handler idempotent.
Common errors and fixes
| Code | Message | Fix |
|---|---|---|
| 400.002.02 | Bad Request - Invalid PhoneNumber | Format as 2547XXXXXXXX or 2541XXXXXXXX (no plus, no leading zero) |
| 401.002.01 | Invalid Access Token | Token expired; request a new one and retry |
| 404.001.03 | Invalid Access Token / URL not found | You are calling production with sandbox credentials, or vice versa |
| 500.001.1001 | Unable to lock subscriber, a transaction is already in process | Wait 30 seconds; the customer has an open STK prompt |
| 500.001.1032 | Request cancelled by user | Customer pressed cancel on the STK prompt; treat as failure and prompt to retry |
| 500.002.1001 | Internal server error | Safaricom-side; log and retry with backoff |
| 2001 | The initiator information is invalid | B2C initiator name or security credential is wrong; regenerate credential |
Going live checklist
- Build and load-test on sandbox against the test short code and test PIN.
- Prepare production callback URLs (HTTPS, publicly reachable, HTTP 200 in under 5 seconds).
- Submit the Go-Live request at developer.safaricom.co.ke with your paybill / till, callback URLs and settlement bank.
- Once Safaricom issues production Consumer Key and Consumer Secret, replace the sandbox environment variables.
- Run one KES 10 end-to-end payment on production to confirm callbacks land and settle.
- Roll out to real traffic; monitor callback latency and STK success rate as your first two SLOs.
Related reading
- M-PESA for Business hub
- Daraja API integration (original guide)
- STK Push integration
- C2B integration
- B2C integration
- M-PESA payment gateways compared
- M-PESA charges 2026
- Kenya mobile money statistics 2026
FAQ
What is the difference between Daraja and the M-PESA API?
Daraja is the M-PESA API. Safaricom launched Daraja 2.0 in 2018 as the modern REST replacement for the older SOAP G2 gateway. Every current integration (STK Push, C2B, B2C, Balance, Transaction Status, B2B) is exposed through Daraja endpoints on api.safaricom.co.ke (production) and sandbox.safaricom.co.ke (sandbox).
How do I get Daraja credentials?
Register at developer.safaricom.co.ke, create an app, and Safaricom issues a Consumer Key and Consumer Secret for the sandbox instantly. For production, submit a Go-Live request with your paybill or till short code, callback URLs and settlement bank account. Production credentials typically arrive within 1 to 3 business days.
How long does the Daraja OAuth token last?
One hour (3599 seconds). Cache the token and only re-request when it is close to expiry. Requesting a fresh token on every API call will hit rate limits and slow every checkout by 300 to 500 ms.
Do I need to whitelist IP addresses for callbacks?
Safaricom does not require you to whitelist their IPs, but you should whitelist your own callback URL by only accepting POST requests from the Safaricom IP range and validating the payload structure. Callbacks that pass validation but are not HTTPS will be rejected by Safaricom on production.
What are the Daraja rate limits?
Safaricom does not publish hard numbers, but community-measured limits sit around 100 requests per minute per short code for STK Push, 300 per minute for OAuth token, and unmetered for callback traffic. B2C is throttled harder because Safaricom applies daily payout caps.
Can I use Daraja from a mobile app directly?
No. Consumer Secret and initiator credentials must never live on a client device. Every Daraja call must go through your server. The mobile app talks to your backend; your backend talks to Daraja.
What is the most common Daraja error in production?
Error 500.001.1001 ("Unable to lock subscriber"), which means the customer already has an open STK prompt. Retry after 30 seconds. The second most common is 400.002.02 ("Bad Request - Invalid PhoneNumber"), which means the number is not in the 2547XXXXXXXX or 2541XXXXXXXX format Daraja expects.
Verified 1 September 2026. Volume and customer figures from Safaricom PLC Annual Report FY2026. Endpoint paths and OAuth flow verified against the current Safaricom Daraja portal at developer.safaricom.co.ke.
Related reading
Curated external sources we cite. Open in a new tab.
Fuliza for Business Calculator
B2B Fuliza fees for merchants who use Fuliza for Business.
mpesa.or.ke · calculator
M-Pesa Bulk Payments Calculator
B2C bulk-disbursement fees for businesses paying many recipients.
mpesa.or.ke · calculator
Comprehensive Guide to Safaricom's My OneApp
Walkthrough of Safaricom's OneApp, features, setup, and use.
mpesa.or.ke · guide