Skip to main content

STK Query

User stories

  • As an online merchant, I want customers to pay with M-Pesa STK Push so they can pay on their phones without leaving my site or app.
  • As a developer, I want to start an STK Push and check its status so I can update orders even if callbacks are late or missing.
  • As a support agent, I want to look up STK Push results to troubleshoot problems and confirm payments before issuing refunds or fulfilling orders.

Parameters

ParameterTypeDescription
BusinessShortCoderequired
int
IntegerMerchant shortcode (PayBill or BuyGoods).
CheckoutRequestIDrequired
str
StringThe CheckoutRequestID returned when the STK Push was initiated.
Passkey
str
StringShortcode passkey. If provided, the SDK can derive Password + Timestamp.
Password
str
StringBase64(Shortcode + Passkey + Timestamp). Provide this together with Timestamp if not using Passkey.
Timestamp
str
StringTimestamp used when generating Password. Format: YYYYMMDDHHMMSS.

Overview

Use STK Query to retrieve the processing result for an STK Push when the callback is missing or to cross-check a callback payload.

Example (MpesaClient, Sync)

PYTHON
# Build and send a query using the high-level client.
import os
from dotenv import load_dotenv
from mpesakit import MpesaClient
load_dotenv()
# Initialize the client
client = MpesaClient(
consumer_key=os.getenv("MPESA_CONSUMER_KEY"),
consumer_secret=os.getenv("MPESA_CONSUMER_SECRET"),
environment="sandbox",
)
# Perform the STK query
result = client.stk_query(
business_short_code=123456,
checkout_request_id="ws_CO_20250101_ABC123",
passkey="your_passkey_here", # or provide password + timestamp
)
if result.is_successful:
print("Transaction processed:", result.ResultDesc)
else:
print("Status:", result.ResponseDescription, "| Code:", result.ResponseCode)

Example (AsyncMpesaClient, Async)

PYTHON
import os
import asyncio
from dotenv import load_dotenv
from mpesakit import AsyncMpesaClient
load_dotenv()
async def main():
async with AsyncMpesaClient(
consumer_key=os.getenv("MPESA_CONSUMER_KEY"),
consumer_secret=os.getenv("MPESA_CONSUMER_SECRET"),
environment="sandbox",
) as client:
result = await client.stk_query(
business_short_code=123456,
checkout_request_id="ws_CO_20250101_ABC123",
passkey="your_passkey_here",
)
if result.is_successful:
print("Transaction processed:", result.ResultDesc)
else:
print("Status:", result.ResponseDescription, "| Code:", result.ResponseCode)
asyncio.run(main())

Response shape & interpretation

  • The query response is a StkPushQueryResponse Pydantic object that contains identifiers and status fields such as MerchantRequestID, CheckoutRequestID, ResponseCode, ResponseDescription, ResultCode and ResultDesc. This shape is identical whether it came from MpesaClient or AsyncMpesaClient.
  • Interpreting results:
    • ResponseCode of 0 generally means the query request was accepted.
    • ResultCode of 0 indicates the transaction was successful. Other ResultCodes convey errors, timeouts or user cancellation.

Response Schema

StkPushQueryResponse — returned by client.stk_query(...) / stk_service.query(...)

ParameterTypeDescription
MerchantRequestIDrequired
str
StringGlobal unique identifier for the original payment request.
CheckoutRequestIDrequired
str
StringGlobal unique identifier of the checkout transaction request being queried.
ResponseCoderequired
int | str
Integer/String0 means the query request itself was accepted and processed.
ResponseDescriptionrequired
str
StringAcknowledgment message about the query submission status.
ResultCoderequired
int | str
Integer/String0 means the underlying STK Push transaction itself was successful. Other codes mean the transaction failed, was cancelled, or timed out.
ResultDescrequired
str
StringHuman readable description of the transaction result.
PYTHON
# Example handling snippet (pseudo)
resp = stk_service.query(request=q) # or: await stk_service.query(request=q)
if not resp.is_successful:
# The query itself failed/was rejected — inspect resp.ResponseDescription
handle_query_error(resp)
elif str(resp.ResultCode) == "0":
# The underlying STK Push payment succeeded
handle_payment_success(resp)
else:
# Payment failed, was cancelled, or timed out — inspect resp.ResultDesc
handle_payment_failure(resp)

Validating a query response with process_stk_query_callback

Both MpesaClient and AsyncMpesaClient expose a process_stk_query_callback helper. It's the same validation logic used for STK callbacks, applied to the query response — useful when you've stored the raw dict (e.g. from a database or log) and want to re-validate it into a typed StkPushQueryResponse object rather than trusting the dict shape.

Python
# Works the same whether 'client' is MpesaClient or AsyncMpesaClient,
# since process_stk_query_callback only validates/parses — it does no I/O.
raw_dict = response.model_dump(mode="json")
validated = client.process_stk_query_callback(raw_dict)
print(validated.ResultDesc)

Next steps