A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://pagerduty.github.io/python-pagerduty/user_guide.html below:

Website Navigation


User Guide — python-pagerduty 4.0.0 documentation

User Guide

This is a topical guide to general API client usage. Module Reference has in-depth documentation on client classes and methods.

Installation

This library is available on the Python Package Index as pagerduty, e.g.:

Command Line Interface

This package also includes a basic CLI for PagerDuty Events API V2. For example, to trigger an incident:

pagerduty trigger -k $ROUTING_KEY --description "Network latency is high"

For more details, use the -h flag to display the script’s helptext.

Authentication

The first step is to construct a client object. The first argument to the constructor is the secret to use for accessing the API:

import pagerduty

# REST API v2:
client = pagerduty.RestApiV2Client(API_KEY)

# REST API v2 with an OAuth2 access token (both are equivalent)
client_oauth = pagerduty.RestApiV2Client(OAUTH_TOKEN, auth_type='oauth2')
client_oauth = pagerduty.RestApiV2Client(OAUTH_TOKEN, auth_type='bearer')

# Events API v2, including change events:
events_client = pagerduty.EventsApiV2Client(ROUTING_KEY)

Session objects, being descendants of requests.Session, can also be used as context managers. For example:

with pagerduty.RestApiV2Client(API_KEY) as client:
    do_application(client)
Using Non-US Service Regions

If your PagerDuty account is in the EU or other service region outside the US, set the url attribute according to the documented API Access URLs, i.e. for the EU:

# REST API
client.url = 'https://api.eu.pagerduty.com'
# Events API:
events_client.url = 'https://events.eu.pagerduty.com'
# Slack Integration "Connections" API (and likewise for other
# APIs that use apps.pagerduty.com and/or some unique base path):
slack_integration_client.url = 'https://apps.eu.pagerduty.com/integration-slack'
Performing an OAuth Exchange to Obtain an Access Token

The client class pagerduty.OAuthTokenClient provides methods implementing the OAuth exchange requests described in Obtaining a User OAuth Token via Code Grant and Obtaining an App OAuth Token.

token_client = pagerduty.OAuthTokenClient(client_secret, client_id)

To generate the URL that the user must visit to authorize the application:

# With a client object:
authorize_url = token_client.authorize_url(scope, redirect_uri)

# Without a client object:
authorize_url = pagerduty.OAuthTokenClient.get_authorize_url(client_id, scope, redirect_uri)

The application must provide a redirect URI at which to receive the authorization code parameter. Once the user visits, has authorized the application and is redirected back to the application at the redirect URI, the code parameter appended to it will contain the authorization code. The code can then be exchanged for an access token as following:

# auth_code contains the "code" parameter in the redirect URL of the application:
auth_response = token_client.get_new_token_from_code(auth_code, scope, redirect_uri)
access_token = auth_response['access_token']
refresh_token = auth_response['refresh_token']
Performing OAuth Token Refresh Automatically

As of version 3.0.0, the OAuth response dictionary returned by token-getting methods of pagerduty.OAuthTokenClient will include a property expiration_date containing a string that is an ISO8601-formatted date/time indicating when the included token will expire. Assuming that your application securely stores this string value in addition to access_token and refresh_token, and has the means to retrieve these values, they can be used to call pagerduty.OAuthTokenClient.refresh_client, which instantiates a new pagerduty.RestApiV2Client and automatically refreshes the access token as necessary.

# Assume the calling application implements methods securely_get_values and
# securely_store_values to recall and store secrets used for API access:
access_token, refresh_token, expiration_date = securely_get_values()
rest_client, auth_response = token_client.refresh_client(
    access_token,
    refresh_token,
    expiration_date
)
# If auth_response == None, the token was not refreshed and does not need
# to be updated; otherwise it will be similar to the value returned by
# other token-getting methods:
if type(auth_response) is dict:
    securely_store_values(
        access_token = auth_response['access_token'],
        refresh_token = auth_response['refresh_token'],
        expiration_date = auth_response['expiration_date']
    )

Note, the current default behavior of OAuthTokenClient is to refresh the token if it is going to expire less than 24 hours in the future. This “buffer time” (expressed as a positive integer number of seconds in the future) can be controlled by setting the property pagerduty.OAuthTokenClient.early_refresh_buffer.

Basic Usage Examples REST API v2

Making a request and decoding the response: obtaining a resource’s contents and having them represented as a dictionary object using three different methods:

# Using get:
response = client.get('/users/PABC123')
user = None
if response.ok:
    user = response.json()['user']

# Using jget (return the full body after decoding):
user = client.jget('/users/PABC123')['user']

# Using rget (return the response entity after unwrapping):
user = client.rget('/users/PABC123')

# >>> user
# {"type": "user", "email": "user@example.com", ... }

Using pagination: iter_all, iter_cursor, list_all and dict_all can be used to obtain results from a resource collection:

# Print each user's email address and name:
for user in client.iter_all('users'):
    print(user['id'], user['email'], user['name'])

Pagination with query parameters: set the params keyword argument, which is converted to URL query parameters by Requests:

# Get a list of all services with "SN" in their name:
services = client.list_all('services', params={'query': 'SN'})

# >>> services
# [{'type':'service', ...}, ...]

Searching resource collections: use find to look up a resource (a user, in this example) exactly matching a string using the query parameter on an index endpoint:

# Find the user with email address "jane@example35.com"
user = client.find('users', 'jane@example35.com', attribute='email')

# >>> user
# {'type': 'user', 'email': 'jane@example35.com', ...}

Getting a count of records: use get_total on any endpoint that supports classic pagination:

# Get the total number of users in the whole account:
total_users = client.get_total('users')

# Get the total number of users on a given team:
total_users_on_team_x = client.get_total(
    'users',
    params = {'team_ids[]': ['PGHI789']}
)

Updating a resource: use the json keyword argument to set the body of the request:

# >>> user
# {'self':'https://api.pagerduty.com/users/PABC123', 'type': 'user', ...}

# (1) using put directly:
updated_user = None
response = client.put(user['self'], json={
    'user': {
        'type':'user',
        'name': 'Jane Doe'
    }
})
if response.ok:
    updated_user = response.json()['user']

# (2) using rput:
#   - The URL argument may also be a resource / resource reference dict
#   - The json argument doesn't have to include the "user" wrapper dict
#   - If an HTTP error is encountered, it will raise an exception
updated_user = client.rput(user, json={
    'type':'user',
    'name': 'Jane Doe'
})

Idempotent create/update: create a user if one doesn’t already exist based on the dictionary object user_data, using the “email” key/property as the uniquely identifying attribute, and update it if it exists and differs from user_data:

user_data = {'email': 'user123@example.com', 'name': 'User McUserson'}
updated_user = client.persist('users', 'email', user_data, update=True)

Using multi-valued set filters: set the value in the params dictionary at the appropriate key to a list. Square brackets will then be automatically appended to the names of list-type-value parameters as necessary. For example:

# Query all open incidents assigned to a user
incidents = client.list_all(
    'incidents',
    params={
      # Both of the following parameter names are valid:
      'user_ids[]': ['PHIJ789'],
      'statuses': ['triggered', 'acknowledged'] # "[]" will be automatically appended
    }
)
# API calls will look like the following:
# GET /incidents?user_ids%5B%5D=PHIJ789&statuses%5B%5D=triggered&statuses%5B%5D=acknowledged&offset=0&limit=100

Get a list of all incident notes submitted by a team within a time range:

notes = list(client.iter_incident_notes(params={
    'team_ids':['PN1T34M'],
    'since': '2025-01-01',
    'until': '2025-07-01'
}))

# >>> notes
# [{'type': 'annotate_log_entry', 'summary': 'Resolved by reboot' ... }, ... ]

Performing multi-update: for endpoints that support it only, i.e. PUT /incidents:

# Acknowledge all triggered incidents assigned to a user:
incidents = client.list_all(
    'incidents',
    params={'user_ids':['PHIJ789'],'statuses':['triggered']}
)
for i in incidents:
    i['status'] = 'acknowledged'
updated_incidents = client.rput('incidents', json=incidents)
Events API v2

Trigger and resolve an alert, getting its deduplication key from the API, using pagerduty.EventsApiV2Client:

dedup_key = events_client.trigger("Server is on fire", 'dusty.old.server.net')
# ...
events_client.resolve(dedup_key)

Trigger an alert and acknowledge it using a custom deduplication key:

events_client.trigger("Server is on fire", 'dusty.old.server.net',
    dedup_key='abc123')
# ...
events_client.acknowledge('abc123')

Submit a change event using a pagerduty.EventsApiV2Client instance:

events_client.submit("new build finished at latest HEAD",
    source="automation")
Client Classes

For each of the APIs documented in the top-level sections of the API References page, this library supports and provides abstraction for the API if it has a dedicated client class. These classes are documented under “API Client Classes” in Module Reference.

All client classes are based on pagerduty.ApiClient and inherit its features.

The class pagerduty.RestApiV2BaseClient defines abstractions for features documented under the generic REST API v2 Overview, and pagerduty.RestApiV2Client inherits them.

The “Integration” APIs are based loosely on REST API v2, and support several of the same features and conventions to varying degrees. For this reason, they also are based on inherit the features of pagerduty.RestApiV2BaseClient.

Generic Client Features

Generally, all of the features of requests.Session are available to the user as they would be if using the Requests Python library directly, since pagerduty.ApiClient and its subclasses for the REST/Events APIs are descendants of it.

The get, post, put and delete methods of REST/Events API client classes are similar to the analogous functions in requests.Session. The arguments they accept are the same and they all return requests.Response objects.

Any keyword arguments passed to the j* or r* methods will be passed through to the analogous method in Requests, though in some cases the arguments (i.e. json) are first modified.

For documentation on any generic HTTP client features that are available, refer to the Requests documentation.

URLs

The first argument to most of the client methods is the URL. However, there is no need to specify a complete API URL. Any path relative to the root of the API, whether or not it includes a leading slash, is automatically normalized to a complete API URL. For instance, one can specify users/PABC123 or /users/PABC123 instead of https://api.pagerduty.com/users/PABC123.

One can also pass the full URL of an API endpoint and it will still work, i.e. the self property of any object can be used, and there is no need to strip out the API base URL.

The r* and j* methods, i.e. pagerduty.RestApiV2Client.rget, can also accept a dictionary object representing an API resource or a resource reference (see: resource references) in place of a URL, in which case the value at its self key will be used as the request URL.

Query Parameters

As with Requests, there is no need to compose the query string (everything that will follow ? in the URL). Simply set the params keyword argument to a dictionary, and each of the key/value pairs will be serialized to the query string in the final URL of the request:

first_dan = client.rget('users', params={
    'query': 'Dan',
    'limit': 1,
    'offset': 0,
})
# GET https://api.pagerduty.com/users?query=Dan&limit=1&offset=0

To specify a multi-value parameter, i.e. include[], set the argument to a list. If a list is given, and the key name does not end with [] (which is required for all such multi-valued parameters in REST API v2), then [] will be automatically appended to the parameter name. For example:

# If there are 82 services with name matching "foo" this will return all of
# them as a list:
foo_services = client.list_all('services', params={
    'query': 'foo',
    'include': ['escalation_policies', 'teams'],
    'limit': 50,
})
# GET https://api.pagerduty.com/services?query=foo&include%5B%5D=escalation_policies&include%5B%5D=teams&limit=50&offset=0
# GET https://api.pagerduty.com/services?query=foo&include%5B%5D=escalation_policies&include%5B%5D=teams&limit=50&offset=50
# >>> foo_services
# [{"type": "service" ...}, ... ]
Requests and Responses

To set the request body in a post or put request, pass as the json keyword argument an object that will be JSON-encoded as the body.

To obtain the response from the API, if using plain get, post, put or delete, use the returned requests.Response object. That object’s json() method will return the result of JSON-decoding the response body (it will typically of type dict). Other metadata such as headers can also be obtained:

response = client.get('incidents')
# The UUID of the API request, which can be supplied to PagerDuty Customer
# Support in the event of server errors (status 5xx):
print(response.headers['x-request-id'])

If using the j* methods, i.e. pagerduty.RestApiV2Client.jget, the return value will be the full body of the response from the API after JSON-decoding, and the json keyword argument is not modified.

When using the r* methods, the json keyword argument is modified before sending to Requests, if necessary, to encapsulate the body inside an entity wrapper. The response is the decoded body after unwrapping, if the API endpoint returns wrapped entities. For more details, refer to Entity Wrapping.

Data types

Main article: Types

Note these analogues in structure between the JSON schema and the object in Python:

For example, consider the example structure of an escalation policy as given in the API reference page for GET /escalation_policies/{id} (“Get an escalation policy”). To access the name of the second target in level 1, assuming the variable ep represents the unwrapped escalation policy object:

ep['escalation_rules'][0]['targets'][1]['summary']
# "Daily Engineering Rotation"

To add a new level, one would need to create a new escalation rule as a dictionary object and then append it to the escalation rules property. Using the example given in the API reference page:

new_rule = {
    "escalation_delay_in_minutes": 30,
    "targets": [
        {
            "id": "PAM4FGS",
            "type": "user_reference"
        },
        {
            "id": "PI7DH85",
            "type": "schedule_reference"
        }
    ]
}
ep['escalation_rules'].append(new_rule)
# Save changes:
ep = client.rput(ep, json=ep)
Resource Schemas

Main article: Resource Schemas

The details of any given resource’s schema can be found in the request and response examples from the PagerDuty API Reference pages for the resource’s respective API, as well as the page documenting the resource type itself.

Entity Wrapping

See also: Wrapped Entities. Most of PagerDuty’s REST API v2 endpoints respond with their content wrapped inside of another object with a single key at the root level of the (JSON-encoded) response body, and/or require the request body be wrapped in another object that contains a single key. Endpoints with such request/response schemas usually (with few exceptions) support pagination.

Identifying Wrapped-entity Endpoints

If the endpoint’s response schema or expected request schema contains only one property that contains all of the content of the API resource, the endpoint is said to wrap entities. In resource collection endpoints that support pagination, the response schema contains additional pagination-related properties such as more (for classic pagination) or next_cursor (for cursor-based pagination) and no other content-bearing properties.

Wrapped-entity-aware Functions

The following methods will automatically extract and return the wrapped content of API responses, and wrap request entities for the user as appropriate:

Special Cases

There are some API endpoints that do not follow API schema conventions for entity wrapping. Some do not wrap entities at all. On all endpoints that do not wrap entities, the results for a given r* method would be the same if using the equivalent j* method, and the details of request and response schemas are are left to the end user to extract and use as desired. Moreover, on all endpoints that completely lack entity wrapping, pagination is not supported, i.e. pagerduty.RestApiV2BaseClient.iter_all will raise pagerduty.UrlError if used with them.

Examples

The endpoint “Create Business Service Subscribers”, or POST /business_services/{id}/subscribers, wraps the response differently from the request. The end user can still pass the content to be wrapped via the json argument without the subscribers wrapper, while the return value is the list representing the content inside of the subscriptions wrapper in the response, and there is no need to hard-code any particular endpoint’s wrapper name into the usage of the client.

Some endpoints are unusual in that the request must be wrapped but the response is not wrapped or vice versa, i.e. creating Schedule overrides (POST /schedules/{id}/overrides) or to create a status update on an incient (POST /incidents/{id}/status_updates). In all such cases, the user still does not need to account for this, as the content will be returned and the request entity is wrapped as appropriate.

What that looks like, for the “Create one or more overrides” endpoint:

created_overrides = client.rpost('/schedules/PGHI789/overrides', json=[
    {
        "start": "2023-07-01T00:00:00-04:00",
        "end": "2023-07-02T00:00:00-04:00",
        "user": {
            "id": "PEYSGVA",
            "type": "user_reference"
        },
        "time_zone": "UTC"
    },
    {
        "start": "2023-07-03T00:00:00-04:00",
        "end": "2023-07-01T00:00:00-04:00",
        "user": {
            "id": "PEYSGVF",
            "type": "user_reference"
        },
        "time_zone": "UTC"
    }
])
# >>> created_overrides
# [
#     {'status': 201, 'override': {...}},
#     {'status': 400, 'errors': ['Override must end after its start'], 'override': {...}}
# ]
Multi-updating

Multi-update actions can be performed using rput with some endpoints. For instance, to resolve two incidents with IDs PABC123 and PDEF456:

client.rput(
    "incidents",
    json=[
        {'id':'PABC123','type':'incident_reference', 'status':'resolved'},
        {'id':'PDEF456','type':'incident_reference', 'status':'resolved'},
    ],
)

In this way, a single API request can more efficiently perform multiple update actions.

It is important to note, however, that updating incidents requires using a user-scoped access token or setting the From header to the login email address of a valid PagerDuty user. To set this, pass it through using the headers keyword argument, or set the pagerduty.RestApiV2Client.default_from property, or pass the email address as the default_from keyword argument when constructing the client initially.

Error Handling

The pagerduty.UrlError is raised prior to making API calls, and it indicates unsupported URLs and/or malformed input.

The base exception class for all errors encountered when making requests is pagerduty.Error. This includes network / transport issues where there is no response from the API, in which case the exception will inherit from the exception raised by the underlying HTTP library.

All errors that involve a response from the API are instances of pagerduty.HttpError and will have a response property containing the requests.Response object. Its subclass pagerduty.HttpServerError is used for special cases when the API is responding in an unexpected way.

One can thus define specialized error handling logic in which the REST API response data (i.e. headers, code and body) are available in the catching scope. For example, the following code prints “User not found” in the event of a 404, prints out the user’s email if the user exists and raises the underlying exception if it’s any other HTTP error code:

try:
    user = client.rget("/users/PJKL678")
    print(user['email'])

except pagerduty.HttpError as e:
    if e.response.status_code == 404:
        print("User not found")
    else:
        raise e
Logging

When a client object is instantiated, a Logger object is created as follows:

The attribute pagerduty.ApiClient.print_debug enables sending debug-level log messages from the client to command line output. It is used as follows:

# Method 1: keyword argument, when constructing a new client:
client = pagerduty.RestApiV2Client(api_key, debug=True)

# Method 2: on an existing client, by setting the property:
client.print_debug = True

# to disable:
client.print_debug = False

What this does is assign a logging.StreamHandler directly to the client’s logger and set the log level to logging.DEBUG. All log messages are then sent directly to sys.stderr. The default value for all clients is False, and it is recommended to keep it that way in production systems.

Using a Proxy Server

To configure the client to use a host as a proxy for HTTPS traffic, update the proxies attribute:

# Host 10.42.187.3 port 4012 protocol https:
client.proxies.update({'https': '10.42.187.3:4012'})
HTTP Retry Configuration

Session objects support retrying API requests if they receive a non-success response or if they encounter a network error.

This behavior is configurable through the following properties:

Default Behavior

By default, after receiving a status 429 response, clients will retry an unlimited number of times, increasing the wait time before retry each successive time. When encountering status 401 Unauthorized, the client will immediately raise pagerduty.HttpError; this is a non-transient error caused by an invalid credential.

For all other success or error statuses, the underlying request method in the client will return the requests.Response object.

Exponential Cooldown

After each unsuccessful attempt, the client will sleep for a short period that increases exponentially with each retry.

Let:

Assuming ρ = 0 (the default value):

tn = t0 an

If ρ is nonzero:

tn = a (1 + ρ rn) tn-1

Configuring Retry Behavior

The dictionary property pagerduty.ApiClient.retry allows customization of HTTP retry limits on a per-HTTP-status basis. This includes the ability to override the above defaults for 401 and 429, although that is not recommended.

Each key in the dictionary represents a HTTP status, and its associated value the number of times that the client will retry the request if it receives that status. Success statuses (2xx) will be ignored.

If a different error status is encountered on a retry, it won’t count towards the limit of the first status, but will be counted separately. However, the total overall number of attempts that will be made to get a success status is limited by pagerduty.ApiClient.max_http_attempts. This will always supersede the maximum number of retries for any status defined in pagerduty.ApiClient.retry if it is lower.

Low-level HTTP request functions in client classes, i.e. get, will return requests.Response objects when they run out of retries. Higher-level functions that require a success status response, i.e. pagerduty.RestApiV2Client.list_all and pagerduty.EventsApiV2Client.trigger, will raise instances of pagerduty.HttpError, but only after the configured retry limits are reached in the underlying HTTP request methods.

Example:

# This will take about 30 seconds plus API request time, carrying out four
# attempts with 2, 4, 8 and 16 second pauses between them, before finally
# returning the status 404 response object for the user that doesn't exist:
client.max_http_attempts = 4 # lower value takes effect
client.retry[404] = 5 # this won't take effect
client.sleep_timer = 1
client.sleep_timer_base = 2
response = client.get('/users/PNOEXST')

# Same as the above, but with the per-status limit taking precedence, so
# the total wait time is 62 seconds:
client.max_http_attempts = 6
response = client.get('/users/PNOEXST')

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4