Python SDK for the Hyperping uptime monitoring and incident management API.
Requires Python 3.11+.
pip install hyperping
# or
uv add hyperpingfromhyperpingimportHyperpingClient, IncidentCreate, LocalizedTextwithHyperpingClient(api_key="sk_...") asclient:
# List all monitorsmonitors=client.list_monitors()
forminmonitors:
print(f"{m.name}: {'down'ifm.downelse'up'}")
# Open an incidentincident=client.create_incident(
IncidentCreate(
title=LocalizedText(en="Service degradation"),
text=LocalizedText(en="Investigating elevated error rates"),
statuspages=["sp_your_uuid"],
)
)
# Resolve itclient.resolve_incident(incident.uuid, "All systems operational")An async-first client is available for use with asyncio and anyio-based frameworks:
fromhyperpingimportAsyncHyperpingClientasyncdefmain():
asyncwithAsyncHyperpingClient(api_key="sk_...") asclient:
monitors=awaitclient.list_monitors()
forminmonitors:
print(f"{m.name}: {'down'ifm.downelse'up'}")
outage=awaitclient.acknowledge_outage("out_uuid", message="On it")The async client supports all the same resources, retry behaviour, and circuit breaker as the sync client. Use RetryConfig and CircuitBreakerConfig in exactly the same way.
An async MCP client is also available:
fromhyperpingimportAsyncHyperpingMcpClientasyncdefmain():
asyncwithAsyncHyperpingMcpClient(api_key="sk_...") asmcp:
summary=awaitmcp.get_status_summary()
members=awaitmcp.list_team_members()
anomalies=awaitmcp.get_monitor_anomalies("mon_uuid")Pass your API key directly or via environment variable:
importosfromhyperpingimportHyperpingClient# Constructor paramclient=HyperpingClient(api_key="sk_...")
# From environmentclient=HyperpingClient(api_key=os.environ["HYPERPING_API_KEY"])monitors=client.list_monitors()
monitor=client.get_monitor("mon_uuid")
created=client.create_monitor(MonitorCreate(name="API", url="https://api.example.com"))
client.pause_monitor("mon_uuid")
client.resume_monitor("mon_uuid")
client.delete_monitor("mon_uuid")
# Reportsreports=client.get_all_reports(period="30d")
report=client.get_monitor_report("mon_uuid", period="7d")incidents=client.list_incidents()
incident=client.get_incident("inci_uuid")
created=client.create_incident(IncidentCreate(...))
client.add_incident_update("inci_uuid", AddIncidentUpdateRequest(...))
client.resolve_incident("inci_uuid", "Fixed")
client.delete_incident("inci_uuid")windows=client.list_maintenance()
window=client.get_maintenance("mw_uuid")
created=client.create_maintenance(MaintenanceCreate(...))
client.update_maintenance("mw_uuid", MaintenanceUpdate(name="New name"))
client.delete_maintenance("mw_uuid")
# Helpersactive=client.get_active_maintenance()
in_maint=client.is_monitor_in_maintenance("mon_uuid")outages=client.list_outages() # auto-fetches all pagesoutages=client.list_outages(page=0) # single pageclient.acknowledge_outage("out_uuid", message="On it")
client.resolve_outage("out_uuid", message="Fixed")
client.escalate_outage("out_uuid")pages=client.list_status_pages(search="prod") # auto-fetches all pagespages=client.list_status_pages(page=0) # single pagepage=client.get_status_page("sp_uuid")
created=client.create_status_page(StatusPageCreate(name="Prod", subdomain="prod-status"))
client.update_status_page("sp_uuid", StatusPageUpdate(name="Production Status"))
client.delete_status_page("sp_uuid")
# Subscriberssubs=client.list_subscribers("sp_uuid") # auto-fetches all pagessub=client.add_subscriber("sp_uuid", "user@example.com")
client.remove_subscriber("sp_uuid", sub.id)Some Hyperping features are only available via the MCP server (JSON-RPC 2.0),
not the REST API. Use HyperpingMcpClient for these:
fromhyperpingimportHyperpingMcpClientwithHyperpingMcpClient(api_key="sk_...") asmcp:
# Status & reportingsummary=mcp.get_status_summary()
mtta=mcp.get_monitor_mtta("mon_uuid")
mttr=mcp.get_monitor_mttr("mon_uuid")
response_time=mcp.get_monitor_response_time("mon_uuid")
# On-call & escalationschedules=mcp.list_on_call_schedules()
policies=mcp.list_escalation_policies()
members=mcp.list_team_members()
# Observabilityanomalies=mcp.get_monitor_anomalies("mon_uuid")
logs=mcp.get_monitor_http_logs("mon_uuid")
alerts=mcp.list_recent_alerts()
# Integrationsintegrations=mcp.list_integrations()
# Outage timeline & monitor searchtimeline=mcp.get_outage_timeline("out_uuid")
results=mcp.search_monitors_by_name("api")The MCP client uses the same API key as HyperpingClient. All methods return
plain dicts/lists; use the exported Pydantic models (e.g., OnCallSchedule,
EscalationPolicy) for validation if needed.
The Hyperping MCP server (https://api.hyperping.io/v1/mcp) is
documented by Hyperping as stateless over HTTP
and rate-limits per API key. The publicly documented limit is 300 requests per
minute shared with the REST API
(rate-limit docs), but
the server also enforces a separate, undocumented cap on the initialize
handshake (observed around 5/minute). Because every new HyperpingMcpClient
instance must perform the MCP initialize handshake on its first call,
instantiating the client in a hot path or running several short-lived processes
against one key will trip this cap.
Operational guidance:
- Create one
HyperpingMcpClientper process and reuse it. Do not instantiate it inside a loop. The first call performs the handshake; subsequent calls reuse it for the life of the client. - Catch
HyperpingRateLimitErrorand honourretry_after. Rate-limit signals arrive two ways: as HTTP 429 (with a standardRetry-Afterheader) and as a JSON-RPC server error (code: -32000, HTTP 200) oninitialize. Both surface asHyperpingRateLimitErrorwithretry_afterparsed from whichever signal was used. Thestatus_codeattribute is429or200, matching the underlying signal; cool-off short-circuits preserve the originating status code so callers can disambiguate the two buckets. - Use
ensure_initialized()for startup health checks. Calling it once on service boot lets you fail fast if the key is already at theinitializecap, instead of failing on the first business call. - Several workloads on one key collide on the
initializecap. A weekly cron, a watchdog daemon, and a developer running the CLI cannot all warm up the same API key inside one minute. Use one long-lived process per workload, or separate API keys per workload if your plan allows. - After a rate-limit on
initialize, the SDK latches a cool-off so that subsequentcall_toolinvocations on the same client fail fast withHyperpingRateLimitError(no extra HTTP traffic) untilretry_afterelapses. This prevents accidentally burning more slots from the bucket. The latch is per-HyperpingMcpClientinstance and per-process; it does not coordinate across separate Python processes sharing the same API key, so multi-process setups still need the workload-separation advice above.
fromhyperpingimportHyperpingMcpClient, HyperpingRateLimitErrormcp=HyperpingMcpClient(api_key="sk_...")
try:
mcp.ensure_initialized()
exceptHyperpingRateLimitErrorase:
print(f"MCP cold-start rate-limited; retry in {e.retry_after}s")
raisesummary=mcp.get_status_summary()checks=client.list_healthchecks()
check=client.get_healthcheck("hc_uuid")
created=client.create_healthcheck(HealthcheckCreate(name="Nightly Job", period=86400, grace=3600))
client.update_healthcheck("hc_uuid", HealthcheckUpdate(grace=7200))
client.pause_healthcheck("hc_uuid")
client.resume_healthcheck("hc_uuid")
client.delete_healthcheck("hc_uuid")fromhyperpingimport (
HyperpingAPIError,
HyperpingAuthError,
HyperpingNotFoundError,
HyperpingRateLimitError,
HyperpingValidationError,
)
try:
monitor=client.get_monitor("mon_uuid")
exceptHyperpingNotFoundError:
print("Monitor not found")
exceptHyperpingRateLimitErrorase:
print(f"Rate limited. Retry after {e.retry_after}s")
exceptHyperpingAuthError:
print("Invalid API key")
exceptHyperpingAPIErrorase:
print(f"API error [{e.status_code}]: {e.message}")
print(f"Request ID: {e.request_id}")The SDK retries automatically on transient errors (5xx, 429) with exponential backoff and jitter. A circuit breaker prevents cascading failures.
fromhyperpingimportHyperpingClientfromhyperping.clientimportRetryConfig, CircuitBreakerConfigclient=HyperpingClient(
api_key="sk_...",
retry_config=RetryConfig(
max_retries=3,
initial_delay=1.0,
max_delay=30.0,
backoff_factor=2.0,
),
circuit_breaker_config=CircuitBreakerConfig(
failure_threshold=5,
recovery_timeout=60.0,
),
)By default a single shared circuit breaker covers every request. If one endpoint flakes, every other endpoint is also blocked. Enable per_endpoint_circuit_breaker=True to keep one breaker per endpoint so a failing endpoint does not punish healthy ones:
client=HyperpingClient(
api_key="sk_...",
per_endpoint_circuit_breaker=True,
)
# Inspect state for an endpoint. The breaker key is canonicalised to the# matching `Endpoint` prefix, so all sub-resource paths share a bucket:fromhyperpingimportCircuitState, Endpointstate=client.circuit_breaker_state_for(str(Endpoint.MONITORS))
# /v1/monitors, /v1/monitors/mon_abc and /v1/monitors/mon_abc/reports all# report the same state — they share the `/v1/monitors` breaker.assertclient.circuit_breaker_state_for(f"{Endpoint.MONITORS}/mon_abc") ==stateassertstatein {CircuitState.CLOSED, CircuitState.HALF_OPEN, CircuitState.OPEN}If you need different bucketing (e.g. one breaker per resource UUID, or a single breaker per HTTP verb), pass a breaker_key_fn:
defper_resource(path: str) ->str:
# one breaker per literal request pathreturnpath.split("?", 1)[0]
client=HyperpingClient(
api_key="sk_...",
per_endpoint_circuit_breaker=True,
breaker_key_fn=per_resource,
)| Option | Type | Default | Description |
|---|---|---|---|
per_endpoint_circuit_breaker | bool | False | When True, maintain a separate circuit breaker keyed by request endpoint instead of using one shared breaker. The same circuit_breaker_config applies to every per-endpoint breaker. The shared breaker remains accessible via client.circuit_breaker. |
breaker_key_fn | Callable[[str], str] | None | None | Override the default endpoint-prefix bucketing. Receives the request path and returns the breaker key. Default behaviour collapses every path under the matching Endpoint prefix so the breaker set stays bounded (one per Endpoint); a custom function takes responsibility for keeping the key set bounded. Ignored unless per_endpoint_circuit_breaker=True. |
State for any path is readable via client.circuit_breaker_state_for(path). In the default (single-breaker) mode this returns the shared breaker's state for any path, so the call is always safe regardless of the flag. The same options and method are available on AsyncHyperpingClient.
This package ships a py.typed marker (PEP 561) and is fully typed. Works out of the box with mypy and pyright.
See CONTRIBUTING.md.
MIT — see LICENSE.