The basics of working with Pngme's APIs


This guide provides a high-level walk-thru of key steps in working with Pngme's APIs.

By the end of this guide, you should understand:

  1. How to get an API token to authenticate API cals
  2. How to call Pngme's risk-scoring endpoint /decision API to retrieve a loan-overdue prediction
  3. (optional) How to enforce back-off and retry to ensure enough elapsed time for near-realtime decisioning of first-time mobile app users


  • The Pngme SDK is live in your organizations mobile app
  • You have a Pngme account and can sign in at If you cannot sign in, or your login is not associated with your organization's mobile app, then please contact your organization's account administrator and/or contact Pngme support for assistance.

Let's go!

API tokens

You'll need a Pngme API token to authenticate your API requests.

API tokens are sensitive credentials since they can be used to access users' financial data. Treat them the same as other sensitive data, such as your password.

You can access an API token in one of two ways:

  1. Via the Organizations Dashboard
    1. Here, you can create, view, and revoke your Organizations' API tokens. These tokens do not have an expiration date, making for a smoother developer experience. Token management capabilities are available in our Dashboard for Organization admins and owners.
  2. Programmatic Token Retrieval:
    1. Please note: This method is being deprecated. We encourage all users to start using the new system as soon as possible. Further details about the deprecation timeline will be provided shortly.
    2. Fetch a token programmatically from the /auth API endpoint.
    3. Pass the same email and password you use at in the Authorization header: Authorization: Basic {base64(email:password)}.


Best Practice

Pngme suggests that organizations add a service account for production environments, for example [email protected]. Email addresses can be added to your organization's account in the admin dashboard. A service account email must be a valid (real) email address, capable of receiving verification emails.


The below pseudo code illustrates the procedure for calling the /decision API to get the risk score (loan overdue probability) for a mobile phone user in Nigeria.


Time-aware predictions

By default, a call to the the /decisionAPI will calculate a prediction based on all mobile phone transactions that occurred at a time up-to now(). If you are doing retroactive analysis, you may want to generate predictions based only on financial transactions up-to a prior timestamp. This is possible with an optional query parameter. See the API Reference for details.

LOGIN_EMAIL = "[email protected]"
LOGIN_PASSWORD = "strOng_p@assword!"
ORGANIZATION_NAME = "acme"  # your organization name listed in the admin dashboard
ENVIRONMENT = "production"  # if your mobile app is live, this will be production

# step 1 - fetch api token
# -----------------------
def fetch_token_api(email, password):
	url = ""
  # call GET to API here...
  response = [{
    "organization_name": "acme",
    "organization_uuid": "000-000...",
    "auth": [{"type": "production", "api_token": "eyJh...", "sdk_token": "..."}]
  api_token = None
  for org in response:
      if org["organization_name"] == ORGANIZATION_NAME:
        for environment in org["auth"]:
          if environment["type"] == ENVIRONMENT:
            api_token = environment["api_token"]
	return api_token

# step 2 - fetch loan overdue prediction
# -------------------------
def fetch_decision_api(app_user_phone, api_token):
  url = ""
  # call GET to API here...
  response = {
  "user": {...},
  "data_recency_minutes": 83,
  "risk_score": 0.53,
  "features": {
    "average_end_of_day_depository_balance_0_30": 6233.53,
    "average_end_of_day_depository_balance_0_90": 6104.64,
  return response["risk_score"]

# RUN - put it all together...
app_user_phone_number = "2343456789011"  # this is the phone number registered via the android SDK in the mobile app

# note: tokens last for 12 hours 
token = fetch_token_api(LOGIN_EMAIL, LOGIN_PASSWORD)

risk_score = fetch_decision_api(app_user_phone_number, token)

Near Real-time decisioning

Pngme builds its datasets from mobile phone SMS. For first-time mobile app users, it can sometimes take as long at 5 minutes for Pngme to capture all the SMS on the user's phone and generate a complete dataset (this time estimate is impacted by mobile carrier network speeds and the volume of SMS on the user's phone).

Pngme highly recommends engineering your product and decisioning system to provide guarantees that at least 5minutes elapses between when the Pngme SDK is invoked on a user's phone, and when you call Pngme endpoints to retrieve data. This avoids the "data race condition", simplifying integration all-around.

If your system expects to call the Risk Score /decision endpoint within 5 minutes, however, you may want to consider implementing smart backoff and retry logic when calling the /decision endpoint to ensure that at least 5 minutes elapses on all API calls.

This backoff and retry can be implemented using the "user_created_at_elapsed_minutes" key returned from the the /decision API. This key-value provides the time, in minutes, since the user was created in our system. The back-off and retry logic simply says "if the user was created in Pngme's system fewer than 5minutes ago, then wait a bit, and try calling back again".

A backoff and retry can be added to the above pseudo-code with the additional logic:

MAX_WAIT_TIME_SEC = 5 * 60  # maximum time, in seconds, system is willing to wait when fetching /decision
BACKOFF_BETWEEN_CALLS_SEC = 30  # wait time between calls
TIME_ELAPSED_THRESHOLD_MINUTES =  5  # Pngme's recommended wait period after a new user is created

# (optional) step 3 - fetch elapsed time between when user was created and now
# -------------------------
def fetch_user_created_at_elapsed_minutes(app_user_phone, api_token):
	url = "{country}/decision"
	# call GET API here
  response = {
  "user": {...},
  "user_created_at_elapsed_minutes": 3,
  "risk_score": ...,
  "features": {
    "count_loan_defaulted_events_0_90": null,
  return response["user_created_at_elapsed_minutes"]

# RUN - put it all together...
app_user_phone_number = "2343456789011"  # this is the phone number registered via the android SDK in the mobile app

# note: tokens last for 12 hours 
token = fetch_token_api(LOGIN_EMAIL, LOGIN_PASSWORD)

risk_score = None

attempts = 0
while attempts < max_attempts:
	user_created_at_elapsed_minutes = fetch_user_created_at_elapsed_minutes(app_user_phone_number, api_token)
  if user_created_at_elapsed_minutes > TIME_ELAPSED_THRESHOLD_MINUTES:
  	risk_score = fetch_decision_api(app_user_phone_number, token)
	attempts = attempts + 1