Skip to main content
This guide walks you through installing the required library, building the signing helper, logging in to obtain a session token, keeping that token fresh, and making authenticated API calls — all in Python.

Installation

Install the requests library if you do not already have it:
pip install requests
All other modules used in this guide (hashlib, urllib.parse, json, threading) are part of the Python standard library.

The signing library (bpm_lib.py)

Create a file called bpm_lib.py with the following content. The rest of your code will import from this file.
bpm_lib.py
import hashlib
import requests
import urllib.parse
import json

sign_key = '1234567890qwertyuiopasdfghjklzxc'
login_url = 'https://bpm.mioesim.com/api_order/login'

def creat_sign(value):
    """
    Accepts a list of "key=value" strings (with "sign" already excluded),
    sorts them alphabetically, concatenates them, appends the secret key,
    URL-encodes the result, and returns the uppercase MD5 hex digest.
    """
    value.sort()
    MD5 = "".join(value) + sign_key
    encoded_str = urllib.parse.quote(MD5, safe='')
    sign_value = hashlib.md5(encoded_str.encode('utf-8')).hexdigest().upper()
    return sign_value

def log_in(phonenumber, password):
    """
    Calls POST /api_order/login and returns the session token on success,
    or an empty string if authentication fails.
    """
    token = ''
    message = [
        'password=%s' % password,
        'phonenumber=%s' % phonenumber,
    ]
    sign = creat_sign(message)

    response = requests.get(
        url=login_url,
        params={
            'password':    password,
            'phonenumber': phonenumber,
            'sign':        sign,
        }
    )

    if response.status_code == 200:
        value = json.loads(response.text)
        if value['code'] == 1:
            token = value['data']['token']
        else:
            print('log_in code error: %d' % value['code'])
    else:
        print('log_in server error: %d' % response.status_code)

    return token
Here is what each part does:
  • sign_key — your secret key, used as a suffix before hashing. Keep this value private.
  • creat_sign(value) — takes a list of "key=value" strings, sorts them, concatenates them, appends the secret key, percent-encodes the full string with urllib.parse.quote, and returns the uppercase MD5 hex digest.
  • log_in(phonenumber, password) — builds the signed parameter list, calls the login endpoint, parses the JSON response, and returns the token on success.
Never include sign_key in client-side code, public repositories, or logs. Treat it like a password.

Logging in

Import bpm_lib and call log_in with your credentials:
main.py
import bpm_lib

phonenumber = 'YOUR_PHONE_NUMBER'
password    = 'YOUR_PASSWORD'

token = bpm_lib.log_in(phonenumber, password)

if token:
    print(f"Login successful, token: {token}")
else:
    print("Login failed. Check your credentials.")
A successful login returns a non-empty token string. Pass this token to every subsequent API call.

Token refresh

Tokens are valid for 2 hours from the time of issue. The official demo schedules a refresh after 324,000 seconds — roughly 90 minutes — using threading.Timer, giving a 30-minute buffer before expiry.
token-refresh.py
import threading
import bpm_lib

token = ''

def refresh_token():
    """Re-authenticate and reschedule the next refresh."""
    global token
    token = bpm_lib.log_in('YOUR_PHONE_NUMBER', 'YOUR_PASSWORD')
    if token:
        print(f"Token refreshed: {token}")
        schedule_refresh()
    else:
        print("Token refresh failed. Retrying in 60 seconds.")
        threading.Timer(60, refresh_token).start()

def schedule_refresh():
    # Refresh after 90 minutes (5400 seconds) to stay within the 2-hour window
    threading.Timer(5400, refresh_token).start()

# Initial login
token = bpm_lib.log_in('YOUR_PHONE_NUMBER', 'YOUR_PASSWORD')
if token:
    schedule_refresh()
Store token in a location accessible to all request functions, and always read the current value rather than caching it in a local variable across long-running operations.
The 324,000-second interval in the official demo script is intentional — it is approximately 90 minutes, not 2 hours. Using a value slightly shorter than the actual expiry prevents your token from becoming invalid mid-request.

Making authenticated API calls

After login, include both token and sign on every request. Build the parameter list the same way log_in does — format each parameter as "key=value", exclude sign, pass the list to creat_sign, then append sign to the request. The example below calls GET /api_esim/getSkus to retrieve available eSIM packages:
get-skus.py
import requests
import json
import bpm_lib

def get_skus(token):
    params_list = [
        'token=%s' % token,
    ]
    sign = bpm_lib.creat_sign(params_list)

    response = requests.get(
        url='https://bpm.mioesim.com/api_esim/getSkus',
        params={
            'token': token,
            'sign':  sign,
        }
    )

    if response.status_code == 200:
        return json.loads(response.text)
    else:
        print('get_skus server error: %d' % response.status_code)
        return None

skus = get_skus(token)
print(json.dumps(skus, indent=2))
Add any additional parameters to both params_list (for signing) and the params dict (for the request), keeping the two in sync.
Replace 'YOUR_PHONE_NUMBER' and 'YOUR_PASSWORD' with your actual MIOeSIM account credentials before running the code.