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.jsonthat 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
- Open https://mrxsim.com and create an account.
- Top up with Crypto (USDT / supported networks).
- Profile → Get API KEY (shown once at create/regenerate).
- Export
MRXSIM_API_KEYor paste into gitignoredconfig.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_keyonget_number()— pass the same string across your own retry attempts (e.g. after catchingMrxsimTimeoutError) 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) andMrxsimServiceUnavailableError.retry_after(503, MRXSIM's own capacity backpressure — distinct from a per-key rate limit) — both carry the server's realRetry-Aftervalue in seconds (Noneif 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 inmrxsim.exceptionssince the ambiguous -purchase safety work landed, but was never re-exported from the top-levelmrxsimpackage or listed in__all__— a caller following this README's own documented pattern (from mrxsim import MrxsimAmbiguousPurchaseError) would get anImportError. 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'sGET /api/v1/balanceendpoint 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_FIELDSwidened to match the real serverOrderPublicOutexactly — real bug found and fixed:balance_usdt,cancel_allowed,cancel_available_at, andcancel_remaining_seconds(all real fields the server has sent on everyget_number()/get_sms()/cancel()response since 2026-09-01) were being silently stripped by this client's own sanitizer before this fix — contradictingget_sms()'s andcancel()'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 anIdempotency-Keyheader; 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/newMrxsimServiceUnavailableErrornow expose a realretry_after(seconds) parsed from the server'sRetry-Afterheader,Noneif absent.MrxsimServiceUnavailableErroris new — raised on503(MRXSIM's own capacity backpressure, distinct from the per-key429).- 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
- Site: https://mrxsim.com
- Docs: https://mrxsim.com/docs
- Issues: GitHub
© 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)
| File | Size | Uploaded | |
|---|---|---|---|
| mrxsim-1.0.7.tar.gz | 37.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|