Skip to main content

mrxsim

Official Python client for MRXSIM.COM — purchase virtual numbers and retrieve SMS OTPs for every catalog app/social service (Telegram, WhatsApp, Google, Instagram, Discord, Snapchat, Other (SMS), and more).

Brand: MRXSIM · Domain: https://mrxsim.com · Package: mrxsim


Security first

  • No hardcoded API keys in library source or examples.
  • Prefer environment variable MRXSIM_API_KEY.
  • Or load a local config.json that is gitignored.
  • Never commit live keys. Revoke immediately if exposed.

Install

pip install mrxsim

From this repository (editable):

pip install -e .

Quick start (environment variable)

export MRXSIM_API_KEY="mrxs_your_key_here"   # Linux / macOS
# setx MRXSIM_API_KEY "mrxs_your_key_here"  # Windows (new shell)
from mrxsim import Client

with Client() as client:
    # Discover the catalog — no country/service needed to browse.
    countries = client.list_countries()
    services = client.list_services("england")
    options = client.list_options(country="england", service="telegram")

    order = client.get_number(
        country="england", service="telegram",
        operator=options[0]["operator"],  # a stable MRXSIM option code — never a provider ID
        idempotency_key="my-own-stable-id-for-this-purchase",  # optional, see "Retry safety" below
    )
    print(order["phone_number"], order["id"])

    sms = client.wait_for_sms(order["id"])
    print(sms["sms_code"])

    # client.cancel(order["id"])  # if you need to cancel before the OTP arrives

One-shot purchase + OTP:

from mrxsim import Client

with Client(country="egypt", service="whatsapp") as client:
    result = client.buy_and_wait()
    print(result["phone_number"], result["sms_code"])

Quick start (config file)

cp config.example.json config.json
# edit config.json → set api_key, country, service
from mrxsim import Client

client = Client.from_config("config.json")
order = client.get_number()
sms = client.wait_for_sms(order["id"])
client.close()

MRXSIM_API_KEY overrides api_key in the file when set.


Get your API key

  1. Open https://mrxsim.com and create an account.
  2. Top up with Crypto (USDT / supported networks).
  3. Profile → Get API KEY (shown once at create/regenerate).
  4. Export MRXSIM_API_KEY or paste into gitignored config.json.

API surface

Method Endpoint Client method
GET /api/v1/balance Client.balance()
GET /api/v1/sms/catalog Client.list_countries()
GET /api/v1/sms/services?country=… Client.list_services()
GET /api/v1/sms/operators?country=…&service=… Client.list_options()
POST /api/v1/get_number Client.get_number()
GET /api/v1/get_sms?order_id=… Client.get_sms() / wait_for_sms()
POST /api/v1/cancel Client.cancel()

Catalog discovery (list_countries/list_services/list_options) is public MRXSIM data — no purchase, no debit — and does not consume your purchase-tier rate budget. An operator value from list_options() is a stable, MRXSIM-owned option code (e.g. "opt1", "opt2" for a service with more than one genuinely distinct price tier) — never an upstream provider identifier — pass it straight to get_number().

list_options() lists every option MRXSIM has ever had genuine backing for on that country/service, not just what's purchasable right now — a row can carry available: False / reliability: "Temporarily unavailable" and still be a real, normally priced product; don't assume every row is instantly purchasable, and don't drop one from your own UI/cache just because it's temporarily unavailable (MRXSIM doesn't either — it resumes automatically once real backing returns). Buying such a row fails cleanly with a 409 (reason == "no_honorable_candidate"), never a silent substitution or a 500. Compare price_usdt numerically, not as a string — the same price can render with a different number of decimal places between calls (e.g. "0.26" vs. "0.2600").

Header on every call:

X-API-Key: YOUR_KEY

Public order fields (zero-knowledge)

Successful responses expose retail fields only — aligned with the MRXSIM server OrderPublicOut contract. This is the complete PUBLIC_ORDER_FIELDS allowlist the client enforces client-side (see "Retry safety" and the changelog below for the newer fields in context):

Field Meaning
id MRXSIM order UUID
phone_number Assigned number
service / country Catalog codes
status Order status
price Retail USDT price charged
sms_code OTP when received
substituted_operator Set when Smart Auto-Fallback delivered a different (never worse) operator than requested
balance_usdt Wallet balance remaining after this purchase
cancel_allowed Whether this order is currently cancelable at all
cancel_available_at ISO timestamp cancellation becomes available, if in cooldown
cancel_remaining_seconds Server-computed seconds left in a cancel cooldown, if any

Internal operational metrics and upstream routing identifiers are not part of the public API. The client also sanitizes responses client-side if any such fields ever appear (defense in depth).

Docs: https://mrxsim.com/docs

Retry safety

Client never automatically retries get_number() — not on a timeout, not on a network error, not on a 503. A hidden automatic retry on a purchase call is exactly how a client library accidentally causes a real second charge for one logical purchase, so this client simply doesn't do it. What it gives you instead, so you can retry safely when you choose to:

  • idempotency_key on get_number() — pass the same string across your own retry attempts (e.g. after catching MrxsimTimeoutError) and MRXSIM guarantees at most one real purchase for that key, returning the original order on a repeat instead of buying a second number. Omit it for a normal, independent purchase.
  • MrxsimRateLimitError.retry_after (429) and MrxsimServiceUnavailableError.retry_after (503, MRXSIM's own capacity backpressure — distinct from a per-key rate limit) — both carry the server's real Retry-After value in seconds (None if the response didn't include one). Prefer it over a fixed client-side delay:
import time
from mrxsim import Client, MrxsimRateLimitError, MrxsimServiceUnavailableError

client = Client()
try:
    order = client.get_number(idempotency_key="my-own-stable-id-for-this-purchase")
except (MrxsimRateLimitError, MrxsimServiceUnavailableError) as exc:
    if exc.retry_after:
        time.sleep(exc.retry_after)
        order = client.get_number(idempotency_key="my-own-stable-id-for-this-purchase")
    else:
        raise

Plain read calls (get_sms(), status polling inside wait_for_sms()) are naturally safe to retry on your own — they never mutate anything — so this client doesn't add speculative auto-retry logic there either; keep your own retry loop as simple or as sophisticated as your application needs.

MrxsimAmbiguousPurchaseError — never retry this one with a new key

For the rare case where a real charge/vendor purchase may already have happened but MRXSIM's own record of it failed to save, get_number() raises MrxsimAmbiguousPurchaseError (a subclass of MrxsimAPIError, so a generic except MrxsimAPIError still catches it — but it needs the opposite handling from every other error above):

from mrxsim import Client, MrxsimAmbiguousPurchaseError

client = Client()
try:
    order = client.get_number(idempotency_key="my-own-stable-id-for-this-purchase")
except MrxsimAmbiguousPurchaseError as exc:
    # Do NOT retry with a new idempotency_key — that risks a genuine second
    # real purchase. Retry with the EXACT SAME key, or hold and contact
    # support with exc.purchase_intent_id.
    order = client.get_number(idempotency_key="my-own-stable-id-for-this-purchase")

exc.purchase_intent_id is MRXSIM's own internal correlation id (never provider-derived, safe to log) — keep it if you need to contact support instead of retrying immediately.


Changelog

Docs-only clarification (2026-09-04, MRXSIM Major Rework Program, Agent 20, "Docs Final Update" pass) — no code or version change. This pass runs after the owner-directed Telegram/Website "always clickable" UX corrections and the final red-team revalidation, and re-verified this package's docstrings against the actual current server code rather than re-stating the prior pass's wording. Those two UX corrections were a Telegram-bot/Website presentation-layer fix only — they never touched this Developer API's wire contract, which had no client-side gate to begin with. One small wording fix made: get_number()'s docstring described a stale quote as "no longer honorable" (an internal-jargon phrase); reworded to plain English ("no longer valid"). The literal, correct reason value the server actually returns (no_honorable_candidate) is unchanged and still documented verbatim.

Docs-only clarification (2026-09-04, MRXSIM Major Rework Program, Agent 20) — no code or version change. list_options()'s docstring and the catalog-discovery note above were corrected/clarified: the previous reliability example values ("High", "Unrated") did not match the labels the server actually sends ("Available" / "Limited availability" / "Temporarily unavailable"), and neither this README nor the docstring explained that an available: False row is a real, permanently listed product rather than one about to vanish — the "always-visible catalog" contract. Also added: a note to compare price_usdt numerically, never as a string (the same price can render with a different decimal-place count between calls). See docs/rework_agent20_docs_sdk_findings.md in the main MRXSIM repository for the full reasoning; nothing here changes this package's behavior, so the version stays 1.0.6.

1.0.7

  • MrxsimAmbiguousPurchaseError — real gap found and closed: this exception class has existed in mrxsim.exceptions since the ambiguous -purchase safety work landed, but was never re-exported from the top-level mrxsim package or listed in __all__ — a caller following this README's own documented pattern (from mrxsim import MrxsimAmbiguousPurchaseError) would get an ImportError. Now correctly exported; see "MrxsimAmbiguousPurchaseError — never retry this one with a new key" below for the full retry-safety guidance this exception exists for. No behavior change — _raise_for_status() was already raising this exception type correctly; only its discoverability was broken.
  • User-Agent bumped to mrxsim-python/1.0.7.

1.0.6

  • Client.balance() — real gap found and closed: the server's GET /api/v1/balance endpoint has existed since 2026-09-01, but this client never exposed a dedicated method for it. Returns {"balance_usdt": "..."}; cheap, idempotent, does not consume your purchase-tier rate budget.
  • PUBLIC_ORDER_FIELDS widened to match the real server OrderPublicOut exactly — real bug found and fixed: balance_usdt, cancel_allowed, cancel_available_at, and cancel_remaining_seconds (all real fields the server has sent on every get_number()/get_sms()/cancel() response since 2026-09-01) were being silently stripped by this client's own sanitizer before this fix — contradicting get_sms()'s and cancel()'s own documented return values. Every prior version back to 1.0.3 was affected. If you're on an older version, upgrade — no code changes needed on your end, you'll simply start receiving fields the server was already sending.
  • User-Agent bumped to mrxsim-python/1.0.6.

1.0.3

  • get_number(idempotency_key=...) — optional, forwarded as an Idempotency-Key header; a retry with the same key (and the same country/service/operator) returns the original order instead of a second purchase. Omitted by default — this client never generates or reuses a key on your behalf.
  • MrxsimRateLimitError/new MrxsimServiceUnavailableError now expose a real retry_after (seconds) parsed from the server's Retry-After header, None if absent. MrxsimServiceUnavailableError is new — raised on 503 (MRXSIM's own capacity backpressure, distinct from the per-key 429).
  • New "Retry safety" section above — documents that this client never automatically retries a purchase call, by design.
  • list_countries()/list_services()/list_options() — real catalog discovery, added 2026-08-31 (this version was never published while this gap existed, so it's folded into 1.0.3 rather than bumping to 1.0.4). Calls MRXSIM's existing public catalog endpoints directly — same canonical data Website/Mini App/Bot render, zero provider identity ever included.
  • cancel(order_id) — added 2026-08-31, alongside a real server-side developer-API cancel endpoint (there was none before). Reuses the same proven cancel/refund logic the web/bot surface already uses — idempotent, provider-neutral errors, refunds only on a confirmed provider outcome.
  • User-Agent bumped to mrxsim-python/1.0.3.

1.0.2

  • Sanitize client responses to strip internal operational metrics and upstream routing identifiers.
  • Harden public documentation for white-label / zero-knowledge API alignment.
  • User-Agent bumped to mrxsim-python/1.0.2.

1.0.1

  • Document and enforce zero-knowledge public order fields.
  • User-Agent bumped to mrxsim-python/1.0.1.

Examples

export MRXSIM_API_KEY="…"
python examples/buy_number_example.py
python examples/get_otp_example.py <order_id>

Development

python -m venv .venv
# Windows: .venv\Scripts\activate
source .venv/bin/activate
pip install -e ".[dev]"
pytest
python -m build

Support

© MRXSIM · Secure SMS Infrastructure

Metadata

Release files for mrxsim 1.0.7

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mrxsim 1.0.7
File Size Uploaded
mrxsim-1.0.7.tar.gz 37.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mrxsim 1.0.7
File Interpreter ABI Platform
mrxsim-1.0.7-py3-none-any.whl Python 3 none any Details

Total release size: 61.6 kB

Release files / mrxsim-1.0.7.tar.gz

Download URL mrxsim-1.0.7.tar.gz
Size 37.4 kB
Tags Source
SHA-256 checksum
How to use checksums
6375cd54722ab5d8f4fa21db7c0f27b758659958a1b6ec58daa1c4c76e74e5df
BLAKE2b-256 checksum
How to use checksums
da21d597048f0b9ce5d02b416d1b639091096933672f927d24eb920aa88ca9d7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.2

Release files / mrxsim-1.0.7-py3-none-any.whl

Download URL mrxsim-1.0.7-py3-none-any.whl
Size 24.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
800f965a072d3afe986850ac92295a03bdb0b1c9306958f9fec16b7734857ea4
BLAKE2b-256 checksum
How to use checksums
6e7325469986111a6493c7d3c1893b18dacfc61480e1ac868e6e620e4da6a292
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.2

Release history Release notifications | RSS feed

1.0.9

2 release files

1.0.8

2 release files

This release

1.0.7 This release

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page