Skip to main content

pyPhoneApi

Async Python library for phone number lookup, validation and formatting.

Built on top of asyncio, aiohttp and phonenumbers. No manually maintained country/calling-code tables, no blocking calls on the event loop, no synchronous public API.

Installation

pip install pyPhoneApi

Requirements

  • Python >= 3.11
  • aiohttp
  • phonenumbers

Features

  • Fully async public API (await phone.lookup(...))
  • Reuses a single aiohttp.ClientSession across calls, never creates one per request
  • Country detection, ISO 3166-1 alpha-2 code, calling code, national and international formatting powered by phonenumbers
  • Validity and possibility checks (is_valid, is_possible)
  • Number type detection (MOBILE, FIXED_LINE, VOIP, ...)
  • Localized country names (language="en", language="ru", ...)
  • Typed exceptions instead of generic errors
  • Safe under concurrent load with asyncio.gather
  • No emoji anywhere in output, logs, or exceptions
  • Fully typed, ships with py.typed

Quick example

import asyncio

from pyPhoneApi import PhoneApi


async def main() -> None:
    async with PhoneApi() as phone:
        result = await phone.lookup("+380643732627")

        print(f"Country: {result.country}")
        print(f"Country code: {result.country_code}")
        print(f"Calling code: {result.calling_code}")
        print(f"Number: {result.number}")
        print(f"National: {result.national}")
        print(f"Valid: {result.is_valid}")
        print(f"Possible: {result.is_possible}")


asyncio.run(main())

Output:

Country: Ukraine
Country code: UA
Calling code: +380
Number: +380643732627
National: 0643732627
Valid: True
Possible: True

Using async with

from pyPhoneApi import PhoneApi

async with PhoneApi() as phone:
    result = await phone.lookup("+380643732627")

Manual session lifecycle

from pyPhoneApi import PhoneApi

phone = PhoneApi()

await phone.start()

result = await phone.lookup("+380643732627")

await phone.close()

Repeated calls to lookup() reuse the same aiohttp.ClientSession. Calling lookup() or start() after close() raises ClientClosedError.

Shortcut function

from pyPhoneApi import lookup

result = await lookup("+380643732627")

Error handling

from pyPhoneApi import PhoneApi
from pyPhoneApi import InvalidPhoneNumberError

async with PhoneApi() as phone:
    try:
        result = await phone.lookup("invalid-number")
    except InvalidPhoneNumberError as e:
        print(e)

Output:

Invalid phone number

Concurrent requests

import asyncio

from pyPhoneApi import PhoneApi


async def main() -> None:
    async with PhoneApi() as phone:
        results = await asyncio.gather(
            phone.lookup("+380643732627"),
            phone.lookup("+37255555555"),
            phone.lookup("+491234567890"),
        )

        for result in results:
            print(result.country, result.number)


asyncio.run(main())

Only one aiohttp.ClientSession is created and shared across all of these lookups, and the event loop is never blocked.

Localized country names

result = await phone.lookup("+380643732627", language="ru")
print(result.country)
Украина
result = await phone.lookup("+380643732627", language="en")
print(result.country)
Ukraine

PhoneResult

@dataclass(frozen=True, slots=True)
class PhoneResult:
    number: str
    international: str
    national: str
    country: str
    country_code: str
    calling_code: str
    is_valid: bool
    is_possible: bool
    number_type: str
Field Description
number Number in E.164 format
international Number in international format
national Number in national format, without separators
country Localized country name
country_code ISO 3166-1 alpha-2 country code (e.g. UA)
calling_code Calling code with leading + (e.g. +380)
is_valid Whether the number is a valid number for its region
is_possible Whether the number is a possible number for its region
number_type MOBILE, FIXED_LINE, FIXED_LINE_OR_MOBILE, TOLL_FREE, PREMIUM_RATE, VOIP, or UNKNOWN

to_dict():

{
    "number": "+380643732627",
    "international": "+380643732627",
    "national": "0643732627",
    "country": "Ukraine",
    "country_code": "UA",
    "calling_code": "+380",
    "is_valid": True,
    "is_possible": True,
    "number_type": "MOBILE"
}

Exceptions

Exception Raised when
PhoneApiError Base exception for all library errors
InvalidPhoneNumberError The number is not a valid or possible phone number
NumberParseError The number could not be parsed at all
UnsupportedCountryError No region could be determined for the number
ClientClosedError lookup() or start() is called after close()

API reference

class PhoneApi(*, language: str = "en")

  • await phone.start() -> None — creates the underlying aiohttp.ClientSession if one does not already exist.
  • await phone.close() -> None — closes the session and marks the client as closed.
  • await phone.lookup(number: str, *, language: str | None = None) -> PhoneResult — parses and analyzes a phone number.
  • async with PhoneApi() as phone: — starts the session on enter, closes it on exit.
  • phone.is_closed -> bool — whether the client has been closed.

async def lookup(number: str, *, language: str = "en") -> PhoneResult

Shortcut that creates a PhoneApi, performs a single lookup, and closes the session automatically.

Running tests

pip install -e ".[test]"
pytest

License

MIT

Download files

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

Source Distribution

pyphoneapi-0.1.0.tar.gz (5.9 kB view details)

Uploaded Source

Built Distribution

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

pyphoneapi-0.1.0-py3-none-any.whl (6.5 kB view details)

Uploaded Python 3

File details

Details for the file pyphoneapi-0.1.0.tar.gz.

File metadata

  • Download URL: pyphoneapi-0.1.0.tar.gz
  • Upload date:
  • Size: 5.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.9

File hashes

Hashes for pyphoneapi-0.1.0.tar.gz
Algorithm Hash digest
SHA256 91ae3a1053332c7695eac87a1d8dbaa6454435ad9af8cbc8fb81faa9210160e6
MD5 e1fde977b2bcea6ab15928feef573469
BLAKE2b-256 01f2c3c302453447657d7829f7b6101e825af3e9b762699ac27d7ebaba94ab35

See more details on using hashes here.

File details

Details for the file pyphoneapi-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pyphoneapi-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 6.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.9

File hashes

Hashes for pyphoneapi-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c6aa0f1f5706134c04f8c7c772b7a9c3dbae8d0ed81556b8a6812f9bae40f240
MD5 8d1c31d498adc0e0880e31358c509bfe
BLAKE2b-256 52fe44f98273b46209e852835c26197e84dbd2d98070448600f5127701579ab0

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Supported by

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