The Daraja API is Safaricom's gateway for integrating M-Pesa payments into your applications. To get started, register at developer.safaricom.co.ke, create an app to get your consumer key and secret, generate an OAuth token, then make your first API call to the sandbox. The whole process takes about an hour if you follow the steps carefully.
"Daraja" means "bridge" in Swahili, and that is exactly what it does. It is Safaricom's official API that lets your application talk to the M-Pesa platform. Before Daraja existed (it launched in 2018), integrating M-Pesa was a painful process involving manual paybill setups, SMS parsing, and a lot of back-and-forth with Safaricom's enterprise team. Daraja replaced all of that with a standard REST API that any developer can use.
Here is what you can do with the Daraja API:
If you have ever made an HTTP request to any API (GitHub, weather APIs, anything), you already understand the core pattern. Daraja uses standard HTTP methods, JSON payloads, and OAuth 2.0 authentication. The M-Pesa-specific parts are mostly about understanding what each endpoint expects and how callbacks work. That is what this tutorial walks you through.
Everything starts at the Safaricom developer portal. This is where you create apps, get credentials, and access the sandbox for testing.
Once you are logged in, you will see a dashboard with options for "My Apps," documentation links, and API references. The portal interface can be a bit clunky (it has improved over the years, but it is still not the most polished developer experience), so do not be surprised if pages load slowly or the layout feels dated. What matters is the API itself, which works reliably.
One thing to note: your developer portal account is separate from your personal M-Pesa account. Having a developer account does not give you a business shortcode or the ability to receive real payments. For the sandbox (which is where you will spend all your time while learning), you do not need any of that. Safaricom provides test shortcodes and credentials that simulate the entire payment flow without touching real money.
If the portal is temporarily down (it happens occasionally), just wait an hour and try again. Do not try to find workarounds or alternative portals. The official Safaricom developer portal is the only legitimate source for Daraja credentials.
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
With your account set up, you need to create an "app" on the portal. This is not a real application you upload somewhere. It is just a container that holds your API credentials.
After creating the app, you will see two critical values on the app's detail page:
Copy both values and store them somewhere safe. A .env file in your project is the standard approach:
DARAJA_CONSUMER_KEY=your_consumer_key_here
DARAJA_CONSUMER_SECRET=your_consumer_secret_here
DARAJA_ENV=sandbox
For the sandbox environment, Safaricom also provides test credentials that you will need for specific API calls. These include a test shortcode (usually 174379), a passkey, and test phone numbers. You can find these in the API documentation section of the portal, or in our dedicated sandbox setup guide.
A common mistake at this stage: creating multiple apps for the same project. You only need one app. Each app gets one consumer key and secret pair, and that pair works for all the Daraja APIs you selected when creating the app.
Every Daraja API call requires an access token. You cannot skip this step. The token proves to Safaricom's servers that you are authorized to make requests. Here is how the flow works:
The OAuth endpoint for the sandbox is:
https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials
You authenticate using HTTP Basic Auth, where the username is your consumer key and the password is your consumer secret. In practice, this means Base64-encoding the string "consumerKey:consumerSecret" and sending it in the Authorization header.
Here is how to do it in Node.js:
async function getAccessToken() {
const consumerKey = process.env.DARAJA_CONSUMER_KEY;
const consumerSecret = process.env.DARAJA_CONSUMER_SECRET;
const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString('base64');
const response = await fetch(
'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials',
{
method: 'GET',
headers: {
Authorization: `Basic ${auth}`,
},
}
);
const data = await response.json();
console.log('Access Token:', data.access_token);
console.log('Expires in:', data.expires_in, 'seconds');
return data.access_token;
}
If this works, you will get back a JSON response like this:
{
"access_token": "cXl6MTIzNDU2Nzg5...",
"expires_in": "3600"
}
If you get a 400 or 401 error instead, double-check your consumer key and secret. The most common problem is extra whitespace when copying from the portal. Trim your credentials and try again.
In a production application, you would cache this token and refresh it before it expires rather than generating a new one for every request. But while learning, generating a fresh token each time is perfectly fine.
Now that you have an access token, let us make an actual API call. The simplest one to start with is an STK Push request, which triggers the M-Pesa payment prompt on a phone. In the sandbox, this will not charge any real money. It simulates the entire flow.
Here is a complete STK Push request in Node.js:
async function initiateSTKPush(accessToken) {
const shortcode = '174379'; // Safaricom sandbox shortcode
const passkey = 'bfb279f9aa9bdbcf158e97dd71a467cd2e0c893059b10f78e6b72ada1ed2c919';
const timestamp = new Date()
.toISOString()
.replace(/[-T:.Z]/g, '')
.slice(0, 14);
const password = Buffer.from(`${shortcode}${passkey}${timestamp}`).toString('base64');
const payload = {
BusinessShortCode: shortcode,
Password: password,
Timestamp: timestamp,
TransactionType: 'CustomerPayBillOnline',
Amount: 1,
PartyA: '254708374149', // Test phone number
PartyB: shortcode,
PhoneNumber: '254708374149',
CallBackURL: 'https://your-callback-url.com/callback',
AccountReference: 'Test001',
TransactionDesc: 'Test payment',
};
const response = await fetch(
'https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest',
{
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
}
);
const data = await response.json();
console.log('STK Push Response:', data);
return data;
}
A successful response looks like this:
{
"MerchantRequestID": "29115-34620561-1",
"CheckoutRequestID": "ws_CO_191220191020363925",
"ResponseCode": "0",
"ResponseDescription": "Success. Request accepted for processing",
"CustomerMessage": "Success. Request accepted for processing"
}
A ResponseCode of "0" means the request was accepted. It does not mean the payment is complete. The actual payment result comes through the callback URL, which we will cover in detail in our STK Push deep dive.
If you get an error, check the following: Is your access token fresh (less than one hour old)? Is the timestamp format correct (YYYYMMDDHHmmss)? Is the password correctly Base64-encoded from shortcode + passkey + timestamp? These three things account for the vast majority of first-attempt failures.
Let us break down what actually happened when you made that STK Push call, because understanding the flow is more important than memorizing the code:
This is an asynchronous flow. Your initial API call is basically saying "hey Safaricom, please ask this person to pay." The actual result comes later, through a separate channel (the callback). This trips up a lot of beginners who expect the payment result in the response to their initial request.
The callback URL is a real URL on the internet that Safaricom's servers can reach. During development, your localhost is not accessible from the internet, so you need a tool like ngrok to create a tunnel. We cover this in detail in the sandbox setup guide.
One more thing worth knowing: the sandbox does not always behave identically to production. Response times are different, some edge cases are not perfectly simulated, and the test phone numbers do not actually receive push notifications. The sandbox is good enough for learning the API structure and testing your code logic, but you should expect some differences when you move to production.
After teaching M-Pesa integration to hundreds of developers at McTaba Labs, these are the mistakes we see over and over again from people working with Daraja for the first time:
1. Hardcoding credentials in your source code. Never put your consumer key, secret, or passkey directly in your JavaScript files. Use environment variables (.env files with the dotenv package). If you accidentally push credentials to a public GitHub repository, anyone can use them. Safaricom can also disable your app if they detect leaked credentials.
2. Using the wrong base URL. The sandbox URL is https://sandbox.safaricom.co.ke. The production URL is https://api.safaricom.co.ke. Mixing these up means your requests will fail with confusing errors. Store the base URL in an environment variable alongside your credentials so you can switch between sandbox and production cleanly.
3. Malformed timestamp or password. The timestamp must be exactly in the format YYYYMMDDHHmmss (for example, 20260719143022). The password is Base64(shortcode + passkey + timestamp). If any of these three components are wrong, the request fails. A common bug is using the wrong time zone. Use the current time in your server's timezone, or better yet, use UTC and be consistent.
4. Ignoring the callback entirely. The STK Push response only tells you the request was accepted. The actual payment result (success or failure) comes to your callback URL. If you do not set up a callback handler, you will never know whether the customer actually paid. This is probably the single biggest gap we see in beginner implementations.
5. Not handling token expiry. Your OAuth token expires after one hour. If your app runs longer than that without refreshing the token, all API calls will start failing with 401 errors. Build token refresh logic early, even in your first prototype.
6. Testing with real phone numbers in sandbox. The sandbox has specific test phone numbers (like 254708374149). Using your real phone number in the sandbox will not work because the sandbox does not actually send push notifications to real phones. Save real-number testing for when you move to production with live credentials.
If you want to build real confidence with Daraja before deploying to production, our M-Pesa Integration for Developers course (KES 9,999) walks you through every integration type with production-ready patterns, proper error handling, and callback architectures that scale.
KES 9,999
Master Daraja API, STK Push, C2B, B2C, and callback handling. Build a real payment flow from scratch. 10 modules, 57 lessons.
Enrol in M-Pesa Integration