MarioSMS

Receive SMS Codes with Python: MarioSMS API Tutorial

Updated · MarioSMS team

If you’re testing sign-up flows, running QA against OTP screens, or automating account provisioning you’re allowed to automate, you’ll want to receive SMS codes from code rather than a dashboard. The MarioSMS REST API is a small, predictable surface: rent a number, poll until the code arrives, done. This guide walks through the whole loop in Python with nothing but requests.

Prerequisites

  • A MarioSMS account with some balance (create one here)
  • Your API key, from your profile in the app
  • Python 3.9+ and pip install requests

The full endpoint reference lives at app.mariosms.com/api-docs. Everything below authenticates with the X-API-Key header.

Step 1: Check your balance

A quick sanity check that your key works:

import requests

BASE = "https://app.mariosms.com/api/v1"
HEADERS = {"X-API-Key": "your_api_key_here"}

r = requests.get(f"{BASE}/balance", headers=HEADERS)
r.raise_for_status()
print(r.json())  # {"balance": 100.0}

Step 2: Rent a number

An activation is one number rented for one verification. You name the service and the country; the response includes the phone number and an expiry:

r = requests.post(
    f"{BASE}/activation",
    headers=HEADERS,
    json={"country": "us", "service": "tg"},  # Telegram, US number
)
r.raise_for_status()
activation = r.json()
print(activation["phone"])  # e.g. "12025551234"

Service and country codes come from GET /api/v1/services and GET /api/v1/countries, and GET /api/v1/prices returns live prices before you commit.

Step 3: Poll for the code

Paste the number into the sign-up form you’re testing, then poll the activation until its status flips to received:

import time

def wait_for_code(activation_id: str, timeout_s: int = 300) -> str | None:
    deadline = time.monotonic() + timeout_s
    while time.monotonic() < deadline:
        r = requests.get(f"{BASE}/activation/{activation_id}", headers=HEADERS)
        r.raise_for_status()
        data = r.json()
        if data["status"] == "received":
            return data["smsCode"]  # full text in data["smsText"]
        time.sleep(5)
    return None

code = wait_for_code(activation["id"])
print("OTP:", code)

A 5-second interval is plenty; most codes land in well under a minute.

Step 4: Cancel if you change your mind

If no SMS has arrived yet, cancelling refunds the full price to your balance:

r = requests.post(f"{BASE}/activation/{activation['id']}/cancel", headers=HEADERS)
print(r.json())  # {"message": "activation cancelled", "refund": 1.32}

You don’t need to handle the no-SMS case yourself: if nothing arrives within the activation window, the platform cancels and refunds automatically. You are only ever charged for codes that actually arrive.

Putting it together

The whole integration is: POST /activation → poll GET /activation/:id → read smsCode. There’s also a compatibility API for tools that already speak the classic handler_api.php protocol; see the API docs for both.

Automation is fine where the underlying activity is fine: QA, app testing, accounts you’re entitled to create. Bulk fake-account farming isn’t; see the acceptable use policy.