Introduction

The Daraja M-PESA Gateway is a unified REST API that simplifies integration with Safaricom's M-PESA Daraja platform. Instead of managing OAuth tokens, security credentials, callback URLs, and complex payload structures yourself, you authenticate with a single API key and send straightforward JSON requests. The gateway handles everything else.

What you can do

Note

Account Balance queries allow you to check your M-PESA organization balance. This endpoint requires a valid API key and appropriate permissions.

Base URL

https:///gateway/v1/

All endpoints accept POST requests with a JSON body and return JSON responses. Every response includes rate-limit metadata in both HTTP headers and the response body.

Response envelope

Every successful response follows this structure:

{
    "success": true,
    "data": {
        // endpoint-specific payload
    },
    "meta": {
        "rate_limit": {
            "limit": 50,
            "remaining": 49,
            "reset": 1711929600
        }
    }
}

Every error response follows this structure:

{
    "success": false,
    "error": {
        "code": 401,
        "message": "Invalid API key."
    }
}

Authentication

All API requests must include a valid API key. Keys are tied to your user account and can be created, rotated, or revoked from the dashboard.

Getting your API key

  1. Register or log in at /user.
  2. Navigate to API Keys in your dashboard.
  3. Click Generate New Key. Your key will be displayed once — copy it immediately.
  4. Store the key securely. Do not expose it in client-side code or public repositories.

Sending the key

Include your key in the X-Api-Key request header:

X-Api-Key: your_api_key_here

Alternatively, you can use the Authorization header with the Bearer scheme:

Authorization: Bearer your_api_key_here
Note

If both headers are present, X-Api-Key takes precedence. If neither header is provided, the gateway returns 401 Unauthorized.

Key lifecycle

You can have multiple active keys. Each key independently tracks its last-used timestamp. Revoking a key is immediate and permanent — any in-flight request using that key will fail with 401.

Rate Limits

Requests are rate-limited on a per-user, per-day basis. The limit depends on your account tier. Rate counters reset at midnight (server time) each day.

Tier Daily Limit Description
Free 50 Default tier for new accounts. Suitable for development and testing.
Basic 500 For small applications in production.
Pro Unlimited No daily cap. For high-volume production workloads.

Response headers

Every API response includes these rate-limit headers:

Header Type Description
X-RateLimit-Limit integer | "unlimited" Maximum number of requests allowed per day for your tier.
X-RateLimit-Remaining integer | "unlimited" Number of requests remaining in the current window.
X-RateLimit-Reset integer Unix timestamp (UTC) when the rate-limit window resets (midnight).

Exceeding the limit

When you exhaust your daily quota, the API returns 429 Too Many Requests with a JSON error body. The X-RateLimit-Reset header tells you when you can resume making requests.

{
    "success": false,
    "error": {
        "code": 429,
        "message": "Rate limit exceeded (50/day for free tier). Upgrade for more requests."
    }
}

Endpoints

The gateway exposes nine endpoints, all under /gateway/v1/ or /daraja/api/. Every endpoint requires POST with a Content-Type: application/json body and a valid API key.

STK Push

Initiates a Lipa Na M-PESA Online (STK Push) prompt on the customer's phone. The customer enters their M-PESA PIN to authorize the payment.

POST /gateway/v1/stk_push.php

Request Headers

HeaderRequiredDescription
Content-Type Required Must be application/json
X-Api-Key Required Your API key

Request Body

ParameterTypeRequiredDescription
phone string Required Customer phone number. Accepts multiple formats: 254XXXXXXXXX, 0XXXXXXXXX, or XXXXXXXXX (9 digits). Whitespace is stripped automatically. The gateway normalizes all formats to 254XXXXXXXXX.
amount integer Required Amount in KES. Must be >= 1.
reference string Optional Account reference / order ID displayed on the STK prompt. Default: "Payment".
description string Optional Human-readable description of the transaction. Default: "Payment".

Example Request

{
    "phone": "254712345678",
    "amount": 100,
    "reference": "ORD-10042",
    "description": "Payment for order #10042"
}

Responses

200 — Success
{
    "success": true,
    "data": {
        "MerchantRequestID": "29115-34620561-1",
        "CheckoutRequestID": "ws_CO_191220191020363925",
        "ResponseCode": "0",
        "ResponseDescription": "Success. Request accepted for processing",
        "CustomerMessage": "Success. Request accepted for processing",
        "statusCheck": {
            "checkoutRequestId": "ws_CO_191220191020363925",
            "endpoint": "gateway/v1/stk_status.php",
            "method": "POST",
            "polling": "Poll every 3-5 seconds for up to 2 minutes."
        }
    },
    "meta": {
        "rate_limit": {
            "limit": 50,
            "remaining": 49,
            "reset": 1711929600
        }
    }
}
400 — Validation Error
{
    "success": false,
    "error": {
        "code": 400,
        "message": "Missing required parameter: phone"
    }
}
Polling for Payment Status

An STK Push is asynchronous — the customer receives a prompt on their phone and may take time to respond. After a successful STK Push request, use the checkoutRequestId from the statusCheck object to poll the STK Status endpoint every 3–5 seconds. The status will update from pending to success or failed once the customer responds (or the request times out).

STK Status

Checks the payment status of an STK Push transaction using the CheckoutRequestID returned from the STK Push response. This queries the gateway's local database (populated by M-PESA callbacks) rather than calling Safaricom directly, so it does not count against your rate limit.

Recommended Workflow

1. Send an STK Push request.
2. Extract checkoutRequestId from the statusCheck object in the response.
3. Poll this endpoint every 3–5 seconds.
4. Stop polling when status is "success" or "failed" (or after ~2 minutes timeout).

POST /gateway/v1/stk_status.php

Request Headers

HeaderRequiredDescription
Content-Type Required Must be application/json
X-Api-Key Required Your API key. You can only check transactions initiated with your own API key.

Request Body

ParameterTypeRequiredDescription
checkoutRequestId string Required The CheckoutRequestID returned from the STK Push response.

Example Request

{
    "checkoutRequestId": "ws_CO_191220191020363925"
}

Responses

200 — Payment Successful
{
    "success": true,
    "data": {
        "checkoutRequestId": "ws_CO_191220191020363925",
        "status": "success",
        "receipt": "UC4C38D22C",
        "resultCode": "0",
        "resultDesc": "The service request is processed successfully.",
        "amount": 100,
        "phone": "254712345678",
        "createdAt": "2026-03-04 14:30:00"
    },
    "meta": {
        "rate_limit": {
            "limit": 50,
            "remaining": 48,
            "reset": 1711929600
        }
    }
}
200 — Still Pending
{
    "success": true,
    "data": {
        "checkoutRequestId": "ws_CO_191220191020363925",
        "status": "pending",
        "receipt": null,
        "resultCode": null,
        "resultDesc": null,
        "amount": 100,
        "phone": "254712345678",
        "createdAt": "2026-03-04 14:30:00"
    },
    "meta": { ... }
}
200 — Payment Failed
{
    "success": true,
    "data": {
        "checkoutRequestId": "ws_CO_191220191020363925",
        "status": "failed",
        "receipt": null,
        "resultCode": "1032",
        "resultDesc": "Request cancelled by user.",
        "amount": 100,
        "phone": "254712345678",
        "createdAt": "2026-03-04 14:30:00"
    },
    "meta": { ... }
}
404 — Not Found
{
    "success": false,
    "error": {
        "code": 404,
        "message": "Transaction not found or does not belong to your account."
    }
}

STK Query

Queries the status of a previously initiated STK Push request. Use this to confirm whether the customer completed, cancelled, or ignored the payment prompt.

STK Status vs STK Query

STK Status checks the gateway's local database (fast, no Safaricom call). STK Query queries Safaricom directly and counts against your rate limit. For most use cases, prefer STK Status for polling after an STK Push.

POST /gateway/v1/stk_query.php

Request Headers

HeaderRequiredDescription
Content-Type Required Must be application/json
X-Api-Key Required Your API key

Request Body

ParameterTypeRequiredDescription
checkoutRequestId string Required The CheckoutRequestID returned from the STK Push response.

Example Request

{
    "checkoutRequestId": "ws_CO_191220191020363925"
}

Responses

200 — Success
{
    "success": true,
    "data": {
        "ResponseCode": "0",
        "ResponseDescription": "The service request has been accepted successfully.",
        "MerchantRequestID": "29115-34620561-1",
        "CheckoutRequestID": "ws_CO_191220191020363925",
        "ResultCode": "0",
        "ResultDesc": "The service request is processed successfully."
    },
    "meta": {
        "rate_limit": {
            "limit": 50,
            "remaining": 48,
            "reset": 1711929600
        }
    }
}
400 — Validation Error
{
    "success": false,
    "error": {
        "code": 400,
        "message": "Missing required parameter: checkoutRequestId"
    }
}

B2C Payment

Sends money from your M-PESA business account directly to a customer's M-PESA wallet. Commonly used for salary payments, cashback, refunds, and promotional disbursements.

POST /gateway/v1/b2c.php

Request Headers

HeaderRequiredDescription
Content-Type Required Must be application/json
X-Api-Key Required Your API key

Request Body

ParameterTypeRequiredDescription
phone string Required Recipient phone number. Accepts 254XXXXXXXXX, 0XXXXXXXXX, or XXXXXXXXX (9 digits). Whitespace stripped automatically.
amount integer Required Amount in KES. Must be >= 1.
commandId string Optional Type of B2C transaction. One of: BusinessPayment, SalaryPayment, PromotionPayment. Default: "BusinessPayment".
remarks string Optional Comments sent with the transaction. Default: "Payment".
occasion string Optional Optional occasion or reason for the payment.

Example Request

{
    "phone": "254712345678",
    "amount": 500,
    "commandId": "BusinessPayment",
    "remarks": "Refund for order #10042",
    "occasion": "Customer refund"
}

Responses

200 — Success
{
    "success": true,
    "data": {
        "ConversationID": "AG_20191219_00004492b1b6f0de8f9c",
        "OriginatorConversationID": "16740-34861180-1",
        "ResponseCode": "0",
        "ResponseDescription": "Accept the service request successfully."
    },
    "meta": {
        "rate_limit": {
            "limit": 500,
            "remaining": 499,
            "reset": 1711929600
        }
    }
}
400 — Validation Error
{
    "success": false,
    "error": {
        "code": 400,
        "message": "Invalid commandId. Allowed: BusinessPayment, SalaryPayment, PromotionPayment"
    }
}

Account Balance Admin Only

Queries the balance of the M-PESA business account associated with your gateway configuration. This endpoint is restricted to administrators and is not available through the API gateway for regular API users.

Restricted

This endpoint returns 403 Forbidden when called through the API gateway. Account balance queries can only be performed from the admin dashboard.

POST /gateway/v1/account_balance.php

Request Headers

HeaderRequiredDescription
Content-Type Required Must be application/json
X-Api-Key Required Your API key

Request Body

ParameterTypeRequiredDescription
remarks string Optional Comments for the balance query. Default: "Balance check".

Example Request

{
    "remarks": "End-of-day balance check"
}

Responses

200 — Success
{
    "success": true,
    "data": {
        "ConversationID": "AG_20191219_00004492b1b6f08c2e88",
        "OriginatorConversationID": "16740-34861134-1",
        "ResponseCode": "0",
        "ResponseDescription": "Accept the service request successfully."
    },
    "meta": {
        "rate_limit": {
            "limit": 50,
            "remaining": 47,
            "reset": 1711929600
        }
    }
}
502 — Upstream Error
{
    "success": false,
    "error": {
        "code": 502,
        "message": "Upstream M-PESA service unavailable. Try again later."
    }
}

Reversal

Reverses a completed M-PESA transaction. The funds are returned to the sender's account. This is typically used to correct erroneous payments.

POST /gateway/v1/reversal.php

Request Headers

HeaderRequiredDescription
Content-Type Required Must be application/json
X-Api-Key Required Your API key

Request Body

ParameterTypeRequiredDescription
transactionId string Required The M-PESA transaction ID to reverse (e.g. OEI2AK4Q16).
amount integer Required Amount to reverse in KES. Must be >= 1.
remarks string Optional Reason for the reversal.
occasion string Optional Optional occasion for the reversal.

Example Request

{
    "transactionId": "OEI2AK4Q16",
    "amount": 100,
    "remarks": "Wrong recipient",
    "occasion": "Correction"
}

Responses

200 — Success
{
    "success": true,
    "data": {
        "ConversationID": "AG_20191219_00005c92cafb81236f16",
        "OriginatorConversationID": "16740-34861299-1",
        "ResponseCode": "0",
        "ResponseDescription": "Accept the service request successfully."
    },
    "meta": {
        "rate_limit": {
            "limit": 50,
            "remaining": 46,
            "reset": 1711929600
        }
    }
}
400 — Validation Error
{
    "success": false,
    "error": {
        "code": 400,
        "message": "Missing required parameter: transactionId"
    }
}

QR Code

Generates an M-PESA dynamic QR code. The customer scans the code with the M-PESA app to complete the payment. Useful for in-store and point-of-sale scenarios.

POST /gateway/v1/qr_code.php

Request Headers

HeaderRequiredDescription
Content-Type Required Must be application/json
X-Api-Key Required Your API key

Request Body

ParameterTypeRequiredDescription
merchantName string Optional Name of the merchant displayed on the QR code.
reference string Optional Payment reference or order number.
amount integer Required Amount in KES. Must be >= 1.
transactionType string Optional QR transaction type code. Default: "BG" (Buy Goods).

Example Request

{
    "merchantName": "Acme Store",
    "reference": "INV-2024-001",
    "amount": 250,
    "transactionType": "BG"
}

Responses

200 — Success
{
    "success": true,
    "data": {
        "ResponseCode": "0",
        "RequestID": "QR-1001",
        "ResponseDescription": "QR code generated successfully.",
        "QRCode": "data:image/png;base64,iVBOR..."
    },
    "meta": {
        "rate_limit": {
            "limit": 50,
            "remaining": 45,
            "reset": 1711929600
        }
    }
}
400 — Validation Error
{
    "success": false,
    "error": {
        "code": 400,
        "message": "Missing required parameter: amount (must be >= 1)"
    }
}

Transaction Status

Queries the status of any M-PESA transaction by its transaction ID. Use this endpoint to verify whether a payment was completed, failed, or is still pending.

Note

This endpoint is available but may require additional server-side configuration depending on your deployment. Contact the administrator if you receive a 501 response.

POST /gateway/v1/transaction_status.php

Request Headers

HeaderRequiredDescription
Content-Type Required Must be application/json
X-Api-Key Required Your API key
X-Api-Secret Required Your API secret

Request Body

ParameterTypeRequiredDescription
transactionId string Required The M-PESA receipt/reference code (e.g. UHQC348YHN).
partyA string Optional Your Organization ShortCode (e.g. 9579226 or 3534519). Defaults to system shortcode.
identifierType string Optional 1 (MSISDN), 2 (Till Number), 4 (ShortCode - default), 11 (Reversal Party).
remarks string Optional Query reason/remarks (e.g. Status Check).

Example Request

{
    "transactionId": "UHQC348YHN",
    "partyA": "9579226",
    "identifierType": "4",
    "remarks": "Status Check"
}

Responses

200 — Success
{
    "success": true,
    "data": {
        "ResponseCode": "0",
        "ResponseDescription": "Accept the service request successfully.",
        "ConversationID": "AG_20191219_00005e83b2effadd5b6a",
        "OriginatorConversationID": "16740-34861444-1"
    },
    "meta": {
        "rate_limit": {
            "limit": 50,
            "remaining": 44,
            "reset": 1711929600
        }
    }
}
400 — Validation Error
{
    "success": false,
    "error": {
        "code": 400,
        "message": "Missing required parameter: transactionId"
    }
}

B2Pochi (Business Pay to Pochi)

The B2Pochi API allows a business to send funds from its M-PESA Business account directly to a Pochi la Biashara MSISDN. This is ideal for businesses paying small-scale vendors or service providers who use Pochi.

POST /api/b2b_pochi.php

Request Body

ParameterTypeRequiredDescription
partyA string Required The sending Business Shortcode.
partyB string Required The receiving Pochi MSISDN (e.g., 2547XXXXXXXX).
amount integer Required Amount to send in KES.
accountReference string Optional Default: B2Pochi.

Bill Manager

Safaricom Bill Manager is an invoicing and payment reconciliation service. It allows businesses to send invoices via SMS/Email and receive payments directly against those invoices.

Endpoints
  • Onboarding: /api/billmanager_onboarding.php
  • Single Invoice: /api/billmanager_single_invoice.php
  • Bulk Invoice: /api/billmanager_bulk_invoice.php
  • Reconciliation: /api/billmanager_reconciliation.php

Example: Single Invoice

POST /api/billmanager_single_invoice.php
{
    "externalReference": "INV-101",
    "billedFullName": "John Doe",
    "billedPhoneNumber": "254712345678",
    "amount": 1500,
    "dueDate": "2026-06-30",
    "accountReference": "ACC-001"
}

C2B v2 (Paybill/Till)

The Customer to Business (C2B) API allows you to receive real-time notifications when a customer pays into your Paybill or Till number. Unlike STK Push, the transaction is initiated by the customer from their M-PESA menu.

Two-Step Process

1. URL Registration: Register your Confirmation and Validation URLs (one-time).
2. Simulation (Sandbox only): Trigger a test payment to verify your URLs are working.

1. URL Registration

POST /api/c2b_v2.php?operation=register

Request Body

ParameterTypeRequiredDescription
shortCode string Required The Paybill or Till number to register.
responseType string Optional Default action if ValidationURL is down. Completed (default) or Cancelled.
confirmationUrl string Optional URL for final payment notifications. Defaults to gateway callback.
validationUrl string Optional URL to validate account numbers before payment. Defaults to gateway callback.

2. C2B Simulation

POST /api/c2b_v2.php?operation=simulate

Request Body

ParameterTypeRequiredDescription
shortCode string Required The receiving Paybill or Till number.
amount integer Required The amount to simulate.
msisdn string Required The phone number of the "customer" (e.g., 254708374149).
billRef string Required The Account Number (for Paybill) or Invoice Number.
commandId string Optional CustomerPayBillOnline (default) or CustomerBuyGoodsOnline.

3. Gateway Callback Endpoints & Payload Schema

When you register your URLs, Safaricom posts real-time payment notifications directly to your Confirmation URL:

  • Validation URL: https://apidj.pgwiz.cloud/gateway/v1/c2b_validation.php
  • Confirmation URL: https://apidj.pgwiz.cloud/gateway/v1/c2b_confirmation.php

Incoming C2B Callback Payload (from Safaricom)

FieldTypeDescription
TransID string M-PESA Receipt Number (e.g. UHQC348YHN).
TransAmount string/numeric Amount paid by customer (e.g. 1.00).
TransTime string Timestamp formatted as YYYYMMDDHHMMSS (e.g. 20260826103418).
BusinessShortCode string Receiving ShortCode / Till / Child ShortCode (e.g. 9579226).
BillRefNumber string Bill / Account reference number (for Paybill).
InvoiceNumber string Invoice number if applicable.
OrgAccountBalance string/numeric Organization account balance after the transaction settled (e.g. 6.00).
MSISDN string Customer Phone identifier (SHA-256 hashed in C2B v1; masked in C2B v2).
FirstName, LastName string Customer's registered M-PESA names (e.g. Peter).
TransactionType string Customer Merchant Payment (Buy Goods) or Pay Utility (Paybill).

Example Callback Payload

{
    "TransactionType": "Customer Merchant Payment",
    "TransID": "UHQC348YHN",
    "TransTime": "20260826103418",
    "TransAmount": "1.00",
    "BusinessShortCode": "9579226",
    "BillRefNumber": "",
    "InvoiceNumber": "",
    "OrgAccountBalance": "6.00",
    "ThirdPartyTransID": "",
    "MSISDN": "ba776a5811b19bac7253658d7fc2bba1012a908ffe7928fff8364db3a093daa7",
    "FirstName": "Peter"
}
Buy Goods Shortcode Routing Note

For Buy Goods (Till numbers), Safaricom requires C2B URLs to be registered to the Child Shortcode (e.g. 9579226), which receives all payments made to the front-facing Till (e.g. 3188200) under Head Office (3534519).

Standing Order (M-PESA Ratiba)

Creates a recurring M-PESA payment instruction. The customer receives an NI push (STK prompt) to authorize the standing order. Once approved, M-PESA automatically debits the customer’s account at the specified frequency until the end date.

Note

This endpoint is available through the API gateway. Ensure your M-PESA Ratiba product is enabled in the Daraja portal before use.

POST /gateway/v1/standing_order

Request Body

ParameterTypeRequiredDescription
standingOrderName string Required A descriptive name for the standing order (e.g. “Monthly Subscription”).
startDate string Required Start date in YYYYMMDD format. Must be a current or future date.
endDate string Required End date in YYYYMMDD format. Must be after startDate.
businessShortCode string Required The organization shortcode receiving the payments.
transactionType string Required Type of standing order. One of: Standing Order Customer Pay Bill, Standing Order Customer Pay Merchant.
amount integer Required Amount in KES to be debited each cycle.
partyA string Required Customer phone number in 254XXXXXXXXX format.
callBackUrl string Required URL where Safaricom will send transaction results.
frequency string Required Payment frequency. Values: 1 (One-Off), 2 (Daily), 3 (Weekly), 4 (Monthly), 5 (Bi-Monthly), 6 (Quarterly), 7 (Half-Year), 8 (Yearly).
accountReference string Optional Account reference for the payment. Default: "Standing Order".
transactionDesc string Optional Human-readable description. Default: "M-PESA Standing Order".
receiverPartyIdentifierType string Optional Receiver identifier type. 4 for shortcode (default).

Example Request

{
    "standingOrderName": "Monthly Subscription",
    "startDate": "20260401",
    "endDate": "20261231",
    "businessShortCode": "174379",
    "transactionType": "Standing Order Customer Pay Bill",
    "amount": 500,
    "partyA": "254712345678",
    "callBackUrl": "https:///callback",
    "frequency": "4",
    "accountReference": "SUB-001",
    "transactionDesc": "Monthly subscription payment"
}

Responses

200 — Success
{
    "ResponseHeader": {
        "responseCode": "0",
        "responseDescription": "Success. Request accepted for processing."
    }
}
400 — Validation Error
{
    "ResponseHeader": {
        "responseCode": "400",
        "responseDescription": "An internal error occurred."
    },
    "errorMessage": "Missing required parameter: 'partyA'"
}

Frequency Reference

ValueFrequency
1One-Off
2Daily
3Weekly
4Monthly
5Bi-Monthly (every 2 months)
6Quarterly (every 3 months)
7Half-Year (every 6 months)
8Yearly

Pull Transactions (C2B Reconciliation)

Query all C2B transactions performed under a shortcode within the last 48 hours. This is a two-step process: first register your shortcode for pull access (one-time), then query transactions within a date range.

Note

This endpoint is available through the API gateway. Ensure your M-PESA Ratiba product is enabled in the Daraja portal before use.

Step 1: Register for Pull Access

A one-time registration to enable pulling transactions for your shortcode. This must be completed before you can query transactions.

POST /gateway/v1/pull_transactions?operation=register

Register — Request Body

ParameterTypeRequiredDescription
shortCode string Required The organization shortcode to register for pull transactions.
nominatedNumber string Required The MSISDN (phone number) nominated for pull access.
callBackUrl string Required URL where Safaricom will send registration confirmation.

Register — Example Request

{
    "shortCode": "600000",
    "nominatedNumber": "254712345678",
    "callBackUrl": "https:///pull-callback"
}

Register — Responses

200 — Success
{
    "ResponseCode": "0",
    "ResponseMessage": "Success"
}
400 — Validation Error
{
    "ResponseStatus": "400",
    "ResponseDescription": "An internal error occurred.",
    "errorMessage": "Missing required parameter: 'shortCode'"
}

Step 2: Query Transactions

After registration, query C2B transactions within a date range. The date range must be within the last 48 hours. Results are paginated using an offset value.

POST /gateway/v1/pull_transactions?operation=query

Query — Request Parameters

ParameterTypeRequiredDescription
shortCode string Required The registered organization shortcode.
startDate string Required Start of the query window in YYYY-MM-DD HH:MM:SS format. Must be within the last 48 hours.
endDate string Required End of the query window in YYYY-MM-DD HH:MM:SS format.
offsetValue string Optional Pagination offset for large result sets. Default: "0".

Query — Example Request

{
    "shortCode": "600000",
    "startDate": "2026-03-02 00:00:00",
    "endDate": "2026-03-04 23:59:59",
    "offsetValue": "0"
}

Query — Responses

200 — Success
{
    "ResponseCode": "1000",
    "ResponseMessage": "Success",
    "Response": [
        {
            "transactionId": "OEI2AK4Q16",
            "trxDate": "2026-03-03 14:30:00",
            "msisdn": "254712345678",
            "sender": "John Doe",
            "transactiontype": "Pay Bill",
            "billreference": "INV-001",
            "amount": "500.00",
            "organizationname": "My Business"
        }
    ]
}
400 — Validation Error
{
    "ResponseStatus": "400",
    "ResponseDescription": "An internal error occurred.",
    "errorMessage": "Date format must be YYYY-MM-DD HH:MM:SS"
}
48-Hour Limit

Safaricom restricts pull transaction queries to a maximum window of 48 hours. If you need transactions older than 48 hours, use the Transaction Status endpoint to look up individual transactions by ID.

Code Examples

The following examples demonstrate how to call the STK Push endpoint. The same pattern applies to all other endpoints — change the URL and request body as needed.

curl -X POST https:///gateway/v1/stk_push.php \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: your_api_key_here" \
  -d '{
    "phone": "254712345678",
    "amount": 100,
    "reference": "ORD-10042",
    "description": "Payment for order #10042"
  }'
<?php

$url = 'https:///gateway/v1/stk_push.php';

$payload = json_encode([
    'phone'       => '254712345678',
    'amount'      => 100,
    'reference'   => 'ORD-10042',
    'description' => 'Payment for order #10042',
]);

$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'Content-Type: application/json',
        'X-Api-Key: your_api_key_here',
    ],
    CURLOPT_POSTFIELDS => $payload,
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($response, true);

if ($data['success']) {
    echo 'CheckoutRequestID: ' . $data['data']['CheckoutRequestID'];
} else {
    echo 'Error: ' . $data['error']['message'];
}
import requests

url = "https:///gateway/v1/stk_push.php"

headers = {
    "Content-Type": "application/json",
    "X-Api-Key": "your_api_key_here",
}

payload = {
    "phone": "254712345678",
    "amount": 100,
    "reference": "ORD-10042",
    "description": "Payment for order #10042",
}

response = requests.post(url, json=payload, headers=headers)
data = response.json()

if data["success"]:
    print("CheckoutRequestID:", data["data"]["CheckoutRequestID"])
    print("Remaining requests:", data["meta"]["rate_limit"]["remaining"])
else:
    print("Error:", data["error"]["message"])
const url = "https:///gateway/v1/stk_push.php";

const response = await fetch(url, {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "X-Api-Key": "your_api_key_here",
    },
    body: JSON.stringify({
        phone: "254712345678",
        amount: 100,
        reference: "ORD-10042",
        description: "Payment for order #10042",
    }),
});

const data = await response.json();

if (data.success) {
    console.log("CheckoutRequestID:", data.data.CheckoutRequestID);
    console.log("Remaining requests:", data.meta.rate_limit.remaining);
} else {
    console.error("Error:", data.error.message);
}
Tip

Always check the success field before accessing data. On error responses, the data field is absent and only error is present.

Platform-Specific cURL Commands

If you're testing from your terminal, use the command appropriate for your platform. The main difference is quote escaping in the JSON payload.

curl -X POST  \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: your_api_key_here" \
  -d '{
    "phone": "254712345678",
    "amount": 100,
    "reference": "ORD-10042",
    "description": "Payment for order #10042"
  }'
curl.exe -X POST  `
  -H "Content-Type: application/json" `
  -H "X-Api-Key: your_api_key_here" `
  -d '{
    "phone": "254712345678",
    "amount": 100,
    "reference": "ORD-10042",
    "description": "Payment for order #10042"
  }'

Note: PowerShell uses backticks (`) for line continuation. Use curl.exe to ensure you're using the system curl, not PowerShell's curl alias.

curl -X POST  ^
  -H "Content-Type: application/json" ^
  -H "X-Api-Key: your_api_key_here" ^
  -d "{\"phone\": \"254712345678\", \"amount\": 100, \"reference\": \"ORD-10042\", \"description\": \"Payment for order #10042\"}"

Note: CMD uses caret (^) for line continuation and requires escaped quotes in JSON (\"). For easier JSON handling, save the payload to a file and use -d @file.json.

Error Reference

All errors return a consistent JSON envelope with an HTTP status code and a descriptive message. Below is the complete list of error codes you may encounter.

Code Status Description Common Causes
400 Bad Request The request body is missing a required parameter or contains invalid data. Missing phone or amount. Amount less than 1. Invalid commandId value. Malformed JSON body.
401 Unauthorized The API key is missing, invalid, or has been revoked. No X-Api-Key or Authorization header provided. Key was deleted or deactivated in the dashboard.
403 Forbidden The API key is valid but the associated account is suspended or deleted. Account was suspended by an administrator. Account was soft-deleted.
405 Method Not Allowed The request used an HTTP method other than POST. Sending a GET, PUT, or DELETE request instead of POST.
429 Too Many Requests You have exceeded your daily rate limit for your tier. Free tier exceeded 50 requests. Basic tier exceeded 500 requests. Check X-RateLimit-Reset header for when the window resets.
502 Bad Gateway The upstream Safaricom M-PESA API returned an error or is unreachable. Safaricom Daraja API is down or experiencing issues. Invalid M-PESA configuration on the server. OAuth token generation failed.

Error response format

Every error response uses the same JSON structure regardless of the status code:

{
    "success": false,
    "error": {
        "code": 401,
        "message": "Invalid or revoked API key."
    }
}

Handling errors

Try It — STK Push

Test the STK Push endpoint directly from this page. Enter your API key and the required parameters below. This sends a real request to the gateway.

Warning

This sends a live request to the M-PESA gateway. If the server is configured with production credentials, the customer will receive an actual STK Push prompt on their phone. Use with caution.