virtualsmslabs 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- virtualsmslabs-1.0.0/.gitignore +20 -0
- virtualsmslabs-1.0.0/LICENSE +21 -0
- virtualsmslabs-1.0.0/PKG-INFO +352 -0
- virtualsmslabs-1.0.0/README.md +326 -0
- virtualsmslabs-1.0.0/examples/balance.py +10 -0
- virtualsmslabs-1.0.0/examples/full_workflow.py +53 -0
- virtualsmslabs-1.0.0/examples/order_number.py +19 -0
- virtualsmslabs-1.0.0/pyproject.toml +41 -0
- virtualsmslabs-1.0.0/src/virtualsms/__init__.py +34 -0
- virtualsmslabs-1.0.0/src/virtualsms/client.py +296 -0
- virtualsmslabs-1.0.0/src/virtualsms/constants.py +15 -0
- virtualsmslabs-1.0.0/src/virtualsms/exceptions.py +126 -0
- virtualsmslabs-1.0.0/src/virtualsms/parser.py +160 -0
- virtualsmslabs-1.0.0/src/virtualsms/response.py +22 -0
- virtualsmslabs-1.0.0/src/virtualsms/transport.py +41 -0
- virtualsmslabs-1.0.0/tests/__init__.py +0 -0
- virtualsmslabs-1.0.0/tests/test_client.py +271 -0
- virtualsmslabs-1.0.0/tests/test_exceptions.py +112 -0
- virtualsmslabs-1.0.0/tests/test_parser.py +255 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 VirtualSMS Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: virtualsmslabs
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python SDK for the VirtualSMS Consumer API — SMS verification, phone number rental, and activation management.
|
|
5
|
+
Project-URL: Homepage, https://virtualsms.de
|
|
6
|
+
Project-URL: Documentation, https://virtualsms.de/docs/sdk
|
|
7
|
+
Project-URL: Repository, https://github.com/VirtualSMSLabs/python-sdk
|
|
8
|
+
Project-URL: Issues, https://github.com/VirtualSMSLabs/python-sdk/issues
|
|
9
|
+
Author-email: VirtualSMS Labs <contact@virtualsms.de>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: otp,phone,sms,sms-activate,verification,virtual,virtualsms
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Communications :: Telephony
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# VirtualSMS Python SDK
|
|
28
|
+
|
|
29
|
+
Python SDK for the VirtualSMS Consumer API — SMS verification, phone number rental, and activation management.
|
|
30
|
+
|
|
31
|
+
[](https://pypi.org/project/virtualsms/)
|
|
32
|
+
[](LICENSE)
|
|
33
|
+
[](https://www.python.org/)
|
|
34
|
+
|
|
35
|
+
## Requirements
|
|
36
|
+
|
|
37
|
+
- Python 3.9 or higher
|
|
38
|
+
- No external dependencies (uses standard library only)
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install virtualsms
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from virtualsms import VirtualSMSClient, ActivationStatus
|
|
50
|
+
|
|
51
|
+
client = VirtualSMSClient("YOUR_API_KEY", "https://api.virtualsms.de")
|
|
52
|
+
|
|
53
|
+
# Check balance
|
|
54
|
+
balance = client.get_balance()
|
|
55
|
+
print(f"Balance: ${balance.balance:.2f}")
|
|
56
|
+
|
|
57
|
+
# Order a WhatsApp number in Brazil
|
|
58
|
+
number = client.get_number("wa", 73, max_price=2.00)
|
|
59
|
+
print(f"Number: {number.phone_number} (ID: {number.activation_id})")
|
|
60
|
+
|
|
61
|
+
# Set status to ready (SMS sent)
|
|
62
|
+
client.set_status(number.activation_id, ActivationStatus.READY)
|
|
63
|
+
|
|
64
|
+
# Poll for SMS code
|
|
65
|
+
status = client.get_status(number.activation_id)
|
|
66
|
+
if status.code is not None:
|
|
67
|
+
print(f"SMS code: {status.code}")
|
|
68
|
+
# Complete the activation
|
|
69
|
+
client.set_status(number.activation_id, ActivationStatus.COMPLETE)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## API Reference
|
|
73
|
+
|
|
74
|
+
### Client Constructor
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
client = VirtualSMSClient(
|
|
78
|
+
api_key="YOUR_API_KEY", # Your API key (required)
|
|
79
|
+
base_url="https://api.virtualsms.de", # API base URL (optional)
|
|
80
|
+
transport=None, # Optional custom transport
|
|
81
|
+
)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Methods
|
|
85
|
+
|
|
86
|
+
#### Account
|
|
87
|
+
|
|
88
|
+
##### `get_balance() -> BalanceResponse`
|
|
89
|
+
|
|
90
|
+
Returns the current account balance.
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
balance = client.get_balance()
|
|
94
|
+
print(balance.balance) # 10.50
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### Information & Pricing
|
|
98
|
+
|
|
99
|
+
##### `get_countries(pool_provider=None) -> dict`
|
|
100
|
+
|
|
101
|
+
Returns all available countries.
|
|
102
|
+
|
|
103
|
+
##### `get_services_list(country=None, lang=None) -> dict`
|
|
104
|
+
|
|
105
|
+
Returns available services for a country.
|
|
106
|
+
|
|
107
|
+
##### `get_operators(country, pool_provider=None) -> list`
|
|
108
|
+
|
|
109
|
+
Returns available mobile operators for a country.
|
|
110
|
+
|
|
111
|
+
##### `get_prices(service=None, country=None, pool_provider=None) -> dict`
|
|
112
|
+
|
|
113
|
+
Returns pricing data organized by country and service.
|
|
114
|
+
|
|
115
|
+
##### `get_prices_extended(service=None, country=None, free_price=None, pool_provider=None) -> dict`
|
|
116
|
+
|
|
117
|
+
Returns extended pricing with price tiers.
|
|
118
|
+
|
|
119
|
+
##### `get_prices_verification(service=None, pool_provider=None) -> dict`
|
|
120
|
+
|
|
121
|
+
Returns pricing in inverted format (service → country).
|
|
122
|
+
|
|
123
|
+
##### `get_numbers_status(country, operator=None, pool_provider=None) -> dict`
|
|
124
|
+
|
|
125
|
+
Returns available phone quantity per service.
|
|
126
|
+
|
|
127
|
+
##### `get_top_countries_by_service(service) -> list`
|
|
128
|
+
|
|
129
|
+
Returns top 10 countries for a service, ranked by purchase share and success rate.
|
|
130
|
+
|
|
131
|
+
#### Ordering Numbers
|
|
132
|
+
|
|
133
|
+
##### `get_number(service, country, **options) -> NumberResponse`
|
|
134
|
+
|
|
135
|
+
Orders a phone number. Returns text format response.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
number = client.get_number(
|
|
139
|
+
service="wa",
|
|
140
|
+
country=73,
|
|
141
|
+
max_price=2.00,
|
|
142
|
+
operator="claro",
|
|
143
|
+
forward=True,
|
|
144
|
+
)
|
|
145
|
+
print(number.activation_id) # 123
|
|
146
|
+
print(number.phone_number) # 447777777777
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Options:**
|
|
150
|
+
|
|
151
|
+
| Parameter | Type | Description |
|
|
152
|
+
|-----------|------|-------------|
|
|
153
|
+
| `max_price` | `float` | Maximum price willing to pay |
|
|
154
|
+
| `operator` | `str` | Mobile operator filter |
|
|
155
|
+
| `phone_exception` | `str` | Phone prefixes to exclude (comma-separated) |
|
|
156
|
+
| `forward` | `bool` | Enable call forwarding |
|
|
157
|
+
| `activation_type` | `int` | Activation type: 0=SMS, 1=number, 2=voice |
|
|
158
|
+
| `language` | `str` | Language for voice activation |
|
|
159
|
+
| `use_cashback` | `bool` | Use cashback balance first |
|
|
160
|
+
| `user_id` | `str` | End-user ID for tracking |
|
|
161
|
+
| `ref` | `str` | Referral ID |
|
|
162
|
+
| `pool_provider` | `str` | Pool provider: alpha, prime, gamma, zeta |
|
|
163
|
+
|
|
164
|
+
##### `get_number_v2(service, country, **options) -> dict`
|
|
165
|
+
|
|
166
|
+
Same as `get_number` but returns JSON with additional fields. Supports `order_id` for idempotency.
|
|
167
|
+
|
|
168
|
+
#### Activation Management
|
|
169
|
+
|
|
170
|
+
##### `set_status(id, status) -> str`
|
|
171
|
+
|
|
172
|
+
Changes activation status.
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from virtualsms import ActivationStatus
|
|
176
|
+
|
|
177
|
+
client.set_status(activation_id, ActivationStatus.READY) # 1 - SMS sent
|
|
178
|
+
client.set_status(activation_id, ActivationStatus.RETRY) # 3 - Request another SMS
|
|
179
|
+
client.set_status(activation_id, ActivationStatus.COMPLETE) # 6 - Finish
|
|
180
|
+
client.set_status(activation_id, ActivationStatus.CANCEL) # 8 - Cancel
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
##### `get_status(id) -> StatusResponse`
|
|
184
|
+
|
|
185
|
+
Returns activation status in text format.
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
status = client.get_status(activation_id)
|
|
189
|
+
print(status.status) # STATUS_OK, STATUS_WAIT_CODE, STATUS_CANCEL
|
|
190
|
+
print(status.code) # 123456 (None if not yet received)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
##### `get_status_v2(id) -> dict`
|
|
194
|
+
|
|
195
|
+
Returns activation status in JSON format with SMS/call details.
|
|
196
|
+
|
|
197
|
+
##### `get_active_activations() -> list`
|
|
198
|
+
|
|
199
|
+
Returns all currently active activations.
|
|
200
|
+
|
|
201
|
+
##### `check_extra_activation(id) -> dict`
|
|
202
|
+
|
|
203
|
+
Checks if a number is available for reactivation.
|
|
204
|
+
|
|
205
|
+
##### `get_extra_activation(id) -> NumberResponse`
|
|
206
|
+
|
|
207
|
+
Creates an extra activation on a previously used number.
|
|
208
|
+
|
|
209
|
+
#### Notifications
|
|
210
|
+
|
|
211
|
+
##### `get_notifications() -> dict`
|
|
212
|
+
|
|
213
|
+
Returns user notifications including penalties, low balance alerts, and admin messages.
|
|
214
|
+
|
|
215
|
+
### Constants
|
|
216
|
+
|
|
217
|
+
#### ActivationStatus
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
ActivationStatus.READY # 1 - SMS has been sent to the number
|
|
221
|
+
ActivationStatus.RETRY # 3 - Request another SMS code
|
|
222
|
+
ActivationStatus.COMPLETE # 6 - Finish activation
|
|
223
|
+
ActivationStatus.CANCEL # 8 - Cancel activation
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### PoolProvider
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
PoolProvider.ALPHA # 'alpha'
|
|
230
|
+
PoolProvider.PRIME # 'prime'
|
|
231
|
+
PoolProvider.GAMMA # 'gamma'
|
|
232
|
+
PoolProvider.ZETA # 'zeta'
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Error Handling
|
|
236
|
+
|
|
237
|
+
The SDK raises typed exceptions for all API errors. Each error code maps to a specific exception class:
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
from virtualsms import (
|
|
241
|
+
VirtualSMSClient,
|
|
242
|
+
VirtualSMSException,
|
|
243
|
+
AuthenticationException,
|
|
244
|
+
InsufficientBalanceException,
|
|
245
|
+
NoNumbersException,
|
|
246
|
+
ValidationException,
|
|
247
|
+
ActivationException,
|
|
248
|
+
RateLimitException,
|
|
249
|
+
ServerException,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
try:
|
|
253
|
+
number = client.get_number("wa", 73)
|
|
254
|
+
except AuthenticationException:
|
|
255
|
+
# BAD_KEY, BANNED, PURCHASE_RESTRICTED, SERVICE_RESTRICTED
|
|
256
|
+
pass
|
|
257
|
+
except InsufficientBalanceException:
|
|
258
|
+
# NO_BALANCE
|
|
259
|
+
pass
|
|
260
|
+
except NoNumbersException:
|
|
261
|
+
# NO_NUMBERS
|
|
262
|
+
pass
|
|
263
|
+
except ValidationException:
|
|
264
|
+
# WRONG_SERVICE, WRONG_COUNTRY, BAD_ACTION, BAD_STATUS, NO_PRICES, INVALID_PROVIDER
|
|
265
|
+
pass
|
|
266
|
+
except ActivationException:
|
|
267
|
+
# NO_ACTIVATION, WRONG_ACTIVATION_ID, EARLY_CANCEL_DENIED, RENEW_ACTIVATION_NOT_AVAILABLE
|
|
268
|
+
pass
|
|
269
|
+
except RateLimitException as e:
|
|
270
|
+
# CONCURRENT_LIMIT — check e.retry_after
|
|
271
|
+
pass
|
|
272
|
+
except ServerException:
|
|
273
|
+
# ERROR_SQL, unknown errors
|
|
274
|
+
pass
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Error Code Reference
|
|
278
|
+
|
|
279
|
+
| Error Code | Exception | Description |
|
|
280
|
+
|-----------|-----------|-------------|
|
|
281
|
+
| `BAD_KEY` | `AuthenticationException` | Invalid API key |
|
|
282
|
+
| `BANNED` | `AuthenticationException` | Account banned or IP blocked |
|
|
283
|
+
| `PURCHASE_RESTRICTED` | `AuthenticationException` | User restricted from purchasing |
|
|
284
|
+
| `SERVICE_RESTRICTED` | `AuthenticationException` | Service restricted for account |
|
|
285
|
+
| `NO_BALANCE` | `InsufficientBalanceException` | Insufficient balance |
|
|
286
|
+
| `NO_NUMBERS` | `NoNumbersException` | No numbers available |
|
|
287
|
+
| `WRONG_SERVICE` | `ValidationException` | Invalid service code |
|
|
288
|
+
| `WRONG_COUNTRY` | `ValidationException` | Invalid country ID |
|
|
289
|
+
| `BAD_ACTION` | `ValidationException` | Invalid action |
|
|
290
|
+
| `BAD_STATUS` | `ValidationException` | Invalid status code |
|
|
291
|
+
| `NO_PRICES` | `ValidationException` | No pricing data available |
|
|
292
|
+
| `INVALID_PROVIDER` | `ValidationException` | Invalid pool provider |
|
|
293
|
+
| `NO_ACTIVATION` | `ActivationException` | Activation not found |
|
|
294
|
+
| `WRONG_ACTIVATION_ID` | `ActivationException` | Invalid activation ID |
|
|
295
|
+
| `EARLY_CANCEL_DENIED` | `ActivationException` | Cannot cancel within 5 minutes |
|
|
296
|
+
| `RENEW_ACTIVATION_NOT_AVAILABLE` | `ActivationException` | Number not available for reactivation |
|
|
297
|
+
| `CONCURRENT_LIMIT` | `RateLimitException` | Too many concurrent activations |
|
|
298
|
+
| `ERROR_SQL` | `ServerException` | Internal server error |
|
|
299
|
+
|
|
300
|
+
## Tracking Headers
|
|
301
|
+
|
|
302
|
+
The SDK sends anonymous tracking headers with every request for analytics and debugging:
|
|
303
|
+
|
|
304
|
+
| Header | Value | Privacy |
|
|
305
|
+
|--------|-------|---------|
|
|
306
|
+
| `X-SDK-Version` | `1.0.0` | SDK version string |
|
|
307
|
+
| `X-SDK-Language` | `python` | SDK language |
|
|
308
|
+
| `X-SDK-Machine-Id` | SHA-256 hash of `platform.platform()` + `sys.implementation.name` (truncated to 32 chars) | Irreversible hash — no hostname or IP exposed |
|
|
309
|
+
| `X-SDK-Timestamp` | ISO 8601 UTC timestamp | Request time |
|
|
310
|
+
|
|
311
|
+
No personally identifiable information is transmitted. The machine ID is a one-way hash and cannot be reversed to identify the source machine.
|
|
312
|
+
|
|
313
|
+
## Custom Transport
|
|
314
|
+
|
|
315
|
+
By default, the SDK uses `urllib.request` for HTTP requests. You can provide your own transport implementation:
|
|
316
|
+
|
|
317
|
+
```python
|
|
318
|
+
from virtualsms import VirtualSMSClient, Transport, Response
|
|
319
|
+
|
|
320
|
+
class MyTransport(Transport):
|
|
321
|
+
def send(self, url: str, headers: dict) -> Response:
|
|
322
|
+
# Your HTTP implementation
|
|
323
|
+
return Response(status_code=200, body="ACCESS_BALANCE:10.50")
|
|
324
|
+
|
|
325
|
+
client = VirtualSMSClient("API_KEY", "https://api.example.com", transport=MyTransport())
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Examples
|
|
329
|
+
|
|
330
|
+
See the `examples/` directory:
|
|
331
|
+
|
|
332
|
+
- [`balance.py`](examples/balance.py) — Check account balance
|
|
333
|
+
- [`order_number.py`](examples/order_number.py) — Order a phone number
|
|
334
|
+
- [`full_workflow.py`](examples/full_workflow.py) — Complete SMS verification workflow
|
|
335
|
+
|
|
336
|
+
## Testing
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
pip install pytest
|
|
340
|
+
pytest
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## License
|
|
344
|
+
|
|
345
|
+
MIT — see [LICENSE](LICENSE).
|
|
346
|
+
|
|
347
|
+
## Links
|
|
348
|
+
|
|
349
|
+
- [PyPI](https://pypi.org/project/virtualsms/)
|
|
350
|
+
- [GitHub](https://github.com/VirtualSMSLabs/python-sdk)
|
|
351
|
+
- [API Documentation](https://virtualsms.de/docs)
|
|
352
|
+
- [MCP Integration](https://virtualsms.de/docs/mcp)
|