Skip to main content

onesms

PyPI CI Docs Python License: MIT

A typed Python client for the 1sms.az SMS API, with both synchronous and asynchronous interfaces.

Documentation: https://martian56.github.io/onesms-sdk/

1sms.az API reference: https://1sms.az/api-docs

Features

  • Sync (Client) and async (AsyncClient) clients with the same surface.
  • OTP, notification, and advertising sends, plus balance and delivery-status lookups.
  • Fully typed responses as frozen dataclasses; ships a py.typed marker.
  • Automatic retries with exponential backoff and jitter, honoring Retry-After.
  • Idempotency-key support so retried sends are never duplicated.
  • A precise exception hierarchy mapped from the API's HTTP status and error codes.
  • Webhook signature verification (HMAC-SHA256) with timestamp tolerance.
  • No hard dependencies beyond httpx.

Requirements

  • Python 3.10 or newer
  • A 1sms.az API key

Installation

pip install onesms

Or add it to a uv project:

uv add onesms

To install the latest unreleased code, point at the repository instead:

pip install git+https://github.com/martian56/onesms-sdk.git

Quickstart

from onesms import Client

with Client("1sk_your_api_key", sender_name="YourSender") as client:
    result = client.send_otp("994501234567", "Your code is 123456")
    print(result.message_id, result.cost, result.balance)

Async

import asyncio

from onesms import AsyncClient


async def main() -> None:
    async with AsyncClient("1sk_your_api_key") as client:
        result = await client.send_otp("994501234567", "Your code is 123456")
        print(result.message_id)


asyncio.run(main())

Usage

Check the balance

balance = client.balance()
print(balance.balance)
print(balance.apis.otp, balance.apis.bulk, balance.apis.advertising)

Send an OTP

result = client.send_otp("994501234567", "Your code is 123456")

Send notifications

A single recipient returns per-message ids; two or more recipients are dispatched as a bulk task and return a task_id you can poll.

single = client.send_notification(["994501234567"], "Order shipped")
print(single.message_ids)

bulk = client.send_notification(
    ["994501234567", "994502223344", "994553334455"],
    "Weekend promotion",
)
print(bulk.task_id, bulk.sent_count, bulk.failed_count)
for item in bulk.rejected:
    print(item.number, item.reason)

Send advertising

result = client.send_advertising(["994501234567"], "Big discounts this week")

Look up delivery status

from onesms import DeliveryStatus

status = client.message_status("message-id")
print(status.status_code, status.status_text)

if status.is_final:
    if status.status is DeliveryStatus.DELIVERED:
        print("delivered")
    else:
        print("not delivered:", status.status_text)

Poll a bulk task

from onesms import Channel

task = client.task_status("task-id", channel=Channel.NOTIFICATION)
print(task.sent_count, task.failed_count)
for message in task.messages:
    print(message.phone, message.status_text, message.is_final)

Idempotency

Pass an idempotency_key to make a send safe to retry. The API remembers the key for 24 hours and will not send the same request twice. When a key is supplied, the client also retries transient network and server errors automatically.

client.send_otp(
    "994501234567",
    "Your code is 123456",
    idempotency_key="order-4821-otp",
)

Retries

GET requests and 429 Too Many Requests responses are always retried. Network failures and 5x responses on writes are retried only when an idempotency key is present, so a send is never silently duplicated. Backoff is exponential with jitter and respects a Retry-After header when the API provides one. Configure the ceiling with max_retries:

client = Client("1sk_your_api_key", max_retries=5, timeout=15.0)

Error handling

Every API failure raises a subclass of OneSmsError.

from onesms import (
    Client,
    InsufficientBalanceError,
    OneSmsAPIError,
    OneSmsConnectionError,
    OneSmsValidationError,
    RateLimitError,
)

try:
    client.send_notification(["994501234567"], "Hello")
except OneSmsValidationError as exc:
    print("invalid input:", exc)
except InsufficientBalanceError as exc:
    print("top up:", exc.required, "have:", exc.balance)
except RateLimitError as exc:
    print("retry after:", exc.retry_after)
except OneSmsConnectionError as exc:
    print("network problem:", exc)
except OneSmsAPIError as exc:
    print(exc.status_code, exc.error_code, exc.message)
Exception Raised for
OneSmsValidationError Client-side checks before a request is sent
OneSmsConnectionError Network failures that could not be retried
BadRequestError 400
AuthenticationError 401
InsufficientBalanceError 402 (exposes required, balance)
PermissionDeniedError 403
NotFoundError 404
ConflictError 409
RateLimitError 429 (exposes retry_after)
ServerError 5xx (exposes msm_errno, msm_err_text, hint)
OneSmsAPIError Base class for any API error

Webhooks

1sms.az signs delivery webhooks with an HMAC-SHA256 signature over the request timestamp and raw body. Verify it with your API secret before trusting the payload.

from onesms import WebhookEvent, verify_signature

signature = request.headers["X-1sms-Signature"]
timestamp = request.headers["X-1sms-Timestamp"]
raw_body = request.get_data()

if not verify_signature("your_api_secret", timestamp, raw_body, signature):
    raise ValueError("invalid signature")

event = WebhookEvent.from_payload(request.get_json())
if event.is_final:
    print(event.message_id, event.status_text)

verify_signature rejects timestamps outside a five-minute window by default. Widen it with tolerance_seconds if needed.

Development

uv sync
uv run ruff check .
uv run ruff format --check .
uv run mypy
uv run pytest

License

MIT

Metadata

Release files for onesms 0.1.1

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

Source distribution (sdist)

Source distribution for onesms 0.1.1
File Size Uploaded
onesms-0.1.1.tar.gz 79.8 kB Details

Built distribution (wheel)

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

Total release size: 93.9 kB

Release files / onesms-0.1.1.tar.gz

Download URL onesms-0.1.1.tar.gz
Size 79.8 kB
Tags Source
SHA-256 checksum
How to use checksums
fff192b9b4f01b5323d95534f352ba91078d1cdc8475cd289828a0e45ee8b41d
BLAKE2b-256 checksum
How to use checksums
a2c662bafdbefb831f57564b9c83e70114c2b47aa8ec056a6370532c790b73fd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 14, 2026.

Transparency log

Release files / onesms-0.1.1-py3-none-any.whl

Download URL onesms-0.1.1-py3-none-any.whl
Size 14.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
33865989996610e83d064b4d627fe3e200570878d82ced98228de3d5df76ef6b
BLAKE2b-256 checksum
How to use checksums
ac62c43cb94e3e4056f1e80998ad3bdbfc3da20a08f7e7d3378b69cbec29643a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 14, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.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