Mista Python SDK
Official Python client for the Mista Messaging, Verify and Voice APIs. Full API reference: https://docs.mista.io
pip install mista
Requires Python 3.9+. The only dependency is httpx.
Both a synchronous (Mista) and an asynchronous (AsyncMista) client are included.
Quickstart
from mista import Mista
mista = Mista(token="...") # or set MISTA_API_TOKEN
message = mista.sms.send(to="250780000001", sender_id="YourBrand", message="Your order has shipped")
print(message["uid"], message["status"])
Get your API token in the dashboard under Settings → API. Responses are plain dicts with the
same snake_case keys as the docs (typed as TypedDicts in mista.types).
Async:
import asyncio
from mista import AsyncMista
async def main() -> None:
async with AsyncMista() as mista:
balance = await mista.account.balance()
print(balance["remaining_unit"])
asyncio.run(main())
Every method below works the same way on AsyncMista; just await it.
SMS
sms.send sends one message to one recipient. To reach several numbers, or to schedule a
send, use a campaign.
message = mista.sms.send(
to="250780000001",
sender_id="YourBrand",
message="Hello",
type="plain", # plain | unicode | voice | mms | whatsapp | viber | otp
)
latest = mista.logs.get(message["uid"]) # delivery status
Campaigns
from datetime import datetime
# Broadcast: one message, up to 10,000 numbers
mista.campaigns.bulk(
sender_id="LOYALTY",
recipients=["250780000001", "250780000002"],
message="Double points this weekend!",
schedule_time=datetime(2026, 12, 24, 9, 0), # or "2026-12-24 09:00"; account timezone
)
# Personalized: one message per number
mista.campaigns.bulk(
sender_id="LOYALTY",
recipients=[
{"to": "250780000001", "message": "Hi Alice, you have 120 points."},
{"to": "250780000002", "message": "Hi Bob, you have 45 points."},
],
)
# Everyone in one or more contact groups
mista.campaigns.send_to_groups(group_uids=["grp_uid"], sender_id="YourBrand", message="Hi!")
campaign = mista.campaigns.get("campaign_uid")
Message logs
page = mista.logs.list(start_date="2026-10-01", status="Delivered", per_page=50)
page.items # this page
page.meta.total # total matches
for message in page: # walks every remaining page (use `async for` with AsyncMista)
print(message["uid"], message["status"])
Filters: page, per_page, start_date, end_date (Y-m-d), sender_id, status, sms_type.
When nothing matches, you get an empty page.
Account
balance = mista.account.balance() # {"remaining_unit": ..., "expired_on": ...}
me = mista.account.me()
Contact groups and contacts
group = mista.contact_groups.create("Developers")
mista.contact_groups.list()
mista.contact_groups.get(group["uid"])
mista.contact_groups.update(group["uid"], "Developers KGL")
contact = mista.contacts.create(
group["uid"],
phone="250780000001",
first_name="Alice",
last_name="Uwase",
fields={"CITY": "Kigali"}, # custom fields, keyed by the group's field tag
)
mista.contacts.list(group["uid"])
mista.contacts.get(group["uid"], contact["uid"])
mista.contacts.update(group["uid"], contact["uid"], phone="250780000001", first_name="Alicia")
mista.contacts.delete(group["uid"], contact["uid"])
mista.contact_groups.delete(group["uid"]) # also deletes its contacts
Verify (OTP)
verification = mista.verify.start(to="+250780000001", channel="sms")
result = mista.verify.check(sid=verification["sid"], code="123456")
if result["verified"]:
... # signed in
else:
print(result["reason"]) # e.g. "invalid_code"; a wrong code does not raise
mista.verify.get(verification["sid"])
Voice
token = mista.voice.access_token(platform="ios")["token"]
numbers = mista.voice.numbers()
calls = mista.voice.calls.list(filter="missed", per_page=20)
call = mista.voice.calls.get("call_uid")
Errors
Every failure raises a subclass of mista.MistaError:
| Exception | When |
|---|---|
BadRequestError |
400, e.g. an invalid phone number |
AuthenticationError |
401, missing or wrong token |
PermissionDeniedError |
403 |
NotFoundError |
404 |
ValidationError |
422; field problems are in error.errors |
RateLimitError |
429 after retries; see error.retry_after |
ServerError |
5xx |
APIError |
any other API error, including a 200 whose body says "status": "error" (e.g. a contact already in the group) |
APIConnectionError / APITimeoutError |
network failure or timeout |
from mista import ValidationError
try:
mista.sms.send(to="123", sender_id="YourBrand", message="Hi")
except ValidationError as error:
for problem in error.errors:
print(problem.field, problem.message)
All API errors carry status, body (the parsed response) and headers.
Retries, timeouts and HTTP client
mista = Mista(max_retries=2, timeout=30.0)
429 Too Many Requestsis retried for every request, waiting forRetry-After.- Network errors and 5xx responses are retried for
GETonly, so a send is never duplicated. max_retries=0turns retries off.- Pass
http_client=httpx.Client(...)(orhttpx.AsyncClient) for proxies or custom transports. - Use the client as a context manager, or call
close()/await aclose(), to release connections.
Not covered
Delivery-report webhooks (configure those in the dashboard) and the retired Push API.
Development
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/pytest && .venv/bin/mypy
MISTA_API_TOKEN=... .venv/bin/python scripts/smoke.py # read-only: balance + account
License
MIT
Metadata
Release files for mista 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mista-0.1.0.tar.gz | 17.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mista-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 34.5 kB
Release files / mista-0.1.0.tar.gz
| Download URL | mista-0.1.0.tar.gz |
|---|---|
| Size | 17.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cfaad4b896c7969f4cdcff66b2a23108abb9d56f71780d6e7d3f52667730ba94
|
|
BLAKE2b-256 checksum How to use checksums |
85968f47eb11eedcdbc0382716bd7a651f2af34487fd93bca2413d0763773763
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.13
|
Release files / mista-0.1.0-py3-none-any.whl
| Download URL | mista-0.1.0-py3-none-any.whl |
|---|---|
| Size | 16.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e9c7b740e904ec7f21e0b515af103b3245fb2fc4d8434e11a14f07f85caf66c7
|
|
BLAKE2b-256 checksum How to use checksums |
26cb36f641d6c153b95c8c3b867837f3344d62760c9bfd0d4390809d9c16301b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.13
|