Skip to main content

VirtualSMS Python SDK

Python SDK for the VirtualSMS Consumer API — SMS verification, phone number rental, and activation management.

PyPI License Python

Requirements

  • Python 3.9 or higher
  • No external dependencies (uses standard library only)

Installation

pip install virtualsmslabs-python-sdk

Quick Start

from virtualsms import VirtualSMSClient, ActivationStatus

client = VirtualSMSClient("YOUR_API_KEY", "https://api.virtualsms.de")

# Check balance
balance = client.get_balance()
print(f"Balance: ${balance.balance:.2f}")

# Order a WhatsApp number in Brazil
number = client.get_number("wa", 73, max_price=2.00)
print(f"Number: {number.phone_number} (ID: {number.activation_id})")

# Set status to ready (SMS sent)
client.set_status(number.activation_id, ActivationStatus.READY)

# Poll for SMS code
status = client.get_status(number.activation_id)
if status.code is not None:
    print(f"SMS code: {status.code}")
    # Complete the activation
    client.set_status(number.activation_id, ActivationStatus.COMPLETE)

API Reference

Client Constructor

client = VirtualSMSClient(
    api_key="YOUR_API_KEY",           # Your API key (required)
    base_url="https://api.virtualsms.de",  # API base URL (optional)
    transport=None,                    # Optional custom transport
)

Methods

Account

get_balance() -> BalanceResponse

Returns the current account balance.

balance = client.get_balance()
print(balance.balance)  # 10.50

Information & Pricing

get_countries(pool_provider=None) -> dict

Returns all available countries.

get_services_list(country=None, lang=None) -> dict

Returns available services for a country.

get_operators(country, pool_provider=None) -> list

Returns available mobile operators for a country.

get_prices(service=None, country=None, pool_provider=None) -> dict

Returns pricing data organized by country and service.

get_prices_extended(service=None, country=None, free_price=None, pool_provider=None) -> dict

Returns extended pricing with price tiers.

get_prices_verification(service=None, pool_provider=None) -> dict

Returns pricing in inverted format (service → country).

get_numbers_status(country, operator=None, pool_provider=None) -> dict

Returns available phone quantity per service.

get_top_countries_by_service(service) -> list

Returns top 10 countries for a service, ranked by purchase share and success rate.

Ordering Numbers

get_number(service, country, **options) -> NumberResponse

Orders a phone number. Returns text format response.

number = client.get_number(
    service="wa",
    country=73,
    max_price=2.00,
    operator="claro",
    forward=True,
)
print(number.activation_id)   # 123
print(number.phone_number)    # 447777777777

Options:

Parameter Type Description
max_price float Maximum price willing to pay
operator str Mobile operator filter
phone_exception str Phone prefixes to exclude (comma-separated)
forward bool Enable call forwarding
activation_type int Activation type: 0=SMS, 1=number, 2=voice
language str Language for voice activation
use_cashback bool Use cashback balance first
user_id str End-user ID for tracking
ref str Referral ID
pool_provider str Pool provider: alpha, prime, gamma, zeta
get_number_v2(service, country, **options) -> dict

Same as get_number but returns JSON with additional fields. Supports order_id for idempotency.

Activation Management

set_status(id, status) -> str

Changes activation status.

from virtualsms import ActivationStatus

client.set_status(activation_id, ActivationStatus.READY)     # 1 - SMS sent
client.set_status(activation_id, ActivationStatus.RETRY)     # 3 - Request another SMS
client.set_status(activation_id, ActivationStatus.COMPLETE)  # 6 - Finish
client.set_status(activation_id, ActivationStatus.CANCEL)    # 8 - Cancel
get_status(id) -> StatusResponse

Returns activation status in text format.

status = client.get_status(activation_id)
print(status.status)   # STATUS_OK, STATUS_WAIT_CODE, STATUS_CANCEL
print(status.code)     # 123456 (None if not yet received)
get_status_v2(id) -> dict

Returns activation status in JSON format with SMS/call details.

get_active_activations() -> list

Returns all currently active activations.

check_extra_activation(id) -> dict

Checks if a number is available for reactivation.

get_extra_activation(id) -> NumberResponse

Creates an extra activation on a previously used number.

Notifications

get_notifications() -> dict

Returns user notifications including penalties, low balance alerts, and admin messages.

Constants

ActivationStatus

ActivationStatus.READY     # 1 - SMS has been sent to the number
ActivationStatus.RETRY     # 3 - Request another SMS code
ActivationStatus.COMPLETE  # 6 - Finish activation
ActivationStatus.CANCEL    # 8 - Cancel activation

PoolProvider

PoolProvider.ALPHA  # 'alpha'
PoolProvider.PRIME  # 'prime'
PoolProvider.GAMMA  # 'gamma'
PoolProvider.ZETA   # 'zeta'

Error Handling

The SDK raises typed exceptions for all API errors. Each error code maps to a specific exception class:

from virtualsms import (
    VirtualSMSClient,
    VirtualSMSException,
    AuthenticationException,
    InsufficientBalanceException,
    NoNumbersException,
    ValidationException,
    ActivationException,
    RateLimitException,
    RateLimitInfo,
    ServerException,
)

try:
    number = client.get_number("wa", 73)
except AuthenticationException:
    # BAD_KEY, BANNED, PURCHASE_RESTRICTED, SERVICE_RESTRICTED
    pass
except InsufficientBalanceException:
    # NO_BALANCE
    pass
except NoNumbersException:
    # NO_NUMBERS
    pass
except ValidationException:
    # WRONG_SERVICE, WRONG_COUNTRY, BAD_ACTION, BAD_STATUS, NO_PRICES, INVALID_PROVIDER
    pass
except ActivationException:
    # NO_ACTIVATION, WRONG_ACTIVATION_ID, EARLY_CANCEL_DENIED, RENEW_ACTIVATION_NOT_AVAILABLE
    pass
except RateLimitException as e:
    # CONCURRENT_LIMIT or BANNED on 429 — check e.retry_after, e.rate_limit_limit, e.rate_limit_remaining
    pass
except ServerException:
    # ERROR_SQL, unknown errors
    pass

Error Code Reference

Error Code Exception HTTP Status Description
BAD_KEY AuthenticationException 401 Invalid API key
BANNED RateLimitException 429 Rate limited (temporary)
BANNED AuthenticationException 403 Account banned or IP blocked (permanent)
PURCHASE_RESTRICTED AuthenticationException 403 User restricted from purchasing
SERVICE_RESTRICTED AuthenticationException 403 Service restricted for account
NO_BALANCE InsufficientBalanceException 402 Insufficient balance
NO_NUMBERS NoNumbersException 404 No numbers available
WRONG_SERVICE ValidationException 400 Invalid service code
WRONG_COUNTRY ValidationException 400 Invalid country ID
BAD_ACTION ValidationException 400 Invalid action
BAD_STATUS ValidationException 400 Invalid status code
NO_PRICES ValidationException 400 No pricing data available
INVALID_PROVIDER ValidationException 400 Invalid pool provider
NO_ACTIVATION ActivationException 404 Activation not found
WRONG_ACTIVATION_ID ActivationException 400 Invalid activation ID
EARLY_CANCEL_DENIED ActivationException 400 Cannot cancel within 5 minutes
RENEW_ACTIVATION_NOT_AVAILABLE ActivationException 400 Number not available for reactivation
CONCURRENT_LIMIT RateLimitException 429 Too many concurrent activations
ERROR_SQL ServerException 500 Internal server error

Each exception instance has:

  • error_code — the API error code string
  • http_status — the HTTP status code from the response
  • retry_after — (only on RateLimitException) seconds to wait before retrying
  • rate_limit_limit — (only on RateLimitException) the rate limit ceiling from X-RateLimit-Limit (nullable)
  • rate_limit_remaining — (only on RateLimitException) remaining requests from X-RateLimit-Remaining (nullable)

Rate Limiting

The API returns rate limit headers on every response. The SDK captures these automatically.

# After any API call, check the current rate limit status
balance = client.get_balance()
info = client.get_rate_limit_info()

if info is not None:
    print(f"Limit: {info.limit}, Remaining: {info.remaining}")

get_rate_limit_info() returns RateLimitInfo | None:

  • None before any request has been made, or if the response lacked rate limit headers
  • RateLimitInfo(limit=int, remaining=int) after a successful or failed request

When a request hits the rate limit (HTTP 429), a RateLimitException is thrown with the rate limit details embedded:

try:
    number = client.get_number("wa", 73)
except RateLimitException as e:
    print(f"Retry after {e.retry_after}s")
    print(f"Limit: {e.rate_limit_limit}, Remaining: {e.rate_limit_remaining}")

Note: An HTTP 429 with body BANNED means you are rate-limited (temporary). An HTTP 403 with body BANNED means your account or IP is actually banned (permanent). The SDK distinguishes these automatically.

Tracking Headers

The SDK sends anonymous tracking headers with every request for analytics and debugging:

Header Value Privacy
X-SDK-Version 1.1.0 SDK version string
X-SDK-Language python SDK language
X-SDK-Machine-Id SHA-256 hash of platform.platform() + sys.implementation.name (truncated to 32 chars) Irreversible hash — no hostname or IP exposed
X-SDK-Timestamp ISO 8601 UTC timestamp Request time

No personally identifiable information is transmitted. The machine ID is a one-way hash and cannot be reversed to identify the source machine.

Custom Transport

By default, the SDK uses urllib.request for HTTP requests. You can provide your own transport implementation:

from virtualsms import VirtualSMSClient, Transport, Response

class MyTransport(Transport):
    def send(self, url: str, headers: dict) -> Response:
        # Your HTTP implementation
        return Response(status_code=200, body="ACCESS_BALANCE:10.50")

client = VirtualSMSClient("API_KEY", "https://api.example.com", transport=MyTransport())

Examples

See the examples/ directory:

Testing

pip install pytest
pytest

License

MIT — see LICENSE.

Links

Download files

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

Source Distribution

virtualsmslabs_python_sdk-1.1.0.tar.gz (14.9 kB view details)

Uploaded Source

Built Distribution

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

virtualsmslabs_python_sdk-1.1.0-py3-none-any.whl (13.2 kB view details)

Uploaded Python 3

File details

Details for the file virtualsmslabs_python_sdk-1.1.0.tar.gz.

File metadata

File hashes

Hashes for virtualsmslabs_python_sdk-1.1.0.tar.gz
Algorithm Hash digest
SHA256 aeb686a908bc417870b10dd5e7fae87621d0c8589a9ff60405b5a01e18c5b089
MD5 7fbb9c7056d45a92fc4d6fc86a1c4afd
BLAKE2b-256 e83f8a62ebf8d23c6f846a882733a767e768bc2bf0b3b1bbc451c10db2f42101

See more details on using hashes here.

File details

Details for the file virtualsmslabs_python_sdk-1.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for virtualsmslabs_python_sdk-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 985392de8c42096c12fb7cb4e9d5b0eaf93d9f0447c616cff562bfe33d01f5ad
MD5 830d7d70c80093187611f7c81fb10c1a
BLAKE2b-256 abba71263cf67570834f28d78f35be0683e99988ad2ea67558515a778fd9eeca

See more details on using hashes here.

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