Skip to main content

📞 VoiceTel Python SDK

The official Python client for the VoiceTel REST API — provision numbers, place orders, validate e911, send messages, and manage your account, all with type-safe, async-ready Python.

Version Python License Coverage Typed

📚 Table of Contents

✨ Features

🛡️ Strongly Typed End-to-End

  • Pydantic v2 models for every one of the 79 API operations — request bodies validated before they leave your machine, responses validated when they arrive.
  • mypy-strict clean. Full type coverage, including async, generics, and discriminated unions.
  • Autocomplete everywhere. Your IDE knows the shape of every field — no more guessing what's in result["data"]["numbers"].

⚡ Sync + Async, Same Surface

  • Client for blocking calls, AsyncClient for await-based async — identical method names, identical return types.
  • Built on httpx — supports HTTP/2, connection pooling, and custom transports if you need them.

🔁 Production-Grade Transport

  • Automatic retry with exponential backoff on 429 / 5xx — honors Retry-After headers.
  • Configurable timeouts per client or per call.
  • Bearer auth managed for you; password→key exchange handled by client.login().
  • Structured exception hierarchy — RateLimitError, AuthenticationError, NotFoundError, etc. all subclasses of ApiError you can catch broadly or narrowly.

📞 Complete API Coverage

  • Numbers — list, get, add, remove, route, translate, CNAM, LIDB, fax, forward, SMS, messaging campaigns, port-out PIN, account moves.
  • Account — profile, sub-accounts, CDRs, credits, payments, MRC, registration, password recovery.
  • e911 — record provisioning, address validation, lookup, removal.
  • Gateways — list, create, update, delete, view bound numbers.
  • Messaging — SMS & MMS sending, message history, 10DLC brand and campaign registration, per-number messaging state, and messaging health (account overview, per-number live check, spam-blocked samples, alert-email setting).
  • Lookups — CNAM and LRN dips.
  • iNumbering — inventory search, coverage queries, number orders, port-in submissions, port-out availability checks.
  • Support — ticket create / read / update / delete, threaded messages, replies.
  • ACL — IP allowlist management with structured 409 conflict bodies.
  • Authentication — switch between Digest, IP, Lax (Digest | IP), or Strict (Digest + IP) modes; rotate passwords.

🧪 Battle-Tested

  • 109 unit tests at 100% statement + branch coverage.
  • Integration test suite that runs read-only operations against api.voicetel.com — gated by env vars, safe for CI.
  • No mocks-pretending-to-be-tests. Mocked HTTP layer with respx, real Pydantic validation on every fixture so spec drift gets caught.

📦 Clean Distribution

  • Zero codegen footprint — every byte hand-written.
  • Built with hatchling; ships as wheel + sdist.
  • py.typed marker — downstream type checkers see your imports natively.

🚀 Installation

pip install voicetel-api

Requires Python 3.10 or later. Tested against 3.10, 3.11, 3.12, and 3.13.

🏁 Quickstart

from voicetel import Client

with Client() as c:
    # Exchange username + password for an API key (one-time per session)
    c.login(username="1000000001", password="hunter2")

    # Typed responses — your IDE knows what `me` is.
    me = c.account.get()
    print(f"Balance: ${me.cash:.2f}  |  Caller ID: {me.callerId}")

    # List your numbers
    for n in c.numbers.list().numbers:
        print(f"{n.number}  route={n.route}  cnam={n.cnam}  sms={n.smsEnabled}")

Or, if you already have an API key:

from voicetel import Client

with Client(api_key="32hex...") as c:
    coverage = c.inumbering.coverage(count_by="npanxx", loc_state="NJ")
    for bucket in coverage.coverage:
        print(f"{bucket.npa}-{bucket.nxx}: {bucket.count} TNs available")

🔑 Authentication

Every endpoint requires Authorization: Bearer <apikey> except POST /v2.2/account/api-key (which exchanges username + password for a fresh key), POST /v2.2/accounts (public sign-up) and POST /v2.2/account/recovery (password recovery). Client.login() (and AsyncClient.login()) handles the exchange and installs the returned key on the transport.

Re-fetch the API key after any password change — the old one is invalidated.

Don't have credentials yet? Get them at voicetel.com/docs/api/v2.2/credentials.

from voicetel import Client

with Client() as c:
    key = c.login(username="1000000001", password="hunter2")
    # `key` is the new 32-hex bearer; the client already has it installed.

🗺️ Resource Reference

Resource Operations Example
client.account Profile, CDR, credits, payments, MRC, signup, recovery, sub-accounts c.account.cdr(start=t1, end=t2)
client.acl IP allowlist (CIDR entries) c.acl.add(AclModifyRequest(acl=[...]))
client.authentication SIP/HTTP auth mode + password c.authentication.update(AuthPutRequest(authType=1))
client.e911 Records, address validation, provisioning c.e911.validate(E911AddressRequest(...))
client.gateways Termination routes c.gateways.list()
client.inumbering Inventory, orders, port-ins c.inumbering.search_inventory(tn_mask="201")
client.lookups CNAM & LRN dips c.lookups.lrn("2015551234", ani="2012548000")
client.messaging SMS/MMS, 10DLC brands & campaigns, messaging health c.messaging.send(MessageSendRequest(...))
client.numbers All operations on TNs on the account c.numbers.assign_campaign("2015551234", ...)
client.support Tickets, replies, attachments c.support.create(TicketCreateRequest(...))

Every method that takes a request body accepts a typed Pydantic model imported from voicetel.models:

from voicetel import Client
from voicetel.models import (
    MessageSendRequest,
    NumberCampaignAssignRequest,
    PortSubmitRequest,
)

with Client(api_key=key) as c:
    sent = c.messaging.send(MessageSendRequest(
        fromNumber="2012548000",
        toNumber="2015551234",
        text="Your code is 482917",
    ))
    print(f"Sent: {sent.id}  ({sent.parts} segment(s))")

    c.numbers.assign_campaign(
        "2015551234",
        NumberCampaignAssignRequest(campaignId="CABC123"),
    )

🚨 Error Handling

All HTTP errors raise subclasses of voicetel.ApiError. Catch broadly or narrowly:

Status Exception
400 BadRequestError
401 AuthenticationError
403 PermissionDeniedError
404 NotFoundError
409 ConflictError
429 RateLimitError
5xx ServerError
other ApiError
from voicetel import Client, NotFoundError, RateLimitError

with Client(api_key=key) as c:
    try:
        n = c.numbers.get("9999999999")
    except NotFoundError:
        print("That number isn't on your account.")
    except RateLimitError as e:
        print(f"Slow down — {e}")

⚡ Async Support

Identical surface to Client, with await-based methods:

import asyncio
from voicetel import AsyncClient

async def fetch_state(numbers: list[str]) -> None:
    async with AsyncClient(api_key="...") as c:
        state = await c.messaging.numbers_state(numbers=numbers)
        for s in state.numbers:
            print(f"{s.number}: network={s.network} campaign={s.campaign}")

asyncio.run(fetch_state(["2015551234", "2015551235"]))

⏱️ Rate Limits

These endpoints are limited to 6 requests per hour per IP:

  • account
  • account/recurring-charges (client.account.recurring_charges())
  • account/cdr (client.account.cdr())
  • account/api-key (client.login())

The SDK automatically retries 429 responses with Retry-After honored, up to max_retries (default 2). To bump it:

Client(api_key=key, max_retries=4, timeout=60.0)

🛠️ Development

git clone https://github.com/voicetel/python-sdk
cd python-sdk
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

# Unit tests (fast, no network)
pytest tests/unit

# 100% coverage gate
pytest tests/unit --cov --cov-fail-under=100

# Lint + type-check
ruff check src tests
mypy src

# Integration tests (live api.voicetel.com, read-only)
cp .env.example .env  # fill in VOICETEL_USERNAME / VOICETEL_PASSWORD
pytest tests/integration

# Build wheel + sdist
python -m build
twine check dist/*

📖 API Documentation

🙌 Contributors

Contributions welcome. Open an issue describing the change you want to make, or send a pull request against main.

💖 Sponsors

Sponsor Contribution
VoiceTel Communications Primary development and production hosting

📄 License

This project is licensed under the MIT License — see the LICENSE file for details.

Metadata

Release files for voicetel-api 2.2.16

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

Source distribution (sdist)

Source distribution for voicetel-api 2.2.16
File Size Uploaded
voicetel_api-2.2.16.tar.gz 33.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for voicetel-api 2.2.16
File Interpreter ABI Platform
voicetel_api-2.2.16-py3-none-any.whl Python 3 none any Details

Total release size: 79.3 kB

Release files / voicetel_api-2.2.16.tar.gz

Download URL voicetel_api-2.2.16.tar.gz
Size 33.1 kB
Tags Source
SHA-256 checksum
How to use checksums
b626484a0893459a3c49722aee7708b04cdabad218709b57b445dbfb11fc491b
BLAKE2b-256 checksum
How to use checksums
73de5265525dfc0f6fbf26e58cdeb8e274fd4b34dd72e545ba1af5203e62c257
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 Oct 17, 2026.

Transparency log

Release files / voicetel_api-2.2.16-py3-none-any.whl

Download URL voicetel_api-2.2.16-py3-none-any.whl
Size 46.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
43ac678511766a7caeb816fe58783aee1b03ef69e50543db05b3dfa884da0a8b
BLAKE2b-256 checksum
How to use checksums
f32bac27276f167c424bb2ed1a0208e5f884e02d1a7d4c7d8dc6de40b908b79f
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 Oct 17, 2026.

Transparency log

Release history Release notifications | RSS feed

2.2.17

2 release files

This release

2.2.16 This release

2 release files

2.2.15

2 release files

2.2.14

2 release files

2.2.10

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