Skip to main content

Python

Use the requests library to interact with the Sentro API from Python. No dedicated SDK is needed.

Install​

pip install requests

Client Wrapper​

Create a reusable API client class:

# sentro.py

import requests
from typing import Optional
from urllib.parse import urlencode


BASE_URL = "https://api.sentro.dev/v1"


class SentroError(Exception):
"""Raised when the API returns an error response."""

def __init__(self, status_code: int, code: str, message: str):
self.status_code = status_code
self.code = code
self.message = message
super().__init__(f"API Error [{status_code}]: {message}")


class SentroClient:
def __init__(self, api_key: str):
self.session = requests.Session()
self.session.headers.update(
{
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
)

def _request(self, method: str, path: str, **kwargs) -> dict:
url = f"{BASE_URL}{path}"
response = self.session.request(method, url, **kwargs)
body = response.json()

if not response.ok:
error = body.get("error", {})
raise SentroError(
status_code=response.status_code,
code=error.get("code", "UNKNOWN"),
message=error.get("message", "Unknown error"),
)

return body

# ── Monitors ──────────────────────────────────────

def list_monitors(self) -> dict:
"""List all monitors for your organization."""
return self._request("GET", "/monitors")

def get_monitor(self, monitor_id: str) -> dict:
"""Get a single monitor with its latest checks."""
return self._request("GET", f"/monitors/{monitor_id}")

def create_monitor(
self,
name: str,
url: str,
method: str = "GET",
interval_seconds: int = 60,
timeout_seconds: int = 30,
expected_status_code: int = 200,
follow_redirects: bool = True,
headers: Optional[dict] = None,
) -> dict:
"""Create a new monitor."""
payload = {
"name": name,
"url": url,
"method": method,
"intervalSeconds": interval_seconds,
"timeoutSeconds": timeout_seconds,
"expectedStatusCode": expected_status_code,
"followRedirects": follow_redirects,
}
if headers:
payload["headers"] = headers

return self._request("POST", "/monitors", json=payload)

def update_monitor(self, monitor_id: str, **kwargs) -> dict:
"""Update an existing monitor. Pass only the fields to change."""
# Convert snake_case kwargs to camelCase for the API
field_map = {
"name": "name",
"url": "url",
"method": "method",
"interval_seconds": "intervalSeconds",
"timeout_seconds": "timeoutSeconds",
"expected_status_code": "expectedStatusCode",
"follow_redirects": "followRedirects",
"headers": "headers",
}

payload = {}
for key, value in kwargs.items():
if key in field_map:
payload[field_map[key]] = value

return self._request("PATCH", f"/monitors/{monitor_id}", json=payload)

def delete_monitor(self, monitor_id: str) -> dict:
"""Delete a monitor."""
return self._request("DELETE", f"/monitors/{monitor_id}")

# ── Checks ────────────────────────────────────────

def list_checks(
self,
monitor_id: str,
limit: int = 50,
offset: int = 0,
) -> dict:
"""List checks for a monitor."""
params = urlencode({"limit": limit, "offset": offset})
return self._request("GET", f"/monitors/{monitor_id}/checks?{params}")

# ── Incidents ─────────────────────────────────────

def list_incidents(
self,
limit: int = 50,
offset: int = 0,
monitor_id: Optional[str] = None,
) -> dict:
"""List incidents for your organization."""
params = {"limit": limit, "offset": offset}
if monitor_id:
params["monitorId"] = monitor_id
query = urlencode(params)
return self._request("GET", f"/incidents?{query}")

def get_incident(self, incident_id: str) -> dict:
"""Get a single incident with its updates."""
return self._request("GET", f"/incidents/{incident_id}")

# ── Alert Channels ────────────────────────────────

def list_alert_channels(self) -> dict:
"""List all alert channels."""
return self._request("GET", "/alert-channels")

def create_alert_channel(
self,
type: str,
name: str,
config: dict,
enabled: bool = True,
) -> dict:
"""Create a new alert channel."""
return self._request(
"POST",
"/alert-channels",
json={
"type": type,
"name": name,
"config": config,
"enabled": enabled,
},
)

def delete_alert_channel(self, channel_id: str) -> dict:
"""Delete an alert channel."""
return self._request("DELETE", f"/alert-channels/{channel_id}")

Usage Examples​

Initialize the Client​

import os
from sentro import SentroClient

client = SentroClient(os.environ["SENTRO_API_KEY"])

List All Monitors​

result = client.list_monitors()

print(f"Total monitors: {result['meta']['total']}")
for monitor in result["data"]:
print(f" {monitor['name']} ({monitor['status']}) - {monitor['url']}")

Create a Monitor​

result = client.create_monitor(
name="Production API",
url="https://api.example.com/health",
method="GET",
interval_seconds=60,
timeout_seconds=30,
expected_status_code=200,
headers={"Accept": "application/json"},
)

print(f"Created monitor: {result['data']['id']}")

Get Check History​

result = client.list_checks("mon_abc123", limit=100, offset=0)

checks = result["data"]
up_count = sum(1 for c in checks if c["isUp"])
uptime = (up_count / len(checks)) * 100

print(f"Uptime: {uptime:.2f}% over {len(checks)} checks")
print(f"Total checks available: {result['meta']['total']}")

List Recent Incidents​

result = client.list_incidents(limit=5)

for incident in result["data"]:
status = "RESOLVED" if incident["resolvedAt"] else "ONGOING"
monitor_name = incident.get("monitor", {}).get("name", "Unknown")
print(f" [{status}] {incident['cause']} ({monitor_name})")

Create a Slack Alert Channel​

result = client.create_alert_channel(
type="SLACK",
name="Engineering Alerts",
config={
"webhookUrl": "https://hooks.slack.com/services/T00/B00/xxx",
},
)

print(f"Created alert channel: {result['data']['id']}")

Error Handling​

from sentro import SentroClient, SentroError

client = SentroClient(os.environ["SENTRO_API_KEY"])

try:
result = client.get_monitor("non_existent_id")
except SentroError as e:
print(f"Error [{e.status_code}]: {e.message}")
# "Error [404]: Monitor with id 'non_existent_id' not found"

Using Plain Requests​

If you prefer not to use a wrapper:

import os
import requests

API_KEY = os.environ["SENTRO_API_KEY"]
BASE_URL = "https://api.sentro.dev/v1"

response = requests.get(
f"{BASE_URL}/monitors",
headers={"Authorization": f"Bearer {API_KEY}"},
)

response.raise_for_status()
data = response.json()

for monitor in data["data"]:
print(f"{monitor['name']} - {monitor['url']}")