Skip to content
  • There are no suggestions because the search field is empty.

Getting Started with the AlertOps Python SDK

Install the SDK with pip, put your API key in ALERTOPS_API_KEY or a .env file, and create an AlertOps() client; every v2 and v3 endpoint is then a method such as ao.groups.list().

Overview

Relevant for Developers automating AlertOps from Python

The AlertOps Python SDK (alertops-sdk) wraps the whole AlertOps REST surface (the v2 and v3 APIs, alert ingestion and the public status viewer) in one client object. Each endpoint is an op on a resource, such as ao.groups.list or ao.escalation_policies.create, and the same definitions drive a synchronous client (AlertOps) and an asynchronous one (AsyncAlertOps). The registry has 784 ops across 27 root resources, all listed in docs/REFERENCE.md.

Key benefits:

  • Complete — every endpoint in the AlertOps API reference maps to an SDK op, and a unit test fails if one is missing.
  • Verified live — every op either returned a 2xx against a real tenant or carries a documented reason why it can't.
  • One definition, two clients — the sync and async clients share every endpoint and every helper.
  • Quirks handled — response envelopes are unwrapped, page-index offsets are paginated correctly, and three error formats map to one exception tree.

Installation

Relevant for Developers setting up a project

The SDK needs Python 3.10 or later. Its only runtime dependency is httpx. Install it from PyPI:

pip install "alertops-sdk[semantic]"

The [semantic] extra adds PyYAML, which the semantic layer (alertops.semantic) uses to load the AlertOps semantic spec. Leave it out if you only call endpoints.

To work on the SDK itself (tests, linting), clone its repository and install the dev extra into a virtual environment:

python -m venv .venv && . .venv/bin/activate     # Windows: .venv\Scripts\activate
pip install -e ".[dev]"

Everything you need is importable from the top-level package:

from alertops import AlertOps, AsyncAlertOps, ClientConfig, APIError, NotFoundError

Configure the client

Relevant for Developers connecting to a tenant

AlertOps() with no arguments reads ALERTOPS_* environment variables. You can pass any setting as a keyword argument instead, and keyword arguments always win.

SettingArgumentEnvironment variableDefault
API keyapi_key= (also the first positional argument)ALERTOPS_API_KEYnone (required for API-key endpoints)
v2 base URLv2_base_url=ALERTOPS_V2_BASE_URL (falls back to ALERTOPS_BASE_URL)https://app.alertops.com/api/v2
v3 base URLv3_base_url=ALERTOPS_V3_BASE_URLhttps://app.alertops.com/api/v3
Ingest basenotify_base_url=ALERTOPS_NOTIFY_BASE_URLhttps://notify.alertops.com
UI session tokensession_token=ALERTOPS_SESSION_TOKENnone (only for session-only endpoints)
AI chat backendai_chat_base_url=ALERTOPS_AI_CHAT_BASE_URLnone (no public default)
AI alert backendai_alert_base_url=ALERTOPS_AI_ALERT_BASE_URLbuilt-in default
Timeouttimeout=ALERTOPS_TIMEOUT30.0 seconds
Retriesmax_retries=ALERTOPS_MAX_RETRIES3
from alertops import AlertOps

ao = AlertOps() # ALERTOPS_API_KEY from the environment
ao = AlertOps("your-api-key", timeout=60) # explicit key and a longer timeout
ao = AlertOps(dotenv=".env") # read a .env file as well

Precedence

Settings resolve in this order, highest first:

  1. Keyword arguments to AlertOps(...) (a None value is ignored).
  2. The config=ClientConfig(...) object, if you pass one. When you pass config=, environment variables and dotenv are not read at all.
  3. Values from the dotenv= file.
  4. Exported ALERTOPS_* environment variables.
  5. The ClientConfig defaults.

A .env file beats exported variables
AlertOps(dotenv=".env") applies the file's KEY=VALUE lines on top of the process environment, so a stale ALERTOPS_API_KEY exported in your shell can't shadow the key in the file. The reader accepts # comments, export KEY=value lines and quoted values. A missing file is silently ignored.

ClientConfig field reference

ClientConfig is a frozen dataclass. Build one when you want to share settings between clients or keep configuration out of the environment.

FieldDescription
api_keyAlertOps API key. Sent as api-key to v2 and as x-api-key to v3.
session_tokenUI session token, sent as Authorization. Only needed for login, refresh, activation and AI backends, which reject API keys.
v2_base_urlRoot of the v2 REST API. Default https://app.alertops.com/api/v2.
v3_base_urlRoot of the v3 REST API, also used for the unauthenticated status viewer (public base). Default https://app.alertops.com/api/v3.
notify_base_urlAlert ingestion host for integration api_urls. Default https://notify.alertops.com.
ai_chat_base_urlAI chat backend. No default: ops on this base raise ConfigurationError until you set it.
ai_alert_base_urlAI alert backend. Has a built-in default.
timeoutPer-request timeout in seconds. Default 30.0.
max_retriesRetries after the first attempt for retryable failures. Default 3.
backoff_factorBase of the exponential backoff, in seconds. Default 0.5 (0.5 s, 1 s, 2 s …).
backoff_maxCap on one backoff delay, in seconds. Default 8.0.
user_agentUser-Agent prefix. Default alertops-sdk-python (the SDK appends /<version>).
extra_headersHeaders added to every request, for example a proxy header. Default {}.
from alertops import AlertOps, ClientConfig

cfg = ClientConfig(api_key="your-api-key", timeout=60.0, max_retries=5,
extra_headers={"X-Request-Source": "nightly-sync"})
ao = AlertOps(config=cfg)

You can also pass your own httpx.Client (or httpx.AsyncClient for the async client) as http_client=. The SDK then doesn't close it for you.

Authentication

Relevant for Developers wondering which header to send

You don't set auth headers yourself. Each op declares its base (v2, v3, public, notify, ai_chat, ai_alert) and its auth mode, and the client picks the header:

Op authBaseHeader sent
keyv2 (and every non-v3 base)api-key: <api_key>
keyv3x-api-key: <api_key>
sessionanyAuthorization: <session_token>
nonepublic, notify and anonymous auth routesno auth header

The key is checked when you make a call, not when you create the client. A missing key raises ConfigurationError("No AlertOps API key: pass api_key= or set ALERTOPS_API_KEY"). A session-only op called without a session token raises UnsupportedAuthError, which says the op "needs a UI session token (session_token=); API keys are not accepted".

Your first calls

Relevant for Developers checking that the connection works

Start with read-only lookups. They are unpaged, cheap and change nothing.

from alertops import AlertOps

with AlertOps() as ao: # closes the HTTP connection pool on exit
print(ao.account.time_zone()) # account time zone display name
print([p.name for p in ao.lookups.priority_types()])
print(ao.lookups.time_zone_name("America/Chicago")) # "(UTC-06:00) Central Time (US & Canada)"

for g in ao.groups.list.iter(): # every group, all pages
print(g.group_id, g.group_name)

noc = ao.groups.find("NOC") # case-insensitive name match, or None
if noc:
print(ao.groups.get(noc.group_id).group_name)

Methods used in this article

MethodHTTPWhat it does
ao.lookups.users()GET /app/usersEvery user, unpaged: user_id, user_name, first_name, last_name.
ao.lookups.groups()GET /app/groupsEvery group, unpaged: group_id, group_name.
ao.lookups.priority_types()GET /app/priority_typesCritical, High, Medium, Low, No Priority.
ao.lookups.time_zones()GET /app/time_zone[{id, name, zone}]. The name is what user and schedule bodies take.
ao.lookups.time_zone_name(zone)flowMaps an IANA zone to the display name AlertOps expects.
ao.lookups.can_add(item_name)flowTrue if the plan allows another User, Group, EscalationPolicy or Workflow.
ao.account.time_zone()flowThe account time zone display name.
ao.groups.list(...)GET /groupsOne page of groups (limit, offset page index, search).
ao.groups.get(group_id)GET /groups/{group_id}One group.
ao.groups.find(name)flowThe group whose name matches (case-insensitive), or None.
ao.request(method, path, base=…)anyEscape hatch for a path the SDK doesn't model.

For every other op, see docs/REFERENCE.md.

Result types

Relevant for Developers reading responses

The SDK returns light, schema-free containers instead of typed models, because AlertOps response shapes vary by endpoint and sometimes by call.

TypeWhat it isNotes
AOObjectA dict with attribute access: group.group_name equals group["group_name"].Nested objects are AOObject too. A missing attribute raises AttributeError, so use .get("key") for optional fields.
AOListA list of results..raw keeps the original envelope (paging metadata, counts) when the SDK unwrapped one. Lists collected with .all() have .raw = None.
NoneEndpoints that answer with an empty body (many PUTs and DELETEs).
bytesCSV, iCal and file downloads (ops flagged non_json).
strA 2xx response whose body isn't JSON.
groups = ao.groups.list(limit=25)          # AOList of AOObject rows, envelope "groups" unwrapped
print(len(groups), groups.raw.keys()) # the full original response is still there
csv_bytes = ao.groups.export() # bytes

id_of(obj) returns the first id-like field of a record (id, user_id, group_id, escalation_policy_id …), which helps when an endpoint answers with a different id alias than you expect:

from alertops import id_of

ep = ao.escalation_policies.create(escalation_policy_name="Payments", priority="Critical",
enabled=True, quick_launch=False)
ep_id = id_of(ep, "escalation_policy_id")

The request() escape hatch

Relevant for Developers calling a path the SDK doesn't model yet

ao.request(method, path, *, base="v2", auth="key", **kw) calls any path with the same auth, retries, error mapping and envelope parsing as a modelled op. It follows the normal call convention: keyword fields become the JSON body for POST/PUT/PATCH and query parameters for GET/DELETE, and body=, params=, files= and headers= work as usual.

pages = ao.request("GET", "/status_pages", base="v3")             # x-api-key sent automatically
ao.request("POST", "/groups", body={"group_name": "sdk-example", "description": [],
"dynamic": False, "topics": []})
ao.request("DELETE", "/groups/12345/access/admins", body=[{"user_id": 101}]) # DELETE may carry a body

Prefer the modelled op when one exists
request() exists for gaps. The modelled ops carry envelope unwrapping, pagination (.iter() / .all()) and documented gotchas that request() doesn't know about.

Where to go next

Relevant for Developers planning what to read

If you want toRead
Understand path params, body vs query, pagination, errors and retriesCalling the API: Parameters, Pagination and Errors
Use asyncio, or write helpers that work on both clientsAsync Client and Composable Flows
Create users, groups and rotationsUsers, Groups and On-Call Schedules with the AlertOps SDKs
Build escalation laddersEscalation Policies and Response Plays with the AlertOps SDKs
Turn on-call intent into correct configPlanning On-Call Setups with the AlertOps Semantic Layer
Send alerts in and work with themInbound Integrations, Alert Ingestion and Heartbeats with the AlertOps SDKs; Working with Alerts in the AlertOps SDKs
See how the SDK itself is testedTesting and Verifying the SDK

Best Practices

Relevant for Anyone running the SDK in scripts or services

Credentials

  • Keep the API key in .env or a secret store, never in source control or command-line history.
  • Use AlertOps(dotenv=".env") in scripts so a key exported in your shell can't override the intended tenant.
  • Treat the url returned by ao.users.schedules.webcal(user_id) and ao.schedules.webcal(group_id) as a secret: it embeds an API key.

Client lifetime

  • Create one client per process and reuse it: it holds an HTTP connection pool.
  • Use with AlertOps() as ao: (or call ao.close()) in short-lived scripts so connections are released.

Start read-only

  • Test a new key with lookups (ao.lookups.priority_types(), ao.account.time_zone()) before any write.
  • Check plan limits with ao.lookups.can_add("User") before bulk creation.

Configuration Examples

Relevant for Starting templates for common setups

Example 1 — Minimal script

from alertops import AlertOps

ao = AlertOps() # export ALERTOPS_API_KEY=... first
for u in ao.users.list.iter(search="ann"):
print(u.user_id, u.user_name)

Example 2 — Project with a .env file

# .env
ALERTOPS_API_KEY=your-api-key
ALERTOPS_TIMEOUT=60
ALERTOPS_MAX_RETRIES=5
from alertops import AlertOps

with AlertOps(dotenv=".env") as ao:
group, created = ao.groups.ensure("sdk-example-noc") # get or create, returns (group, created)
print(group.group_id, "created" if created else "existing")

Example 3 — Explicit config shared by a sync and an async client

import asyncio
from alertops import AlertOps, AsyncAlertOps, ClientConfig

cfg = ClientConfig(api_key="your-api-key", timeout=45.0)
ao = AlertOps(config=cfg)

async def main():
async with AsyncAlertOps(config=cfg) as aao:
print(await aao.account.time_zone())

asyncio.run(main())

Troubleshooting

Relevant for Anyone debugging a first connection

ConfigurationError: No AlertOps API key

  • The key wasn't found when the call ran. Export ALERTOPS_API_KEY, pass api_key=, or pass dotenv= pointing to a file that defines it.
  • If you passed config=ClientConfig(...), the environment isn't read: set api_key on the config.

AuthenticationError (401) "Invalid API Key"

  • The key is wrong or revoked, or a stale exported key is shadowing the right one. Use AlertOps(dotenv=".env") and check ao.config.api_key.
  • Check that v2_base_url and v3_base_url point at the tenant's host.

ConfigurationError: base 'ai_chat' has no URL configured

  • The AI chat backend has no public default. Set ALERTOPS_AI_CHAT_BASE_URL or pass ai_chat_base_url=. These ops also need a UI session token.

UnsupportedAuthError: needs a UI session token

  • The op is session-only (login, refresh, activation, AI backends). API keys are rejected by the server, so the SDK refuses before sending. Pass session_token= from a signed-in UI session, or use an API-key alternative.

AttributeError on a result

  • AOObject raises AttributeError for a field the response doesn't contain. Use row.get("field") for optional fields, and print the row to see the real shape.

Related Articles

Need help?

Email support@alertops.com or contact your AlertOps representative.