The PayFlow API lets you initiate M-Pesa STK Push payments, query transaction statuses, and retrieve transaction history — all via a simple REST interface. Every response is JSON, every request requires your API credentials in the headers.
Base URL
All endpoints are relative to this base URL. Always use HTTPS — plain HTTP requests will be rejected.
Every request must include the following HTTP headers. Find your credentials in your PayFlow dashboard under API Keys.
-
X-API-Key Your unique public API key
-
X-API-Secret Your private API secret — keep this confidential
-
Content-Type: application/json Required for all POST requests
Example header
POST /api/v2/stkpush.php HTTP/1.1
Host: payflow.top
X-API-Key: pk_live_xxxxxxxxxxxx
X-API-Secret: sk_live_xxxxxxxxxxxx
Content-Type: application/json
Pick your language and make your first API call in under a minute.
<?php
function initiatePayment(string $apiKey, string $apiSecret, array $payload): array {
$ch = curl_init('https://payflow.top/api/v2/stkpush.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . $apiKey,
'X-API-Secret: ' . $apiSecret,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($payload),
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
$result = initiatePayment(
apiKey: 'pk_live_xxxxxxxxxxxx',
apiSecret: 'sk_live_xxxxxxxxxxxx',
payload: [
'payment_account_id' => 17,
'phone' => '254712345678',
'amount' => 100,
'reference' => 'ORDER_001',
'description' => 'Product payment',
]
);
if ($result['success']) {
echo 'STK sent! Checkout ID: ' . $result['checkout_request_id'];
} else {
echo 'Error: ' . $result['message'];
}
?>
async function initiatePayment({ apiKey, apiSecret, payload }) {
const res = await fetch('https://payflow.top/api/v2/stkpush.php', {
method: 'POST',
headers: {
'X-API-Key': apiKey,
'X-API-Secret': apiSecret,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}
// Usage
const result = await initiatePayment({
apiKey: 'pk_live_xxxxxxxxxxxx',
apiSecret: 'sk_live_xxxxxxxxxxxx',
payload: {
payment_account_id: 17,
phone: '254712345678',
amount: 100,
reference: 'ORDER_001',
description: 'Product payment',
},
});
console.log(result.checkout_request_id);
curl -X POST https://payflow.top/api/v2/stkpush.php \
-H "X-API-Key: pk_live_xxxxxxxxxxxx" \
-H "X-API-Secret: sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"payment_account_id": 17,
"phone": "254712345678",
"amount": 100,
"reference": "ORDER_001",
"description": "Product payment"
}'
Initiates an M-Pesa STK Push prompt on the customer's phone. The customer sees a PIN entry dialog and confirms the payment.
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| payment_account_id | integer | Required | Your PayFlow payment account ID |
| phone | string | Required | Customer phone in 2547XXXXXXXX format |
| amount | float | Required | Amount in KES (minimum 1) |
| reference | string | Optional | Your internal order / reference ID |
| description | string | Optional | Short description shown to customer |
Responses
{
"success": true,
"message": "STK Push sent successfully",
"checkout_request_id": "ws_CO_191020231234567890",
"merchant_request_id": "29115-34620561-1",
"transaction_id": 4821
}
{
"success": false,
"message": "Invalid API credentials",
"error_code": "AUTH_FAILED"
}
Query the current status of a transaction using the checkout_request_id returned by the STK Push endpoint.
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| checkout_request_id | string | Required | The ID returned by the STK Push endpoint |
Success response
{
"success": true,
"status": "completed",
"amount": 100,
"phone": "254712345678",
"mpesa_receipt": "RAK74J8YAZ",
"transaction_date": "2024-10-19 14:32:11"
}
Possible status values: pending, completed, failed, cancelled. Poll every 3–5 seconds until the status is no longer pending.
Retrieve a paginated list of all transactions for your account.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| page | integer | 1 | Page number |
| limit | integer | 10 | Results per page (max 100) |
| status | string | — | Filter by status: completed, failed, pending |
Success response
{
"success": true,
"data": [
{
"id": 4821,
"phone": "254712345678",
"amount": 100,
"status": "completed",
"mpesa_receipt": "RAK74J8YAZ",
"reference": "ORDER_001",
"created_at": "2024-10-19 14:32:11"
}
],
"pagination": {
"current_page": 1,
"per_page": 10,
"total_items": 342,
"total_pages": 35
}
}
Test the API directly from your browser using real credentials. No setup required.
API Playground
Requests are sent to the live PayFlow API — use real credentials
All errors return a JSON object with success: false, a human-readable message, and an error_code for programmatic handling.
| HTTP | Error Code | Description |
|---|---|---|
| 401 | AUTH_FAILED | API key or secret is invalid or missing |
| 400 | INVALID_PHONE | Phone number format is incorrect |
| 400 | INVALID_AMOUNT | Amount is below minimum or non-numeric |
| 404 | ACCOUNT_NOT_FOUND | The provided payment_account_id does not exist |
| 429 | RATE_LIMITED | Too many requests — slow down and retry |
| 500 | MPESA_ERROR | Upstream M-Pesa Daraja API error |