This release is a pre-release and may not be stable for production use.
Uptimer Python SDK
A Python SDK for hosted and self-hosted Uptimer.
License
This project is licensed under the MIT License - see the LICENSE file for details.
For third-party license information, see the NOTICE file.
Installation
pip install uptimer-python-sdk
or
uv add uptimer-python-sdk
Usage
Create client
self-hosted
from uptimer.client import UptimerClient
client = UptimerClient(
api_key="your-api-key-here",
base_url="http://127.0.0.1:2517/api", # or your custom base URL
)
cloud
from uptimer.client import UptimerCloudClient
client = UptimerCloudClient(
api_key="your-api-key-here",
)
Basic example
from uptimer.client import UptimerClient
from uptimer.errors import (
DefaultUptimerApiError,
IncompatibleServerError,
UptimerError,
UptimerInvalidHttpCodeError,
)
from uptimer.models.v2 import (
AGREEMENT_MAJORITY,
CreateWebsiteMonitorRequest,
UpdateWebsiteMonitorRequest,
WebsiteMonitorRequest,
WebsiteMonitorResponse,
WebsiteMonitorResponseBody,
)
client = UptimerClient(
api_key="your-api-key-here",
base_url="http://127.0.0.1:2517/api", # or your custom base URL
)
# Optional: fail fast with a message that names the fix, rather than a 404 on
# the first real call.
print("server:", client.check_compatibility())
workspace = client.v2.workspaces.all()[0]
locations = [location.name for location in client.v2.locations.all()]
monitor = client.v2.monitoring.websites.create(
CreateWebsiteMonitorRequest(
name="Checkout API",
interval=60, # seconds between probes
workspace_id=workspace.id,
request=WebsiteMonitorRequest(
url="https://checkout.example/health",
method="GET", # one of GET, POST, PATCH, OPTIONS
content_type="application/json",
data="",
),
response=WebsiteMonitorResponse(
statuses=[200, 201], # any of these means the site is up
body=WebsiteMonitorResponseBody(content="ok"), # expected substring
),
locations=locations,
# How many locations must report a problem before this monitor does:
# "any", "majority" or "all". Omit to keep the server default.
agreement=AGREEMENT_MAJORITY,
),
)
monitor = client.v2.monitoring.websites.update(
monitor.id,
UpdateWebsiteMonitorRequest(
name="Checkout API",
interval=120,
request=WebsiteMonitorRequest(url="https://checkout.example/health", method="GET"),
response=WebsiteMonitorResponse(statuses=[200]),
locations=locations,
# Omitting agreement here keeps the stored one.
),
)
# What is wrong right now. Only open incidents come back.
for incident in client.v2.incidents.all(workspace.id):
print(incident.monitor_name, incident.status, incident.locations.failing)
try:
client.v2.monitoring.websites.delete(monitor.id)
except DefaultUptimerApiError as e:
# error responses from the uptimer server
print(
e.message, # user message
e.code, # error id
e.error_type, # class of error
e.details, # detailed message for a developer
)
except IncompatibleServerError as e:
# the server does not provide API v2 — see Migrating from 0.4.x below
print(e)
except UptimerInvalidHttpCodeError as e:
# the uptimer api always returns 200; anything else is a transport error.
# a 404 really is "no such URL", not "no object with that id".
print(e.url, e.status_code)
except UptimerError: # base error, if you need one
raise
Reporting your own observations
Uptimer probes websites itself. For anything else — a cron job, a queue worker, a nightly export — you add a custom signal to a subject in the Uptimer UI and report to it yourself.
Requires Uptimer 1.6.0 or later, and a custom heartbeat or event signal. The platform HTTP signal of a website monitor is written by Uptimer's own probe and refuses posted observations.
from uptimer.client import UptimerClient
from uptimer.models.v2 import (
OBSERVATION_STATUS_OK,
OBSERVATION_STATUS_PROBLEM,
CreateObservationRequest,
)
client = UptimerClient(
api_key="your-api-key-here",
base_url="http://127.0.0.1:2517/api",
)
# The two slugs are the address: the subject, and the signal within it. Both
# are shown on the signal's page in the Uptimer UI.
observations = client.v2.subjects("checkout-api").signals("worker-pulse").observations
# A heartbeat: "I ran, and I am fine."
stored = observations.create(CreateObservationRequest(status=OBSERVATION_STATUS_OK))
# Everything except status is optional.
stored = observations.create(
CreateObservationRequest(
status=OBSERVATION_STATUS_PROBLEM,
observed_at="2026-08-30T12:00:00Z", # RFC 3339; omit to mean "now"
value=0.0, # optional numeric reading
error="queue backlog over threshold",
labels={"instance": "worker-3", "env": "prod"},
),
)
print(stored.accepted, stored.reject_reason)
accepted reports acceptance, not health: it says Uptimer stored the
observation and may evaluate it, not that anything is wrong or fine. Whether an
observation raises an incident is decided by a rule that selects the signal.
An observation Uptimer keeps but will not evaluate — one stamped too far in the
future, say — comes back with accepted=False and a reject_reason such as
clock_skew. It is returned, not raised: it was received. An exception
means nothing was stored.
Retries are safe. An observation is identified by its signal, its observed_at
and its labels, so re-sending the same one replaces it rather than counting
twice.
Incident status
client.v2.incidents.all() returns only open incidents. status carries the
same words the Uptimer screens show, so a client and the UI cannot disagree:
| status | meaning |
|---|---|
problem |
confirmed, and notifications have gone out |
pending |
failing, but inside the confirm hold — nobody has been notified yet |
recovering |
reporting ok again while the incident is still open |
no_data |
nothing usable arrived; a silent location counts toward the agreement |
ok |
healthy |
locations.failing / .unknown / .ok is the evidence the verdict was taken
from. A location that has never reported stays in unknown — that is a real
state, not a missing one.
Migrating from 0.4.x
1.5.0 targets API v2 only. Your existing 0.4.x code keeps working against
the server — API v1 is unchanged and supported — but it must stay on the 0.4.x
SDK. Pin uptimer-python-sdk<1 if you are not ready to move.
What changed:
| 0.4.x (API v1) | 1.5.0 (API v2) |
|---|---|
client.v1.workspaces |
client.v2.workspaces |
client.v1.regions |
client.v2.locations |
client.v1.rules |
client.v2.monitoring.websites |
Region |
Location |
Rule, CreateRuleRequest |
WebsiteMonitor, CreateWebsiteMonitorRequest |
regions=[...] |
locations=[...] |
| — | agreement="any"|"majority"|"all" |
| — | client.v2.incidents |
from uptimer.models import … |
from uptimer.models.v2 import … |
The version namespace stays, and now covers the types too. As in 0.4.x,
resources sit under the API version that serves them — client.v1.* becomes
client.v2.*, not a bare client.* — and the models follow: import them from
uptimer.models.v2, not from uptimer.models. The HTTP API is versioned by
path, so the SDK shows the same thing rather than hiding it. There are no
root-level aliases for either surface, so a stale flat import fails loudly
instead of silently binding to the wrong thing.
The deserialization exceptions (ModelError, TypeMismatchError, …) stay on
uptimer.models: the same error is raised whichever API version produced the
payload, so versioning them would say something untrue.
Why monitoring.websites rather than monitors: website monitoring is a
built-in template, not the general model. Keeping the bare name free lets other
monitor types arrive later without renaming this one.
client.version(), client.check_compatibility() and
client.ensure_compatible() are unchanged and stay on the client itself —
/version is a shared global endpoint, not a versioned one, so it works against
any server, including one too old for the rest of this SDK.
Why 1.5.0 and not 1.0.0: the SDK's major.minor tracks the uptimer release it targets, so the version is the compatibility statement — 1.5.x speaks to uptimer 1.5.0 and later. Patch numbers are independent, so an SDK fix can ship without a server release.
Also, check out the examples directory.
Development Setup
- Clone the repository:
git clone <repository-url>
cd uptimer-python-sdk
- Install dependencies:
uv sync --dev
# for integration tests
uv run playwright install chromium
- Run tests:
uv run pytest
# integration
docker pull ghcr.io/myuptime-info/uptimer:1.3.0
docker run -p 2517:2517 ghcr.io/myuptime-info/uptimer:1.3.0
UPTIMER_URL=http://localhost:2517 uv run --integration
- Run linting:
uv run ruff check .
uv run mypy src
- Format code:
uv run ruff format .
- Run pre-commit hooks:
uv run pre-commit run --all-files
Third-Party Licenses
This project uses the following third-party libraries:
Production Dependencies
- httpx (BSD 3-Clause License) - HTTP client for Python
Development Dependencies
- mypy (Apache 2.0 License) - Static type checker
- playwright (Apache 2.0 License) - Browser automation
- pre-commit (MIT License) - Git hooks framework
- pytest (MIT License) - Testing framework
- pytest-cov (MIT License) - Coverage plugin for pytest
- pytest-httpx (MIT License) - HTTPX plugin for pytest
- pytest-playwright (MIT License) - Playwright plugin for pytest
- responses (Apache 2.0 License) - Mock library for requests
- ruff (MIT License) - Fast Python linter and formatter
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.
Metadata
Release files for uptimer-python-sdk 1.6.0rc0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| uptimer_python_sdk-1.6.0rc0.tar.gz | 92.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| uptimer_python_sdk-1.6.0rc0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 117.9 kB
Release files / uptimer_python_sdk-1.6.0rc0.tar.gz
| Download URL | uptimer_python_sdk-1.6.0rc0.tar.gz |
|---|---|
| Size | 92.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
60cd774042cddce67afc780af54ce0a92b24638c75a9355d9a4f4fd7732599dc
|
|
BLAKE2b-256 checksum How to use checksums |
741bcc022816c4bf75ad5e28199133a7d26a7318333fb691c05e83f4945d8d6a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / uptimer_python_sdk-1.6.0rc0-py3-none-any.whl
| Download URL | uptimer_python_sdk-1.6.0rc0-py3-none-any.whl |
|---|---|
| Size | 25.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
490dac7c1892b3b31ae2e54a29ab97c710dcbcb945caf1e3f20ee53e22a4f981
|
|
BLAKE2b-256 checksum How to use checksums |
5dd0d8d85f1d2478ff1cd650677e53462a22207122b01a7cc6857fad02e65b7c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|