Official Python SDK for Openstatus
Project description
Openstatus Python SDK
Official Python SDK for Openstatus — the open-source status page and uptime monitoring platform.
Status: pre-alpha. APIs may change without notice until v1.0.
Table of contents
- Features
- Installation
- Quick start
- Authentication
- Sync and async
- Custom HTTP client
- Services
- Reference
- Error handling
- Recipes
- Migration from the Node SDK
- Development
- License
Features
- Sync and async clients —
OpenstatusClientandAsyncOpenstatusClient, both backed byhttpx. - Typed messages — request and response types are generated from the
upstream protobuf schema, with
.pyistubs for editor and pyright support. - All services covered — Health, Monitor, StatusReport, StatusPage, Maintenance, and Notification.
- JSON over HTTP — uses Connect-RPC's JSON mode; no binary protobuf required at runtime.
- Predictable errors — Connect-style error envelopes are mapped to a typed exception hierarchy.
Installation
pip install openstatus
# or
uv add openstatus
Requires Python 3.10 or newer.
Quick start
from openstatus import OpenstatusClient
from openstatus._gen.openstatus.monitor.v1.assertions_pb2 import (
NumberComparator,
StatusCodeAssertion,
)
from openstatus._gen.openstatus.monitor.v1.http_monitor_pb2 import (
HTTPMethod,
HTTPMonitor,
)
from openstatus._gen.openstatus.monitor.v1.monitor_pb2 import Periodicity, Region
from openstatus._gen.openstatus.monitor.v1.service_pb2 import (
CreateHTTPMonitorRequest,
ListMonitorsRequest,
)
with OpenstatusClient() as client:
created = client.monitor.v1.MonitorService.create_http_monitor(
CreateHTTPMonitorRequest(
monitor=HTTPMonitor(
name="My API",
url="https://api.example.com/health",
method=HTTPMethod.HTTP_METHOD_GET,
periodicity=Periodicity.PERIODICITY_1M,
regions=[Region.REGION_FLY_AMS, Region.REGION_FLY_IAD],
active=True,
status_code_assertions=[
StatusCodeAssertion(
comparator=NumberComparator.NUMBER_COMPARATOR_EQUAL,
target=200,
)
],
)
)
)
print(f"Created monitor id={created.monitor.id}")
monitors = client.monitor.v1.MonitorService.list_monitors(ListMonitorsRequest())
print(f"Total monitors: {monitors.total_size}")
Authentication
The SDK reads OPENSTATUS_API_KEY from the environment by default:
export OPENSTATUS_API_KEY="..."
Or pass it explicitly:
from openstatus import ClientOptions, OpenstatusClient
client = OpenstatusClient(ClientOptions(api_key="..."))
Override the base URL with OPENSTATUS_API_URL or ClientOptions(base_url=...).
The default is https://api.openstatus.dev.
Sync and async
Both clients expose the same nested namespace path
(client.<service>.v1.<ServiceName>.<method>):
from openstatus import OpenstatusClient
from openstatus._gen.openstatus.health.v1.health_pb2 import CheckRequest
with OpenstatusClient() as client:
health = client.health.v1.HealthService.check(CheckRequest())
import asyncio
from openstatus import AsyncOpenstatusClient
from openstatus._gen.openstatus.health.v1.health_pb2 import CheckRequest
async def main() -> None:
async with AsyncOpenstatusClient() as client:
health = await client.health.v1.HealthService.check(CheckRequest())
print(health.status)
asyncio.run(main())
Custom HTTP client
Supply your own httpx.Client to control timeouts, transports, proxies, or
TLS configuration:
import httpx
from openstatus import ClientOptions, OpenstatusClient
http = httpx.Client(
timeout=httpx.Timeout(connect=2.0, read=10.0, write=10.0, pool=10.0),
transport=httpx.HTTPTransport(retries=3),
)
client = OpenstatusClient(ClientOptions(http_client=http))
When you pass a client in, the SDK does not close it; manage its lifetime yourself.
Services
Each method takes a typed protobuf request and returns a typed protobuf
response. Pass per-call headers via the keyword-only headers= argument.
Health
from openstatus._gen.openstatus.health.v1.health_pb2 import CheckRequest
health = client.health.v1.HealthService.check(CheckRequest())
Monitor
from openstatus._gen.openstatus.monitor.v1.service_pb2 import (
GetMonitorRequest,
ListMonitorsRequest,
TriggerMonitorRequest,
)
client.monitor.v1.MonitorService.list_monitors(ListMonitorsRequest())
client.monitor.v1.MonitorService.get_monitor(GetMonitorRequest(id="abc"))
client.monitor.v1.MonitorService.trigger_monitor(TriggerMonitorRequest(id="abc"))
Status report
from openstatus._gen.openstatus.status_report.v1.service_pb2 import ListStatusReportsRequest
client.status_report.v1.StatusReportService.list_status_reports(ListStatusReportsRequest())
Status page
from openstatus._gen.openstatus.status_page.v1.service_pb2 import ListStatusPagesRequest
client.status_page.v1.StatusPageService.list_status_pages(ListStatusPagesRequest())
Maintenance
from openstatus._gen.openstatus.maintenance.v1.service_pb2 import ListMaintenancesRequest
client.maintenance.v1.MaintenanceService.list_maintenances(ListMaintenancesRequest())
Notification
from openstatus._gen.openstatus.notification.v1.service_pb2 import ListNotificationsRequest
client.notification.v1.NotificationService.list_notifications(ListNotificationsRequest())
Reference
Regions
Region constants live in openstatus._gen.openstatus.monitor.v1.monitor_pb2
as Region. Use the descriptor API to enumerate them:
from openstatus._gen.openstatus.monitor.v1.monitor_pb2 import Region
print(list(Region.keys())) # ["REGION_UNSPECIFIED", "REGION_FLY_AMS", ...]
print(Region.Name(1)) # "REGION_FLY_AMS"
print(Region.Value("REGION_FLY_AMS"))
Enums
Common enums:
HTTPMethod(openstatus.monitor.v1.http_monitor_pb2) — request method for HTTP monitors.Periodicity(openstatus.monitor.v1.monitor_pb2) — check frequency.MonitorStatus,Region(openstatus.monitor.v1.monitor_pb2).NumberComparator,StringComparator,RecordComparator(openstatus.monitor.v1.assertions_pb2) — assertion operators.HTTPResponseLogRequestStatus,HTTPResponseLogTrigger,TimeRange(openstatus.monitor.v1.service_pb2).
Error handling
All transport errors derive from OpenstatusError:
from openstatus import (
AuthenticationError,
NotFoundError,
OpenstatusClient,
OpenstatusError,
)
from openstatus._gen.openstatus.monitor.v1.service_pb2 import GetMonitorRequest
with OpenstatusClient() as client:
try:
client.monitor.v1.MonitorService.get_monitor(GetMonitorRequest(id="missing"))
except NotFoundError as err:
print("monitor not found:", err.connect_code, err.http_status)
except AuthenticationError:
print("check OPENSTATUS_API_KEY")
except OpenstatusError as err:
print("openstatus failed:", err.connect_code, err.details)
Each exception carries connect_code, http_status, details, and
raw_body attributes for inspection.
| Connect code | Exception |
|---|---|
unauthenticated |
AuthenticationError |
not_found |
NotFoundError |
invalid_argument |
InvalidArgumentError |
permission_denied |
PermissionDeniedError |
resource_exhausted |
RateLimitError |
unavailable |
ServiceUnavailableError |
| anything else | OpenstatusError |
When the server response is not a Connect error envelope (e.g. a 502 HTML
body from a proxy), ServiceUnavailableError is raised with the raw body
preserved on .raw_body.
Recipes
FastAPI
from fastapi import Depends, FastAPI
from openstatus import OpenstatusClient
app = FastAPI()
_client = OpenstatusClient()
def get_client() -> OpenstatusClient:
return _client
@app.on_event("shutdown")
def shutdown() -> None:
_client.close()
@app.get("/monitors")
def monitors(client: OpenstatusClient = Depends(get_client)):
from openstatus._gen.openstatus.monitor.v1.service_pb2 import ListMonitorsRequest
res = client.monitor.v1.MonitorService.list_monitors(ListMonitorsRequest())
return {"total": res.total_size}
Django
# myapp/openstatus.py
from django.conf import settings
from openstatus import ClientOptions, OpenstatusClient
_singleton: OpenstatusClient | None = None
def client() -> OpenstatusClient:
global _singleton
if _singleton is None:
_singleton = OpenstatusClient(ClientOptions(api_key=settings.OPENSTATUS_API_KEY))
return _singleton
Migration from the Node SDK
- Method names:
MonitorService.listMonitors(...)→MonitorService.list_monitors(...). - Message field access:
monitor.fooBar→monitor.foo_bar. - Enum lookups:
Region[value](Node) →Region.Name(value)(Python). - Headers per call: pass as keyword argument
headers={...}. - Authentication: same
OPENSTATUS_API_KEYenv var andClientOptions(api_key=...)parameter.
Development
uv sync
uv run pytest tests/unit
uv run pyright
uv run ruff check .
Integration tests against the live API:
OPENSTATUS_API_KEY=... uv run pytest tests/integration
Regenerating the protobuf message classes:
bash scripts/regen.sh
See CONTRIBUTING.md for details.
For the design rationale (why JSON over HTTP, why a pinned Buf archive, why sync+async from day one), see docs/decisions.md.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file openstatus-0.1.0.tar.gz.
File metadata
- Download URL: openstatus-0.1.0.tar.gz
- Upload date:
- Size: 92.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
84cfc6c8efa3a0713c0f2b168e0558a5a4e1d3efb1ee8db45c1b5581e89e105b
|
|
| MD5 |
06b357e36c18cae108ba6f9ba40389a9
|
|
| BLAKE2b-256 |
03524e632260ed22177fc72402ccf82157fb726a6ea4338b0fea17c601ee40c0
|
Provenance
The following attestation bundles were made for openstatus-0.1.0.tar.gz:
Publisher:
release.yml on openstatusHQ/sdk-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openstatus-0.1.0.tar.gz -
Subject digest:
84cfc6c8efa3a0713c0f2b168e0558a5a4e1d3efb1ee8db45c1b5581e89e105b - Sigstore transparency entry: 1670390749
- Sigstore integration time:
-
Permalink:
openstatusHQ/sdk-python@3607a145b5c0c09ac5be7e49267a3768dccceb93 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/openstatusHQ
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3607a145b5c0c09ac5be7e49267a3768dccceb93 -
Trigger Event:
push
-
Statement type:
File details
Details for the file openstatus-0.1.0-py3-none-any.whl.
File metadata
- Download URL: openstatus-0.1.0-py3-none-any.whl
- Upload date:
- Size: 132.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e3de81ef0c9507d982f846464b6f47474e692133699bc303a354b6267342182c
|
|
| MD5 |
82689fb226debe28c15580c130bb524c
|
|
| BLAKE2b-256 |
5370a9c10d1509f192a39725aad4cefe15da5e64dbb97cf5cbea23a37c90bd5d
|
Provenance
The following attestation bundles were made for openstatus-0.1.0-py3-none-any.whl:
Publisher:
release.yml on openstatusHQ/sdk-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openstatus-0.1.0-py3-none-any.whl -
Subject digest:
e3de81ef0c9507d982f846464b6f47474e692133699bc303a354b6267342182c - Sigstore transparency entry: 1670390967
- Sigstore integration time:
-
Permalink:
openstatusHQ/sdk-python@3607a145b5c0c09ac5be7e49267a3768dccceb93 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/openstatusHQ
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3607a145b5c0c09ac5be7e49267a3768dccceb93 -
Trigger Event:
push
-
Statement type: