uptimer-python-sdk 0.3.0__tar.gz → 1.5.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.
Files changed (77) hide show
  1. uptimer_python_sdk-1.5.0/CHANGELOG.md +74 -0
  2. uptimer_python_sdk-1.5.0/PKG-INFO +287 -0
  3. uptimer_python_sdk-1.5.0/README.md +271 -0
  4. uptimer_python_sdk-1.5.0/Taskfile.yml +35 -0
  5. uptimer_python_sdk-1.5.0/examples/01_client_setup.py +21 -0
  6. uptimer_python_sdk-1.5.0/examples/02_list_workspaces.py +10 -0
  7. uptimer_python_sdk-1.5.0/examples/03_list_locations.py +13 -0
  8. uptimer_python_sdk-1.5.0/examples/04_create_website_monitor.py +43 -0
  9. uptimer_python_sdk-1.5.0/examples/05_open_incidents.py +26 -0
  10. uptimer_python_sdk-1.5.0/examples/README.md +33 -0
  11. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/pyproject.toml +8 -3
  12. uptimer_python_sdk-1.5.0/src/uptimer/__init__.py +12 -0
  13. uptimer_python_sdk-1.5.0/src/uptimer/client.py +69 -0
  14. uptimer_python_sdk-1.5.0/src/uptimer/compat.py +58 -0
  15. uptimer_python_sdk-1.5.0/src/uptimer/endpoints/incidents.py +37 -0
  16. uptimer_python_sdk-0.3.0/src/uptimer/endpoints/regions.py → uptimer_python_sdk-1.5.0/src/uptimer/endpoints/locations.py +7 -6
  17. uptimer_python_sdk-1.5.0/src/uptimer/endpoints/v2.py +38 -0
  18. uptimer_python_sdk-1.5.0/src/uptimer/endpoints/websites.py +116 -0
  19. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/src/uptimer/endpoints/workspaces.py +4 -3
  20. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/src/uptimer/errors.py +18 -0
  21. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/src/uptimer/http.py +12 -1
  22. uptimer_python_sdk-1.5.0/src/uptimer/models/__init__.py +29 -0
  23. uptimer_python_sdk-1.5.0/src/uptimer/models/v2/__init__.py +70 -0
  24. uptimer_python_sdk-1.5.0/src/uptimer/models/v2/deserialize.py +119 -0
  25. uptimer_python_sdk-1.5.0/src/uptimer/models/v2/incident.py +39 -0
  26. uptimer_python_sdk-1.5.0/src/uptimer/models/v2/location.py +16 -0
  27. uptimer_python_sdk-1.5.0/src/uptimer/models/v2/monitor.py +88 -0
  28. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/tests/integrations/conftest.py +2 -3
  29. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/tests/integrations/test_workspaces.py +4 -4
  30. uptimer_python_sdk-1.5.0/tests/test_client.py +114 -0
  31. uptimer_python_sdk-1.5.0/tests/test_integration_v2.py +274 -0
  32. uptimer_python_sdk-1.5.0/tests/test_model_namespace.py +118 -0
  33. uptimer_python_sdk-1.5.0/tests/test_v2_endpoints.py +134 -0
  34. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/tests/test_workspaces_endpoint.py +2 -2
  35. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/uv.lock +569 -567
  36. uptimer_python_sdk-0.3.0/CHANGELOG.md +0 -19
  37. uptimer_python_sdk-0.3.0/PKG-INFO +0 -203
  38. uptimer_python_sdk-0.3.0/README.md +0 -190
  39. uptimer_python_sdk-0.3.0/examples/01_client_setup.py +0 -14
  40. uptimer_python_sdk-0.3.0/examples/02_list_workspaces.py +0 -12
  41. uptimer_python_sdk-0.3.0/examples/03_list_regions.py +0 -12
  42. uptimer_python_sdk-0.3.0/examples/04_list_rules.py +0 -13
  43. uptimer_python_sdk-0.3.0/examples/05_get_rule.py +0 -13
  44. uptimer_python_sdk-0.3.0/examples/06_create_rule.py +0 -35
  45. uptimer_python_sdk-0.3.0/examples/07_update_rule.py +0 -37
  46. uptimer_python_sdk-0.3.0/examples/08_delete_rule.py +0 -13
  47. uptimer_python_sdk-0.3.0/examples/README.md +0 -40
  48. uptimer_python_sdk-0.3.0/src/uptimer/__init__.py +0 -3
  49. uptimer_python_sdk-0.3.0/src/uptimer/client.py +0 -26
  50. uptimer_python_sdk-0.3.0/src/uptimer/endpoints/rules.py +0 -75
  51. uptimer_python_sdk-0.3.0/src/uptimer/endpoints/v1.py +0 -17
  52. uptimer_python_sdk-0.3.0/src/uptimer/models/__init__.py +0 -47
  53. uptimer_python_sdk-0.3.0/src/uptimer/models/deserialize.py +0 -121
  54. uptimer_python_sdk-0.3.0/src/uptimer/models/region.py +0 -9
  55. uptimer_python_sdk-0.3.0/src/uptimer/models/rule.py +0 -57
  56. uptimer_python_sdk-0.3.0/tests/test_client.py +0 -37
  57. uptimer_python_sdk-0.3.0/tests/test_regions_endpoint.py +0 -46
  58. uptimer_python_sdk-0.3.0/tests/test_rules_endpoint.py +0 -535
  59. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/.gitignore +0 -0
  60. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/.pre-commit-config.yaml +0 -0
  61. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/.vscode/extensions.json +0 -0
  62. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/.vscode/launch.json +0 -0
  63. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/.vscode/settings.json +0 -0
  64. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/.vscode/tasks.json +0 -0
  65. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/LICENSE +0 -0
  66. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/NOTICE +0 -0
  67. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/examples/__init__.py +0 -0
  68. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/src/uptimer/endpoints/__init__.py +0 -0
  69. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/src/uptimer/endpoints/endpoint.py +0 -0
  70. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/src/uptimer/models/errors.py +0 -0
  71. {uptimer_python_sdk-0.3.0/src/uptimer/models → uptimer_python_sdk-1.5.0/src/uptimer/models/v2}/workspace.py +0 -0
  72. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/src/uptimer/py.typed +0 -0
  73. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/tests/__init__.py +0 -0
  74. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/tests/conftest.py +0 -0
  75. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/tests/integrations/__init__.py +0 -0
  76. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/tests/test_endpoint.py +0 -0
  77. {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/tools/mypy-diff.sh +0 -0
@@ -0,0 +1,74 @@
1
+ ## 1.5.0 (2026-08-20)
2
+
3
+ ### BREAKING CHANGE
4
+
5
+ - Targets Uptimer **API v2 only**. The `client.v1` namespace, its models and its
6
+ kinds are gone. Code written against 0.4.x keeps working **against the
7
+ server** — API v1 is unchanged and supported — but must pin
8
+ `uptimer-python-sdk<1`.
9
+ - The version now tracks the uptimer release it targets, so this is 1.5.0 rather
10
+ than 1.0.0: 1.5.x speaks to uptimer 1.5.0 and later. Patch numbers stay
11
+ independent (product Decision 0013).
12
+
13
+ ### Feat
14
+
15
+ - `client.v2.workspaces`, `client.v2.locations`, `client.v2.incidents` and
16
+ `client.v2.monitoring.websites` replace the v1 namespace. The API version stays
17
+ visible in the SDK, as it was in 0.4.x — there are no root-level aliases.
18
+ - **Types are versioned too:** import them from `uptimer.models.v2`
19
+ (`Location`, `Incident`, `Workspace`, the website-monitor classes, the
20
+ `AGREEMENT_*` / `STATUS_*` constants and the `from_api*` helpers). They are no
21
+ longer exported from `uptimer.models`, and there are no flat aliases. The
22
+ deserialization exceptions stay on `uptimer.models`, being
23
+ version-independent.
24
+ - `client.v2.incidents` reads **open** incidents, with the same five status words
25
+ the Uptimer screens use — `problem`, `pending`, `recovering`, `no_data`, `ok`.
26
+ `pending` means failing but inside the confirm hold: nobody has been notified
27
+ yet.
28
+ - Website monitors carry `agreement` (`any`, `majority`, `all`) — how many
29
+ locations must report a problem before the monitor does. Omitting it on update
30
+ keeps the stored value.
31
+ - `client.check_compatibility()` refuses a server that predates API v2 with a
32
+ message naming the fix, instead of a bare 404 on the first call.
33
+
34
+ ### Migration
35
+
36
+ | 0.4.x | 1.5.0 |
37
+ |---|---|
38
+ | `client.v1.workspaces` | `client.v2.workspaces` |
39
+ | `client.v1.regions` | `client.v2.locations` |
40
+ | `client.v1.rules` | `client.v2.monitoring.websites` |
41
+ | `Region` | `Location` |
42
+ | `Rule` / `CreateRuleRequest` | `WebsiteMonitor` / `CreateWebsiteMonitorRequest` |
43
+ | `regions=[...]` | `locations=[...]` |
44
+ | — | `agreement=...`, `client.v2.incidents` |
45
+ | `from uptimer.models import …` | `from uptimer.models.v2 import …` |
46
+
47
+ `client.version()` and the compatibility helpers are unchanged and stay on the
48
+ client itself: `/version` is a shared global endpoint, not a versioned one.
49
+
50
+ ## 0.4.0 (2026-07-15)
51
+
52
+ ### Feat
53
+
54
+ - assign regions (by name) when creating or updating a rule via `rules.create`/`rules.update`; rules now expose a `regions` field
55
+
56
+ ## 0.3.0 (2025-08-19)
57
+
58
+ ### Feat
59
+
60
+ - self-hosted client now requires a base_url, added cloud client, updated docs
61
+
62
+ ## 0.2.0 (2025-08-10)
63
+
64
+ ### Feat
65
+
66
+ - create/update/delete rule methods.
67
+ - added method to get all rules
68
+ - added regions and get rule API
69
+ - added getting workspace list
70
+ - add Cursor IDE configuration for testing and development
71
+
72
+ ### Fix
73
+
74
+ - fixed dependencies in pyproject.toml
@@ -0,0 +1,287 @@
1
+ Metadata-Version: 2.5
2
+ Name: uptimer-python-sdk
3
+ Version: 1.5.0
4
+ Summary: A Python SDK for hosted and self-hosted Uptimer
5
+ Project-URL: Homepage, https://myuptime.info
6
+ Project-URL: Documentation, https://uptimer.myuptime.info/latest/reference/python-sdk/
7
+ Project-URL: Repository, https://github.com/myuptime-info/uptimer-python-sdk
8
+ Project-URL: Changelog, https://myuptime.info/product-updates
9
+ Author-email: Roman Zadoev <zadoev@gmail.com>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ License-File: NOTICE
13
+ Requires-Python: >=3.9
14
+ Requires-Dist: httpx>=0.28.1
15
+ Description-Content-Type: text/markdown
16
+
17
+ # Uptimer Python SDK
18
+
19
+ A Python SDK for hosted and self-hosted Uptimer.
20
+
21
+ * [Hosted Uptimer](https://myuptime.info)
22
+ * [Self-hosted documentation](https://uptimer.myuptime.info)
23
+ * [PyPI package](https://pypi.org/project/uptimer-python-sdk/)
24
+ * [Uptimer resources](https://myuptime.info/resources)
25
+ * [Product updates](https://myuptime.info/product-updates)
26
+
27
+ ## License
28
+
29
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
30
+
31
+ For third-party license information, see the [NOTICE](NOTICE) file.
32
+
33
+ ## Installation
34
+
35
+ ```shell
36
+ pip install uptimer-python-sdk
37
+ ```
38
+
39
+ or
40
+ ```shell
41
+ uv add uptimer-python-sdk
42
+ ```
43
+
44
+ ## Usage
45
+
46
+ ### Create client
47
+
48
+ #### self-hosted
49
+
50
+ ```python
51
+ from uptimer.client import UptimerClient
52
+ client = UptimerClient(
53
+ api_key="your-api-key-here",
54
+ base_url="http://127.0.0.1:2517/api", # or your custom base URL
55
+ )
56
+ ```
57
+
58
+ #### cloud
59
+ ```python
60
+ from uptimer.client import UptimerCloudClient
61
+ client = UptimerCloudClient(
62
+ api_key="your-api-key-here",
63
+ )
64
+ ```
65
+
66
+ ### Basic example
67
+
68
+ ```python
69
+ from uptimer.client import UptimerClient
70
+ from uptimer.errors import (
71
+ DefaultUptimerApiError,
72
+ IncompatibleServerError,
73
+ UptimerError,
74
+ UptimerInvalidHttpCodeError,
75
+ )
76
+ from uptimer.models.v2 import (
77
+ AGREEMENT_MAJORITY,
78
+ CreateWebsiteMonitorRequest,
79
+ UpdateWebsiteMonitorRequest,
80
+ WebsiteMonitorRequest,
81
+ WebsiteMonitorResponse,
82
+ WebsiteMonitorResponseBody,
83
+ )
84
+
85
+ client = UptimerClient(
86
+ api_key="your-api-key-here",
87
+ base_url="http://127.0.0.1:2517/api", # or your custom base URL
88
+ )
89
+
90
+ # Optional: fail fast with a message that names the fix, rather than a 404 on
91
+ # the first real call.
92
+ print("server:", client.check_compatibility())
93
+
94
+ workspace = client.v2.workspaces.all()[0]
95
+ locations = [location.name for location in client.v2.locations.all()]
96
+
97
+ monitor = client.v2.monitoring.websites.create(
98
+ CreateWebsiteMonitorRequest(
99
+ name="Checkout API",
100
+ interval=60, # seconds between probes
101
+ workspace_id=workspace.id,
102
+ request=WebsiteMonitorRequest(
103
+ url="https://checkout.example/health",
104
+ method="GET", # one of GET, POST, PATCH, OPTIONS
105
+ content_type="application/json",
106
+ data="",
107
+ ),
108
+ response=WebsiteMonitorResponse(
109
+ statuses=[200, 201], # any of these means the site is up
110
+ body=WebsiteMonitorResponseBody(content="ok"), # expected substring
111
+ ),
112
+ locations=locations,
113
+ # How many locations must report a problem before this monitor does:
114
+ # "any", "majority" or "all". Omit to keep the server default.
115
+ agreement=AGREEMENT_MAJORITY,
116
+ ),
117
+ )
118
+
119
+ monitor = client.v2.monitoring.websites.update(
120
+ monitor.id,
121
+ UpdateWebsiteMonitorRequest(
122
+ name="Checkout API",
123
+ interval=120,
124
+ request=WebsiteMonitorRequest(url="https://checkout.example/health", method="GET"),
125
+ response=WebsiteMonitorResponse(statuses=[200]),
126
+ locations=locations,
127
+ # Omitting agreement here keeps the stored one.
128
+ ),
129
+ )
130
+
131
+ # What is wrong right now. Only open incidents come back.
132
+ for incident in client.v2.incidents.all(workspace.id):
133
+ print(incident.monitor_name, incident.status, incident.locations.failing)
134
+
135
+ try:
136
+ client.v2.monitoring.websites.delete(monitor.id)
137
+ except DefaultUptimerApiError as e:
138
+ # error responses from the uptimer server
139
+ print(
140
+ e.message, # user message
141
+ e.code, # error id
142
+ e.error_type, # class of error
143
+ e.details, # detailed message for a developer
144
+ )
145
+ except IncompatibleServerError as e:
146
+ # the server does not provide API v2 — see Migrating from 0.4.x below
147
+ print(e)
148
+ except UptimerInvalidHttpCodeError as e:
149
+ # the uptimer api always returns 200; anything else is a transport error.
150
+ # a 404 really is "no such URL", not "no object with that id".
151
+ print(e.url, e.status_code)
152
+ except UptimerError: # base error, if you need one
153
+ raise
154
+ ```
155
+
156
+ ### Incident status
157
+
158
+ `client.v2.incidents.all()` returns only **open** incidents. `status` carries the
159
+ same words the Uptimer screens show, so a client and the UI cannot disagree:
160
+
161
+ | status | meaning |
162
+ |---|---|
163
+ | `problem` | confirmed, and notifications have gone out |
164
+ | `pending` | failing, but inside the confirm hold — **nobody has been notified yet** |
165
+ | `recovering` | reporting ok again while the incident is still open |
166
+ | `no_data` | nothing usable arrived; a silent location counts toward the agreement |
167
+ | `ok` | healthy |
168
+
169
+ `locations.failing` / `.unknown` / `.ok` is the evidence the verdict was taken
170
+ from. A location that has never reported stays in `unknown` — that is a real
171
+ state, not a missing one.
172
+
173
+ ### Migrating from 0.4.x
174
+
175
+ **1.5.0 targets API v2 only.** Your existing 0.4.x code keeps working against
176
+ the server — API v1 is unchanged and supported — but it must stay on the 0.4.x
177
+ SDK. Pin `uptimer-python-sdk<1` if you are not ready to move.
178
+
179
+ What changed:
180
+
181
+ | 0.4.x (API v1) | 1.5.0 (API v2) |
182
+ |---|---|
183
+ | `client.v1.workspaces` | `client.v2.workspaces` |
184
+ | `client.v1.regions` | `client.v2.locations` |
185
+ | `client.v1.rules` | `client.v2.monitoring.websites` |
186
+ | `Region` | `Location` |
187
+ | `Rule`, `CreateRuleRequest` | `WebsiteMonitor`, `CreateWebsiteMonitorRequest` |
188
+ | `regions=[...]` | `locations=[...]` |
189
+ | — | `agreement="any"|"majority"|"all"` |
190
+ | — | `client.v2.incidents` |
191
+ | `from uptimer.models import …` | `from uptimer.models.v2 import …` |
192
+
193
+ **The version namespace stays, and now covers the types too.** As in 0.4.x,
194
+ resources sit under the API version that serves them — `client.v1.*` becomes
195
+ `client.v2.*`, not a bare `client.*` — and the models follow: import them from
196
+ `uptimer.models.v2`, not from `uptimer.models`. The HTTP API is versioned by
197
+ path, so the SDK shows the same thing rather than hiding it. There are no
198
+ root-level aliases for either surface, so a stale flat import fails loudly
199
+ instead of silently binding to the wrong thing.
200
+
201
+ The deserialization exceptions (`ModelError`, `TypeMismatchError`, …) stay on
202
+ `uptimer.models`: the same error is raised whichever API version produced the
203
+ payload, so versioning them would say something untrue.
204
+
205
+ Why `monitoring.websites` rather than `monitors`: website monitoring is a
206
+ built-in template, not the general model. Keeping the bare name free lets other
207
+ monitor types arrive later without renaming this one.
208
+
209
+ `client.version()`, `client.check_compatibility()` and
210
+ `client.ensure_compatible()` are unchanged and stay on the client itself —
211
+ `/version` is a shared global endpoint, not a versioned one, so it works against
212
+ any server, including one too old for the rest of this SDK.
213
+
214
+ **Why 1.5.0 and not 1.0.0:** the SDK's major.minor tracks the uptimer release it
215
+ targets, so the version is the compatibility statement — 1.5.x speaks to uptimer
216
+ 1.5.0 and later. Patch numbers are independent, so an SDK fix can ship without a
217
+ server release.
218
+
219
+ Also, check out the [examples directory](https://github.com/myuptime-info/uptimer-python-sdk/tree/main/examples).
220
+
221
+ ### Development Setup
222
+
223
+ 1. Clone the repository:
224
+
225
+ ```bash
226
+ git clone <repository-url>
227
+ cd uptimer-python-sdk
228
+ ```
229
+
230
+ 2. Install dependencies:
231
+
232
+ ```bash
233
+ uv sync --dev
234
+ # for integration tests
235
+ uv run playwright install chromium
236
+ ```
237
+
238
+ 3. Run tests:
239
+
240
+ ```bash
241
+ uv run pytest
242
+ # integration
243
+ docker pull ghcr.io/myuptime-info/uptimer:1.3.0
244
+ docker run -p 2517:2517 ghcr.io/myuptime-info/uptimer:1.3.0
245
+ UPTIMER_URL=http://localhost:2517 uv run --integration
246
+ ```
247
+
248
+ 4. Run linting:
249
+
250
+ ```bash
251
+ uv run ruff check .
252
+ uv run mypy src
253
+ ```
254
+
255
+ 5. Format code:
256
+
257
+ ```bash
258
+ uv run ruff format .
259
+ ```
260
+
261
+ 6. Run pre-commit hooks:
262
+
263
+ ```bash
264
+ uv run pre-commit run --all-files
265
+ ```
266
+
267
+ ## Third-Party Licenses
268
+
269
+ This project uses the following third-party libraries:
270
+
271
+ ### Production Dependencies
272
+
273
+ - **httpx** (BSD 3-Clause License) - HTTP client for Python
274
+
275
+ ### Development Dependencies
276
+
277
+ - **mypy** (Apache 2.0 License) - Static type checker
278
+ - **playwright** (Apache 2.0 License) - Browser automation
279
+ - **pre-commit** (MIT License) - Git hooks framework
280
+ - **pytest** (MIT License) - Testing framework
281
+ - **pytest-cov** (MIT License) - Coverage plugin for pytest
282
+ - **pytest-httpx** (MIT License) - HTTPX plugin for pytest
283
+ - **pytest-playwright** (MIT License) - Playwright plugin for pytest
284
+ - **responses** (Apache 2.0 License) - Mock library for requests
285
+ - **ruff** (MIT License) - Fast Python linter and formatter
286
+
287
+ All third-party licenses are compatible with the MIT License used by this project. Note that the BSD 3-Clause License (used by httpx) includes an additional restriction prohibiting the use of the copyright holder's name for endorsement without permission.
@@ -0,0 +1,271 @@
1
+ # Uptimer Python SDK
2
+
3
+ A Python SDK for hosted and self-hosted Uptimer.
4
+
5
+ * [Hosted Uptimer](https://myuptime.info)
6
+ * [Self-hosted documentation](https://uptimer.myuptime.info)
7
+ * [PyPI package](https://pypi.org/project/uptimer-python-sdk/)
8
+ * [Uptimer resources](https://myuptime.info/resources)
9
+ * [Product updates](https://myuptime.info/product-updates)
10
+
11
+ ## License
12
+
13
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
14
+
15
+ For third-party license information, see the [NOTICE](NOTICE) file.
16
+
17
+ ## Installation
18
+
19
+ ```shell
20
+ pip install uptimer-python-sdk
21
+ ```
22
+
23
+ or
24
+ ```shell
25
+ uv add uptimer-python-sdk
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ### Create client
31
+
32
+ #### self-hosted
33
+
34
+ ```python
35
+ from uptimer.client import UptimerClient
36
+ client = UptimerClient(
37
+ api_key="your-api-key-here",
38
+ base_url="http://127.0.0.1:2517/api", # or your custom base URL
39
+ )
40
+ ```
41
+
42
+ #### cloud
43
+ ```python
44
+ from uptimer.client import UptimerCloudClient
45
+ client = UptimerCloudClient(
46
+ api_key="your-api-key-here",
47
+ )
48
+ ```
49
+
50
+ ### Basic example
51
+
52
+ ```python
53
+ from uptimer.client import UptimerClient
54
+ from uptimer.errors import (
55
+ DefaultUptimerApiError,
56
+ IncompatibleServerError,
57
+ UptimerError,
58
+ UptimerInvalidHttpCodeError,
59
+ )
60
+ from uptimer.models.v2 import (
61
+ AGREEMENT_MAJORITY,
62
+ CreateWebsiteMonitorRequest,
63
+ UpdateWebsiteMonitorRequest,
64
+ WebsiteMonitorRequest,
65
+ WebsiteMonitorResponse,
66
+ WebsiteMonitorResponseBody,
67
+ )
68
+
69
+ client = UptimerClient(
70
+ api_key="your-api-key-here",
71
+ base_url="http://127.0.0.1:2517/api", # or your custom base URL
72
+ )
73
+
74
+ # Optional: fail fast with a message that names the fix, rather than a 404 on
75
+ # the first real call.
76
+ print("server:", client.check_compatibility())
77
+
78
+ workspace = client.v2.workspaces.all()[0]
79
+ locations = [location.name for location in client.v2.locations.all()]
80
+
81
+ monitor = client.v2.monitoring.websites.create(
82
+ CreateWebsiteMonitorRequest(
83
+ name="Checkout API",
84
+ interval=60, # seconds between probes
85
+ workspace_id=workspace.id,
86
+ request=WebsiteMonitorRequest(
87
+ url="https://checkout.example/health",
88
+ method="GET", # one of GET, POST, PATCH, OPTIONS
89
+ content_type="application/json",
90
+ data="",
91
+ ),
92
+ response=WebsiteMonitorResponse(
93
+ statuses=[200, 201], # any of these means the site is up
94
+ body=WebsiteMonitorResponseBody(content="ok"), # expected substring
95
+ ),
96
+ locations=locations,
97
+ # How many locations must report a problem before this monitor does:
98
+ # "any", "majority" or "all". Omit to keep the server default.
99
+ agreement=AGREEMENT_MAJORITY,
100
+ ),
101
+ )
102
+
103
+ monitor = client.v2.monitoring.websites.update(
104
+ monitor.id,
105
+ UpdateWebsiteMonitorRequest(
106
+ name="Checkout API",
107
+ interval=120,
108
+ request=WebsiteMonitorRequest(url="https://checkout.example/health", method="GET"),
109
+ response=WebsiteMonitorResponse(statuses=[200]),
110
+ locations=locations,
111
+ # Omitting agreement here keeps the stored one.
112
+ ),
113
+ )
114
+
115
+ # What is wrong right now. Only open incidents come back.
116
+ for incident in client.v2.incidents.all(workspace.id):
117
+ print(incident.monitor_name, incident.status, incident.locations.failing)
118
+
119
+ try:
120
+ client.v2.monitoring.websites.delete(monitor.id)
121
+ except DefaultUptimerApiError as e:
122
+ # error responses from the uptimer server
123
+ print(
124
+ e.message, # user message
125
+ e.code, # error id
126
+ e.error_type, # class of error
127
+ e.details, # detailed message for a developer
128
+ )
129
+ except IncompatibleServerError as e:
130
+ # the server does not provide API v2 — see Migrating from 0.4.x below
131
+ print(e)
132
+ except UptimerInvalidHttpCodeError as e:
133
+ # the uptimer api always returns 200; anything else is a transport error.
134
+ # a 404 really is "no such URL", not "no object with that id".
135
+ print(e.url, e.status_code)
136
+ except UptimerError: # base error, if you need one
137
+ raise
138
+ ```
139
+
140
+ ### Incident status
141
+
142
+ `client.v2.incidents.all()` returns only **open** incidents. `status` carries the
143
+ same words the Uptimer screens show, so a client and the UI cannot disagree:
144
+
145
+ | status | meaning |
146
+ |---|---|
147
+ | `problem` | confirmed, and notifications have gone out |
148
+ | `pending` | failing, but inside the confirm hold — **nobody has been notified yet** |
149
+ | `recovering` | reporting ok again while the incident is still open |
150
+ | `no_data` | nothing usable arrived; a silent location counts toward the agreement |
151
+ | `ok` | healthy |
152
+
153
+ `locations.failing` / `.unknown` / `.ok` is the evidence the verdict was taken
154
+ from. A location that has never reported stays in `unknown` — that is a real
155
+ state, not a missing one.
156
+
157
+ ### Migrating from 0.4.x
158
+
159
+ **1.5.0 targets API v2 only.** Your existing 0.4.x code keeps working against
160
+ the server — API v1 is unchanged and supported — but it must stay on the 0.4.x
161
+ SDK. Pin `uptimer-python-sdk<1` if you are not ready to move.
162
+
163
+ What changed:
164
+
165
+ | 0.4.x (API v1) | 1.5.0 (API v2) |
166
+ |---|---|
167
+ | `client.v1.workspaces` | `client.v2.workspaces` |
168
+ | `client.v1.regions` | `client.v2.locations` |
169
+ | `client.v1.rules` | `client.v2.monitoring.websites` |
170
+ | `Region` | `Location` |
171
+ | `Rule`, `CreateRuleRequest` | `WebsiteMonitor`, `CreateWebsiteMonitorRequest` |
172
+ | `regions=[...]` | `locations=[...]` |
173
+ | — | `agreement="any"|"majority"|"all"` |
174
+ | — | `client.v2.incidents` |
175
+ | `from uptimer.models import …` | `from uptimer.models.v2 import …` |
176
+
177
+ **The version namespace stays, and now covers the types too.** As in 0.4.x,
178
+ resources sit under the API version that serves them — `client.v1.*` becomes
179
+ `client.v2.*`, not a bare `client.*` — and the models follow: import them from
180
+ `uptimer.models.v2`, not from `uptimer.models`. The HTTP API is versioned by
181
+ path, so the SDK shows the same thing rather than hiding it. There are no
182
+ root-level aliases for either surface, so a stale flat import fails loudly
183
+ instead of silently binding to the wrong thing.
184
+
185
+ The deserialization exceptions (`ModelError`, `TypeMismatchError`, …) stay on
186
+ `uptimer.models`: the same error is raised whichever API version produced the
187
+ payload, so versioning them would say something untrue.
188
+
189
+ Why `monitoring.websites` rather than `monitors`: website monitoring is a
190
+ built-in template, not the general model. Keeping the bare name free lets other
191
+ monitor types arrive later without renaming this one.
192
+
193
+ `client.version()`, `client.check_compatibility()` and
194
+ `client.ensure_compatible()` are unchanged and stay on the client itself —
195
+ `/version` is a shared global endpoint, not a versioned one, so it works against
196
+ any server, including one too old for the rest of this SDK.
197
+
198
+ **Why 1.5.0 and not 1.0.0:** the SDK's major.minor tracks the uptimer release it
199
+ targets, so the version is the compatibility statement — 1.5.x speaks to uptimer
200
+ 1.5.0 and later. Patch numbers are independent, so an SDK fix can ship without a
201
+ server release.
202
+
203
+ Also, check out the [examples directory](https://github.com/myuptime-info/uptimer-python-sdk/tree/main/examples).
204
+
205
+ ### Development Setup
206
+
207
+ 1. Clone the repository:
208
+
209
+ ```bash
210
+ git clone <repository-url>
211
+ cd uptimer-python-sdk
212
+ ```
213
+
214
+ 2. Install dependencies:
215
+
216
+ ```bash
217
+ uv sync --dev
218
+ # for integration tests
219
+ uv run playwright install chromium
220
+ ```
221
+
222
+ 3. Run tests:
223
+
224
+ ```bash
225
+ uv run pytest
226
+ # integration
227
+ docker pull ghcr.io/myuptime-info/uptimer:1.3.0
228
+ docker run -p 2517:2517 ghcr.io/myuptime-info/uptimer:1.3.0
229
+ UPTIMER_URL=http://localhost:2517 uv run --integration
230
+ ```
231
+
232
+ 4. Run linting:
233
+
234
+ ```bash
235
+ uv run ruff check .
236
+ uv run mypy src
237
+ ```
238
+
239
+ 5. Format code:
240
+
241
+ ```bash
242
+ uv run ruff format .
243
+ ```
244
+
245
+ 6. Run pre-commit hooks:
246
+
247
+ ```bash
248
+ uv run pre-commit run --all-files
249
+ ```
250
+
251
+ ## Third-Party Licenses
252
+
253
+ This project uses the following third-party libraries:
254
+
255
+ ### Production Dependencies
256
+
257
+ - **httpx** (BSD 3-Clause License) - HTTP client for Python
258
+
259
+ ### Development Dependencies
260
+
261
+ - **mypy** (Apache 2.0 License) - Static type checker
262
+ - **playwright** (Apache 2.0 License) - Browser automation
263
+ - **pre-commit** (MIT License) - Git hooks framework
264
+ - **pytest** (MIT License) - Testing framework
265
+ - **pytest-cov** (MIT License) - Coverage plugin for pytest
266
+ - **pytest-httpx** (MIT License) - HTTPX plugin for pytest
267
+ - **pytest-playwright** (MIT License) - Playwright plugin for pytest
268
+ - **responses** (Apache 2.0 License) - Mock library for requests
269
+ - **ruff** (MIT License) - Fast Python linter and formatter
270
+
271
+ All third-party licenses are compatible with the MIT License used by this project. Note that the BSD 3-Clause License (used by httpx) includes an additional restriction prohibiting the use of the copyright holder's name for endorsement without permission.
@@ -0,0 +1,35 @@
1
+ # Dev + git release tasks for the uptimer Python SDK. All tooling via `uv` (uv.lock).
2
+ # PyPI publish lives in the private product repo: /work → task sdk:publish*
3
+ version: '3'
4
+
5
+ tasks:
6
+ build:
7
+ desc: Build the sdist + wheel into dist/ (uv build).
8
+ cmds:
9
+ - rm -rf dist
10
+ - uv build
11
+
12
+ test:
13
+ desc: Run the test suite (uv run pytest).
14
+ cmds:
15
+ - uv run pytest
16
+
17
+ lint:
18
+ desc: Lint + type-check (ruff + mypy).
19
+ cmds:
20
+ - uv run ruff check .
21
+ - uv run mypy src
22
+
23
+ cq:
24
+ desc: 'Code quality gate: lint + test.'
25
+ cmds:
26
+ - task: lint
27
+ - task: test
28
+
29
+ release:
30
+ desc: 'Cut a release: cq, then `cz bump` (version + CHANGELOG + tag) + push. Commit first. Prefer manual tag when version is already set.'
31
+ cmds:
32
+ - task: cq
33
+ - cz bump --yes
34
+ - git push --follow-tags
35
+ - 'echo ">> released v$(cz version --project) — publish from /work: task sdk:publish:test, then task sdk:publish"'
@@ -0,0 +1,21 @@
1
+ """Set up a client and confirm the server speaks API v2."""
2
+
3
+ from uptimer.client import UptimerClient
4
+ from uptimer.errors import IncompatibleServerError
5
+
6
+ client = UptimerClient(
7
+ api_key="your-api-key-here",
8
+ base_url="https://myuptime.info/api", # or your self-hosted URL + /api
9
+ )
10
+
11
+ # Optional but recommended: fail fast with a message that names the fix, rather
12
+ # than a 404 on the first real call.
13
+ try:
14
+ print("Server version:", client.check_compatibility())
15
+ except IncompatibleServerError as exc:
16
+ raise SystemExit(str(exc)) from exc
17
+
18
+ # API v2 lives under client.v2 — the SDK keeps the API version visible, because
19
+ # the API itself is versioned by path.
20
+ print("Available: client.v2.workspaces, client.v2.locations,")
21
+ print(" client.v2.incidents, client.v2.monitoring.websites")
@@ -0,0 +1,10 @@
1
+ """List the workspaces an API key can reach."""
2
+
3
+ from uptimer.client import UptimerClient
4
+
5
+ client = UptimerClient(
6
+ api_key="your-api-key-here", base_url="https://myuptime.info/api"
7
+ )
8
+
9
+ for workspace in client.v2.workspaces.all():
10
+ print(f"{workspace.id} {workspace.name} ({workspace.role})")