orb-cloud-client 1.2.1__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.
- orb_cloud_client-1.2.1/LICENSE +21 -0
- orb_cloud_client-1.2.1/MANIFEST.in +4 -0
- orb_cloud_client-1.2.1/PKG-INFO +426 -0
- orb_cloud_client-1.2.1/README.md +398 -0
- orb_cloud_client-1.2.1/orb_cloud_client/__init__.py +15 -0
- orb_cloud_client-1.2.1/orb_cloud_client/client.py +107 -0
- orb_cloud_client-1.2.1/orb_cloud_client/example.py +183 -0
- orb_cloud_client-1.2.1/orb_cloud_client/example_async.py +47 -0
- orb_cloud_client-1.2.1/orb_cloud_client/models.py +245 -0
- orb_cloud_client-1.2.1/orb_cloud_client.egg-info/PKG-INFO +426 -0
- orb_cloud_client-1.2.1/orb_cloud_client.egg-info/SOURCES.txt +15 -0
- orb_cloud_client-1.2.1/orb_cloud_client.egg-info/dependency_links.txt +1 -0
- orb_cloud_client-1.2.1/orb_cloud_client.egg-info/entry_points.txt +2 -0
- orb_cloud_client-1.2.1/orb_cloud_client.egg-info/requires.txt +7 -0
- orb_cloud_client-1.2.1/orb_cloud_client.egg-info/top_level.txt +1 -0
- orb_cloud_client-1.2.1/pyproject.toml +43 -0
- orb_cloud_client-1.2.1/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Orb Networks
|
|
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,426 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: orb-cloud-client
|
|
3
|
+
Version: 1.2.1
|
|
4
|
+
Summary: Python client library for Orb Cloud API
|
|
5
|
+
Author-email: Orb Networks <support@orb.net>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Documentation, https://api.orb.net/docs
|
|
8
|
+
Project-URL: Homepage, https://github.com/orbforge/orb-cloud
|
|
9
|
+
Project-URL: Repository, https://github.com/orbforge/orb-cloud
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: build>=1.2.2.post1
|
|
22
|
+
Requires-Dist: httpx>=0.24.0
|
|
23
|
+
Requires-Dist: pydantic>=2.0.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: build; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest; extra == "dev"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# Orb Cloud API Client
|
|
30
|
+
|
|
31
|
+
A Python client library for interacting with the Orb Cloud API. This package provides a simple interface to manage devices, configure data collection, and interact with Orb Cloud services.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
Install the package from the wheel file:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install orb-cloud-client
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The package will automatically install its dependencies:
|
|
42
|
+
- `httpx>=0.24.0` (for HTTP requests)
|
|
43
|
+
- `pydantic>=2.0.0` (for data models)
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from orb_cloud_client import OrbCloudClient
|
|
49
|
+
|
|
50
|
+
# Create client
|
|
51
|
+
client = OrbCloudClient(token="your-api-token")
|
|
52
|
+
|
|
53
|
+
# Get organization devices
|
|
54
|
+
devices = client.get_organization_devices("your-org-id")
|
|
55
|
+
print(f"Found {len(devices)} devices")
|
|
56
|
+
|
|
57
|
+
# Use as context manager (recommended)
|
|
58
|
+
with OrbCloudClient(token="your-api-token") as client:
|
|
59
|
+
devices = client.get_organization_devices("your-org-id")
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## API Reference
|
|
63
|
+
|
|
64
|
+
### OrbCloudClient
|
|
65
|
+
|
|
66
|
+
#### Constructor
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
OrbCloudClient(base_url="https://panel.orb.net", token=None)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Parameters:**
|
|
73
|
+
- `base_url` (str, optional): API base URL. Defaults to production environment.
|
|
74
|
+
- `token` (str, optional): Bearer authentication token.
|
|
75
|
+
|
|
76
|
+
#### Methods
|
|
77
|
+
|
|
78
|
+
##### get_organization_devices(organization_id)
|
|
79
|
+
|
|
80
|
+
Get a list of all devices for an organization.
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
devices = client.get_organization_devices("org-123")
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Parameters:**
|
|
87
|
+
- `organization_id` (str): The organization ID
|
|
88
|
+
|
|
89
|
+
**Returns:**
|
|
90
|
+
- `List[Device]`: List of Device objects
|
|
91
|
+
|
|
92
|
+
**Example:**
|
|
93
|
+
```python
|
|
94
|
+
devices = client.get_organization_devices("org-123")
|
|
95
|
+
for device in devices:
|
|
96
|
+
print(f"Device: {device.name} (ID: {device.id})")
|
|
97
|
+
print(f" Status: {'Connected' if device.is_connected else 'Disconnected'}")
|
|
98
|
+
print(f" Location: {device.geo_ip_info.city}, {device.geo_ip_info.state}")
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
##### configure_temporary_datasets(device_id, temp_datasets_request)
|
|
102
|
+
|
|
103
|
+
Configure temporary data collection for a device with custom data push endpoint.
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from orb_cloud_client.models import TempDatasetsRequest, Datasets, DataPush
|
|
107
|
+
|
|
108
|
+
temp_config = TempDatasetsRequest(
|
|
109
|
+
duration="5m", # Run for 5 minutes
|
|
110
|
+
datasets_config=Datasets(
|
|
111
|
+
enabled=True,
|
|
112
|
+
datasets=["responsiveness_1s", "scores_1s"], # Collect these datasets
|
|
113
|
+
push=DataPush(
|
|
114
|
+
enabled=True,
|
|
115
|
+
url="https://your-endpoint.com/data", # Your webhook URL
|
|
116
|
+
datasets=["responsiveness_1s", "scores_1s"],
|
|
117
|
+
format="json",
|
|
118
|
+
interval_ms=500 # Push new data every 500ms (when available)
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
result = client.configure_temporary_datasets("device-456", temp_config)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Parameters:**
|
|
127
|
+
- `device_id` (str): The device ID
|
|
128
|
+
- `temp_datasets_request` (TempDatasetsRequest): Configuration object
|
|
129
|
+
|
|
130
|
+
**Returns:**
|
|
131
|
+
- `Dict[str, Any]`: API response
|
|
132
|
+
|
|
133
|
+
##### request(method, endpoint, **kwargs)
|
|
134
|
+
|
|
135
|
+
Make a custom HTTP request to any API endpoint.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
response = client.request("GET", "/api/v2/custom-endpoint")
|
|
139
|
+
data = response.json()
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Parameters:**
|
|
143
|
+
- `method` (str): HTTP method (GET, POST, PUT, DELETE, etc.)
|
|
144
|
+
- `endpoint` (str): API endpoint path
|
|
145
|
+
- `**kwargs`: Additional arguments passed to httpx
|
|
146
|
+
|
|
147
|
+
**Returns:**
|
|
148
|
+
- `httpx.Response`: Raw HTTP response
|
|
149
|
+
|
|
150
|
+
##### close()
|
|
151
|
+
|
|
152
|
+
Close the HTTP client connection.
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
client.close()
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Data Models
|
|
159
|
+
|
|
160
|
+
All API models are Pydantic models with full type validation and serialization.
|
|
161
|
+
|
|
162
|
+
#### Device
|
|
163
|
+
|
|
164
|
+
Represents an Orb device with all its properties.
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from orb_cloud_client.models import Device
|
|
168
|
+
|
|
169
|
+
# Device properties
|
|
170
|
+
device.id # str: Device UUID
|
|
171
|
+
device.name # str: Device name
|
|
172
|
+
device.is_connected # IsConnected: Connection status (0 or 1)
|
|
173
|
+
device.last_seen # Optional[str]: Last seen timestamp
|
|
174
|
+
device.geo_ip_info # Optional[GeoIPInfo]: Geographic information
|
|
175
|
+
device.device_info # Optional[DeviceInfo]: Hardware/software info
|
|
176
|
+
device.network_interfaces # Optional[List[NetworkInterface]]: Network info
|
|
177
|
+
device.orb_score # Optional[OrbScore]: Performance metrics
|
|
178
|
+
device.summary # Optional[DeviceSummary]: Device summary
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### TempDatasetsRequest
|
|
182
|
+
|
|
183
|
+
Configuration for temporary data collection.
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from orb_cloud_client.models import TempDatasetsRequest, Datasets, DataPush
|
|
187
|
+
|
|
188
|
+
temp_request = TempDatasetsRequest(
|
|
189
|
+
duration="5m", # Duration (e.g., "5m", "1h", "30m", "2h")
|
|
190
|
+
datasets_config=Datasets(
|
|
191
|
+
enabled=True,
|
|
192
|
+
datasets=["responsiveness_1s", "scores_1s"], # Dataset types to collect
|
|
193
|
+
push=DataPush( # Data push configuration
|
|
194
|
+
enabled=True,
|
|
195
|
+
url="https://your-server.com/webhook",
|
|
196
|
+
datasets=["responsiveness_1s", "scores_1s"],
|
|
197
|
+
format="json",
|
|
198
|
+
interval_ms=500 # Push interval in milliseconds
|
|
199
|
+
)
|
|
200
|
+
)
|
|
201
|
+
)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### Datasets
|
|
205
|
+
|
|
206
|
+
Data collection configuration with push settings.
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
datasets = Datasets(
|
|
210
|
+
enabled=True, # bool: Enable data collection
|
|
211
|
+
datasets=[ # List[str]: Dataset types to collect
|
|
212
|
+
"responsiveness_1s",
|
|
213
|
+
"scores_1s",
|
|
214
|
+
"latency_1s",
|
|
215
|
+
"throughput_1s"
|
|
216
|
+
],
|
|
217
|
+
push=DataPush( # Optional: Push configuration
|
|
218
|
+
enabled=True,
|
|
219
|
+
url="https://api.example.com/webhook",
|
|
220
|
+
datasets=["responsiveness_1s", "scores_1s"],
|
|
221
|
+
format="json", # Data format
|
|
222
|
+
interval_ms=500 # Push interval in milliseconds
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
#### DataPush
|
|
228
|
+
|
|
229
|
+
Configuration for pushing data to external endpoints.
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
data_push = DataPush(
|
|
233
|
+
enabled=True, # bool: Enable data pushing
|
|
234
|
+
url="https://api.example.com/webhook", # str: Webhook URL
|
|
235
|
+
datasets=["responsiveness_1s", "scores_1s"], # List[str]: Datasets to push
|
|
236
|
+
format="json", # str: Data format
|
|
237
|
+
interval_ms=500 # int: Push interval in milliseconds
|
|
238
|
+
)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
#### GeoIPInfo
|
|
242
|
+
|
|
243
|
+
Geographic information from device IP address.
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
geo_info.city # Optional[str]: City name
|
|
247
|
+
geo_info.state # Optional[str]: State/region
|
|
248
|
+
geo_info.country # Optional[str]: Country name
|
|
249
|
+
geo_info.country_code # Optional[str]: Country code (e.g., "US")
|
|
250
|
+
geo_info.isp_name # Optional[str]: Internet service provider
|
|
251
|
+
geo_info.latitude # Optional[float]: Latitude coordinate
|
|
252
|
+
geo_info.longitude # Optional[float]: Longitude coordinate
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
#### DeviceInfo
|
|
256
|
+
|
|
257
|
+
Device hardware and software information.
|
|
258
|
+
|
|
259
|
+
```python
|
|
260
|
+
device_info.name # Optional[str]: Device hostname
|
|
261
|
+
device_info.version # Optional[str]: Software version
|
|
262
|
+
device_info.cpu_count # Optional[int]: Number of CPU cores
|
|
263
|
+
device_info.full_name # Optional[str]: Full hostname
|
|
264
|
+
device_info.operating_system # Optional[str]: OS name
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
#### NetworkInterface
|
|
268
|
+
|
|
269
|
+
Network interface information.
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
interface.name # Optional[str]: Interface name
|
|
273
|
+
interface.type # Optional[str]: Interface type (e.g., "Ethernet")
|
|
274
|
+
interface.local_ip # Optional[str]: Local IP address
|
|
275
|
+
interface.mac_address # Optional[str]: MAC address
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Usage Examples
|
|
279
|
+
|
|
280
|
+
### Basic Device Management
|
|
281
|
+
|
|
282
|
+
```python
|
|
283
|
+
from orb_cloud_client import OrbCloudClient
|
|
284
|
+
|
|
285
|
+
with OrbCloudClient(token="your-token") as client:
|
|
286
|
+
# Get all devices
|
|
287
|
+
devices = client.get_organization_devices("org-123")
|
|
288
|
+
|
|
289
|
+
# Filter connected devices
|
|
290
|
+
connected_devices = [d for d in devices if d.is_connected == 1]
|
|
291
|
+
|
|
292
|
+
# Find specific device
|
|
293
|
+
target_device = next((d for d in devices if "hardy" in d.name.lower()), None)
|
|
294
|
+
if target_device:
|
|
295
|
+
print(f"Found device: {target_device.name}")
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Configure Data Collection
|
|
299
|
+
|
|
300
|
+
```python
|
|
301
|
+
from orb_cloud_client import OrbCloudClient
|
|
302
|
+
from orb_cloud_client.models import TempDatasetsRequest, Datasets, DataPush
|
|
303
|
+
|
|
304
|
+
with OrbCloudClient(token="your-token") as client:
|
|
305
|
+
# Configure temporary datasets with custom endpoint
|
|
306
|
+
config = TempDatasetsRequest(
|
|
307
|
+
duration="5m", # Run for 5 minutes
|
|
308
|
+
datasets_config=Datasets(
|
|
309
|
+
enabled=True,
|
|
310
|
+
datasets=["responsiveness_1s", "scores_1s"],
|
|
311
|
+
push=DataPush(
|
|
312
|
+
enabled=True,
|
|
313
|
+
url="https://your-analytics.com/orb-data",
|
|
314
|
+
datasets=["responsiveness_1s", "scores_1s"],
|
|
315
|
+
format="json",
|
|
316
|
+
interval_ms=500 # Push every 500ms when data available
|
|
317
|
+
)
|
|
318
|
+
)
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
result = client.configure_temporary_datasets("device-456", config)
|
|
322
|
+
print("Configuration successful:", result)
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Error Handling
|
|
326
|
+
|
|
327
|
+
```python
|
|
328
|
+
from orb_cloud_client import OrbCloudClient
|
|
329
|
+
import httpx
|
|
330
|
+
|
|
331
|
+
with OrbCloudClient(token="your-token") as client:
|
|
332
|
+
try:
|
|
333
|
+
devices = client.get_organization_devices("org-123")
|
|
334
|
+
except httpx.HTTPStatusError as e:
|
|
335
|
+
print(f"HTTP error {e.response.status_code}: {e.response.text}")
|
|
336
|
+
except httpx.RequestError as e:
|
|
337
|
+
print(f"Request error: {e}")
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Working with Device Data
|
|
341
|
+
|
|
342
|
+
```python
|
|
343
|
+
from orb_cloud_client import OrbCloudClient
|
|
344
|
+
|
|
345
|
+
with OrbCloudClient(token="your-token") as client:
|
|
346
|
+
devices = client.get_organization_devices("org-123")
|
|
347
|
+
|
|
348
|
+
for device in devices:
|
|
349
|
+
print(f"\n--- {device.name} ---")
|
|
350
|
+
print(f"ID: {device.id}")
|
|
351
|
+
print(f"Connected: {'Yes' if device.is_connected else 'No'}")
|
|
352
|
+
|
|
353
|
+
if device.geo_ip_info:
|
|
354
|
+
print(f"Location: {device.geo_ip_info.city}, {device.geo_ip_info.state}")
|
|
355
|
+
print(f"ISP: {device.geo_ip_info.isp_name}")
|
|
356
|
+
|
|
357
|
+
if device.device_info:
|
|
358
|
+
print(f"OS: {device.device_info.operating_system}")
|
|
359
|
+
print(f"Version: {device.device_info.version}")
|
|
360
|
+
|
|
361
|
+
if device.network_interfaces:
|
|
362
|
+
for interface in device.network_interfaces:
|
|
363
|
+
print(f"Interface: {interface.name} ({interface.local_ip})")
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Interactive Example
|
|
367
|
+
|
|
368
|
+
The package includes an interactive example that demonstrates the complete workflow:
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
export ORB_API_TOKEN="your-api-token"
|
|
372
|
+
export ORB_ORGANIZATION_ID="your-org-id"
|
|
373
|
+
python3 -m orb_cloud_client.example
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
The example will:
|
|
377
|
+
1. Fetch and display all devices
|
|
378
|
+
2. Configure temporary datasets for the selected device
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
See `example.py` for example code.
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
## Available Dataset Types
|
|
385
|
+
|
|
386
|
+
Common dataset types you can collect:
|
|
387
|
+
- `responsiveness_{timeframe}` - time-binned responsiveness (example `responiveness_1s`, `responsiveness_15s`)
|
|
388
|
+
- `scores_{timeframe}` - time-binned scores (example `scores_1s`, `scores_1m`)
|
|
389
|
+
- `speed_results` - Result-level speed measurements
|
|
390
|
+
- `web_responsiveness_results` - Result-level DNS resolve and TTFB measurements
|
|
391
|
+
|
|
392
|
+
## Authentication
|
|
393
|
+
|
|
394
|
+
The client uses Bearer token authentication. Get your API token from the Orb Cloud dashboard and pass it to the client constructor:
|
|
395
|
+
|
|
396
|
+
```python
|
|
397
|
+
client = OrbCloudClient(token="your-bearer-token")
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## Environment Support
|
|
401
|
+
|
|
402
|
+
- **Staging**: `https://api.staging.orb.net` (default)
|
|
403
|
+
- **Production**: `https://api.orb.net`
|
|
404
|
+
|
|
405
|
+
```python
|
|
406
|
+
# Production client
|
|
407
|
+
client = OrbCloudClient(
|
|
408
|
+
base_url="https://api.orb.net",
|
|
409
|
+
token="your-token"
|
|
410
|
+
)
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
## Requirements
|
|
414
|
+
|
|
415
|
+
- Python 3.8+
|
|
416
|
+
- httpx >= 0.24.0
|
|
417
|
+
- pydantic >= 2.0.0
|
|
418
|
+
|
|
419
|
+
## Error Handling
|
|
420
|
+
|
|
421
|
+
The client raises standard httpx exceptions:
|
|
422
|
+
- `httpx.HTTPStatusError` - For HTTP error responses (4xx, 5xx)
|
|
423
|
+
- `httpx.RequestError` - For network/connection errors
|
|
424
|
+
- `httpx.TimeoutException` - For request timeouts
|
|
425
|
+
|
|
426
|
+
Always wrap API calls in try-catch blocks for production use.
|