Skip to main content

Official Python SDK for the VoiceTel REST API

Project description

📞 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 73 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 hierarchyRateLimitError, 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.
  • 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-only, or hybrid modes; rotate passwords.

🧪 Battle-Tested

  • 108 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(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. 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(npa=201)
client.lookups CNAM & LRN dips c.lookups.lrn("2015551234", ani="2012548000")
client.messaging SMS/MMS, 10DLC brands & campaigns 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 — retry in {e.body.get('retryAfter', '?')}s")

⚡ 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/info
  • account/mrc (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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

voicetel_api-2.2.10.tar.gz (29.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

voicetel_api-2.2.10-py3-none-any.whl (42.0 kB view details)

Uploaded Python 3

File details

Details for the file voicetel_api-2.2.10.tar.gz.

File metadata

  • Download URL: voicetel_api-2.2.10.tar.gz
  • Upload date:
  • Size: 29.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for voicetel_api-2.2.10.tar.gz
Algorithm Hash digest
SHA256 5c6c7e1008bad78109bb41b997eb3ec2941f78a16af46ba618d601fae4ebdb3c
MD5 8a660b63e4a83db8d634bc2fe5b2d627
BLAKE2b-256 8c77537de87ef94b0b939bb2c5218de15bca895fd881c39ea36edb021cee95ae

See more details on using hashes here.

Provenance

The following attestation bundles were made for voicetel_api-2.2.10.tar.gz:

Publisher: publish.yml on voicetel/python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file voicetel_api-2.2.10-py3-none-any.whl.

File metadata

  • Download URL: voicetel_api-2.2.10-py3-none-any.whl
  • Upload date:
  • Size: 42.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for voicetel_api-2.2.10-py3-none-any.whl
Algorithm Hash digest
SHA256 77f238d28b945b6dc61e3819d4c326e74dd82eecfce40f9f39265392eba9ffe3
MD5 8d1e81d90488b683466872fc99941d6c
BLAKE2b-256 b756fabc45af79f52c65ac5b8e464ea1763d5f0e76207c565fb34c7ff771fe69

See more details on using hashes here.

Provenance

The following attestation bundles were made for voicetel_api-2.2.10-py3-none-any.whl:

Publisher: publish.yml on voicetel/python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page