Repository: PagerDuty/go-pagerduty
Pull request #405 (2021-11-25 07:59:51)

Update pagination query to conform to API spec
While looking for API shape mismatches for #389, I stumbled upon a major issue
with our pagination parameters across the entire package. This fixes that
discrepency, but is done via a pretty intrusive breaking change by removing the
embedded `APIListObject` from all `List*Options` types, replacing them with
three distinct fields: `Limit`, `Offset`, and `Total`.

For the traditional API pagination, there is a `total` URL query parameter that
tells the API to include the total count of records in the response. For the
query parameter it's a bool flag, with a value of `true` telling the API to
include the count.

When the response comes back from the API, there is a `total` field in the JSON
that is the total count of items in the collection. This value is only set if
that `total` query parameter is set to `true` because it's an expensive
operation that PagerDuty recommends you don't enable.

The problem is we tried to reuse the `APIListObject` struct type for both the
pagination URL query parameters, and the pagination fields in the JSON response
body. The issue with doing that is for requests the `Total` field needs to be a
bool, but within the `APIListObject` it's a `uint` because it was really only designed
to be used for response body parsing.

So since we're intending to remove the embedded struct types in v2 (#318), it
feels like the best course of action here is to move forward with that plan for
these paginated query parameter struct types so that we can conform to the
PagerDuty API spec.

Related to #318
Related to #389


Patch:
diff --git a/addon.go b/addon.go
index d444cc8..9fd85e6 100644
--- a/addon.go
+++ b/addon.go
@@ -18,7 +18,25 @@ type Addon struct {
 
 // ListAddonOptions are the options available when calling the ListAddons API endpoint.
 type ListAddonOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
+
 	Includes   []string `url:"include,omitempty,brackets"`
 	ServiceIDs []string `url:"service_ids,omitempty,brackets"`
 	Filter     string   `url:"filter,omitempty"`
diff --git a/business_service.go b/business_service.go
index b8df9e0..7d4e9e4 100644
--- a/business_service.go
+++ b/business_service.go
@@ -44,7 +44,24 @@ type ListBusinessServicesResponse struct {
 
 // ListBusinessServiceOptions is the data structure used when calling the ListBusinessServices API endpoint.
 type ListBusinessServiceOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
 }
 
 // ListBusinessServices lists existing business services. This method currently
diff --git a/client.go b/client.go
index 0c5adcf..67d49b3 100644
--- a/client.go
+++ b/client.go
@@ -55,10 +55,10 @@ type APIObject struct {
 
 // APIListObject are the fields used to control pagination when listing objects.
 type APIListObject struct {
-	Limit  uint `url:"limit,omitempty"`
-	Offset uint `url:"offset,omitempty"`
-	More   bool `url:"more,omitempty"`
-	Total  uint `url:"total,omitempty"`
+	Limit  uint `json:"limit,omitempty"`
+	Offset uint `json:"offset,omitempty"`
+	More   bool `json:"more,omitempty"`
+	Total  uint `json:"total,omitempty"`
 }
 
 // APIReference are the fields required to reference another API object.
diff --git a/command/vendor_list.go b/command/vendor_list.go
index 91c2092..adac675 100644
--- a/command/vendor_list.go
+++ b/command/vendor_list.go
@@ -2,11 +2,12 @@ package main
 
 import (
 	"fmt"
+	"strings"
+
 	"github.com/PagerDuty/go-pagerduty"
-	log "github.com/sirupsen/logrus"
 	"github.com/mitchellh/cli"
+	log "github.com/sirupsen/logrus"
 	"gopkg.in/yaml.v2"
-	"strings"
 )
 
 type VendorList struct {
@@ -49,9 +50,7 @@ func (c *VendorList) Run(args []string) int {
 		return -1
 	}
 	client := c.Meta.Client()
-	opts := pagerduty.ListVendorOptions{
-		Query: query,
-	}
+	opts := pagerduty.ListVendorOptions{}
 	if resp, err := client.ListVendors(opts); err != nil {
 		log.Error(err)
 		return -1
diff --git a/escalation_policy.go b/escalation_policy.go
index 2f3febd..b4df257 100644
--- a/escalation_policy.go
+++ b/escalation_policy.go
@@ -45,7 +45,25 @@ type ListEscalationRulesResponse struct {
 
 // ListEscalationPoliciesOptions is the data structure used when calling the ListEscalationPolicies API endpoint.
 type ListEscalationPoliciesOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
+
 	Query    string   `url:"query,omitempty"`
 	UserIDs  []string `url:"user_ids,omitempty,brackets"`
 	TeamIDs  []string `url:"team_ids,omitempty,brackets"`
diff --git a/extension.go b/extension.go
index bd72ffe..1cb3f92 100644
--- a/extension.go
+++ b/extension.go
@@ -28,7 +28,25 @@ type ListExtensionResponse struct {
 
 // ListExtensionOptions are the options to use when listing extensions.
 type ListExtensionOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
+
 	ExtensionObjectID string `url:"extension_object_id,omitempty"`
 	ExtensionSchemaID string `url:"extension_schema_id,omitempty"`
 	Query             string `url:"query,omitempty"`
diff --git a/extension_schema.go b/extension_schema.go
index 3c1a396..32903f2 100644
--- a/extension_schema.go
+++ b/extension_schema.go
@@ -32,8 +32,24 @@ type ListExtensionSchemaResponse struct {
 // ListExtensionSchemaOptions are the options to send with the
 // ListExtensionSchema reques(s).
 type ListExtensionSchemaOptions struct {
-	APIListObject
-	Query string `url:"query,omitempty"`
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
 }
 
 // ListExtensionSchemas lists all of the extension schemas. Each schema
diff --git a/incident.go b/incident.go
index 18b8233..37a4926 100644
--- a/incident.go
+++ b/incident.go
@@ -97,7 +97,25 @@ type ListIncidentsResponse struct {
 
 // ListIncidentsOptions is the structure used when passing parameters to the ListIncident API endpoint.
 type ListIncidentsOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
+
 	Since       string   `url:"since,omitempty"`
 	Until       string   `url:"until,omitempty"`
 	DateRange   string   `url:"date_range,omitempty"`
@@ -406,7 +424,25 @@ type ListAlertsResponse struct {
 
 // ListIncidentAlertsOptions is the structure used when passing parameters to the ListIncidentAlerts API endpoint.
 type ListIncidentAlertsOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
+
 	Statuses []string `url:"statuses,omitempty,brackets"`
 	SortBy   string   `url:"sort_by,omitempty"`
 	Includes []string `url:"include,omitempty,brackets"`
@@ -534,7 +570,25 @@ type ListIncidentLogEntriesResponse struct {
 
 // ListIncidentLogEntriesOptions is the structure used when passing parameters to the ListIncidentLogEntries API endpoint.
 type ListIncidentLogEntriesOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
+
 	Includes   []string `url:"include,omitempty,brackets"`
 	IsOverview bool     `url:"is_overview,omitempty"`
 	TimeZone   string   `url:"time_zone,omitempty"`
diff --git a/log_entry.go b/log_entry.go
index 1585e4c..ab26fb8 100644
--- a/log_entry.go
+++ b/log_entry.go
@@ -55,7 +55,25 @@ type ListLogEntryResponse struct {
 
 // ListLogEntriesOptions is the data structure used when calling the ListLogEntry API endpoint.
 type ListLogEntriesOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
+
 	TimeZone   string   `url:"time_zone,omitempty"`
 	Since      string   `url:"since,omitempty"`
 	Until      string   `url:"until,omitempty"`
diff --git a/maintenance_window.go b/maintenance_window.go
index 5578342..d7cc3b4 100644
--- a/maintenance_window.go
+++ b/maintenance_window.go
@@ -28,7 +28,25 @@ type ListMaintenanceWindowsResponse struct {
 
 // ListMaintenanceWindowsOptions is the data structure used when calling the ListMaintenanceWindows API endpoint.
 type ListMaintenanceWindowsOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
+
 	Query      string   `url:"query,omitempty"`
 	Includes   []string `url:"include,omitempty,brackets"`
 	TeamIDs    []string `url:"team_ids,omitempty,brackets"`
diff --git a/notification.go b/notification.go
index f110039..2a79429 100644
--- a/notification.go
+++ b/notification.go
@@ -17,7 +17,25 @@ type Notification struct {
 
 // ListNotificationOptions is the data structure used when calling the ListNotifications API endpoint.
 type ListNotificationOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
+
 	TimeZone string   `url:"time_zone,omitempty"`
 	Since    string   `url:"since,omitempty"`
 	Until    string   `url:"until,omitempty"`
diff --git a/on_call.go b/on_call.go
index bc222ca..f27ad61 100644
--- a/on_call.go
+++ b/on_call.go
@@ -24,7 +24,25 @@ type ListOnCallsResponse struct {
 
 // ListOnCallOptions is the data structure used when calling the ListOnCalls API endpoint.
 type ListOnCallOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
+
 	TimeZone            string   `url:"time_zone,omitempty"`
 	Includes            []string `url:"include,omitempty,brackets"`
 	UserIDs             []string `url:"user_ids,omitempty,brackets"`
diff --git a/priorites.go b/priorites.go
index d379c2f..845738c 100644
--- a/priorites.go
+++ b/priorites.go
@@ -2,6 +2,8 @@ package pagerduty
 
 import (
 	"context"
+
+	"github.com/google/go-querystring/query"
 )
 
 // PriorityProperty is a single priorty object returned from the Priorities endpoint
@@ -11,28 +13,56 @@ type PriorityProperty struct {
 	Description string `json:"description"`
 }
 
-// Priorities repreents the API response from PagerDuty when listing the
-// configured priorities.
-type Priorities struct {
+// ListPrioritiesResponse repreents the API response from PagerDuty when listing
+// the configured priorities.
+type ListPrioritiesResponse struct {
 	APIListObject
 	Priorities []PriorityProperty `json:"priorities"`
 }
 
+// ListPrioritiesOptions is the data structure used when calling the
+// ListPriorities API endpoint.
+type ListPrioritiesOptions struct {
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
+}
+
 // ListPriorities lists existing priorities.
 //
 // Deprecated: Use ListPrioritiesWithContext instead.
-func (c *Client) ListPriorities() (*Priorities, error) {
-	return c.ListPrioritiesWithContext(context.Background())
+func (c *Client) ListPriorities() (*ListPrioritiesResponse, error) {
+	return c.ListPrioritiesWithContext(context.Background(), ListPrioritiesOptions{})
 }
 
 // ListPrioritiesWithContext lists existing priorities.
-func (c *Client) ListPrioritiesWithContext(ctx context.Context) (*Priorities, error) {
-	resp, err := c.get(ctx, "/priorities")
+func (c *Client) ListPrioritiesWithContext(ctx context.Context, o ListPrioritiesOptions) (*ListPrioritiesResponse, error) {
+	v, err := query.Values(o)
+	if err != nil {
+		return nil, err
+	}
+
+	resp, err := c.get(ctx, "/priorities?"+v.Encode())
 	if err != nil {
 		return nil, err
 	}
 
-	var p Priorities
+	var p ListPrioritiesResponse
 	if err := c.decodeJSON(resp, &p); err != nil {
 		return nil, err
 	}
diff --git a/schedule.go b/schedule.go
index c3a73ff..87d1878 100644
--- a/schedule.go
+++ b/schedule.go
@@ -53,7 +53,25 @@ type Schedule struct {
 
 // ListSchedulesOptions is the data structure used when calling the ListSchedules API endpoint.
 type ListSchedulesOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
+
 	Query string `url:"query,omitempty"`
 }
 
@@ -114,7 +132,6 @@ func (c *Client) CreateScheduleWithContext(ctx context.Context, s Schedule) (*Sc
 
 // PreviewScheduleOptions is the data structure used when calling the PreviewSchedule API endpoint.
 type PreviewScheduleOptions struct {
-	APIListObject
 	Since    string `url:"since,omitempty"`
 	Until    string `url:"until,omitempty"`
 	Overflow bool   `url:"overflow,omitempty"`
@@ -159,7 +176,6 @@ func (c *Client) DeleteScheduleWithContext(ctx context.Context, id string) error
 
 // GetScheduleOptions is the data structure used when calling the GetSchedule API endpoint.
 type GetScheduleOptions struct {
-	APIListObject
 	TimeZone string `url:"time_zone,omitempty"`
 	Since    string `url:"since,omitempty"`
 	Until    string `url:"until,omitempty"`
@@ -209,7 +225,6 @@ func (c *Client) UpdateScheduleWithContext(ctx context.Context, id string, s Sch
 
 // ListOverridesOptions is the data structure used when calling the ListOverrides API endpoint.
 type ListOverridesOptions struct {
-	APIListObject
 	Since    string `url:"since,omitempty"`
 	Until    string `url:"until,omitempty"`
 	Editable bool   `url:"editable,omitempty"`
@@ -218,7 +233,6 @@ type ListOverridesOptions struct {
 
 // ListOverridesResponse is the data structure returned from calling the ListOverrides API endpoint.
 type ListOverridesResponse struct {
-	APIListObject
 	Overrides []Override `json:"overrides,omitempty"`
 }
 
@@ -295,7 +309,6 @@ func (c *Client) DeleteOverrideWithContext(ctx context.Context, scheduleID, over
 
 // ListOnCallUsersOptions is the data structure used when calling the ListOnCallUsers API endpoint.
 type ListOnCallUsersOptions struct {
-	APIListObject
 	Since string `url:"since,omitempty"`
 	Until string `url:"until,omitempty"`
 }
diff --git a/service.go b/service.go
index 1547fac..887e536 100644
--- a/service.go
+++ b/service.go
@@ -112,7 +112,25 @@ type AlertGroupParamsConfig struct {
 
 // ListServiceOptions is the data structure used when calling the ListServices API endpoint.
 type ListServiceOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
+
 	TeamIDs  []string `url:"team_ids,omitempty,brackets"`
 	TimeZone string   `url:"time_zone,omitempty"`
 	SortBy   string   `url:"sort_by,omitempty"`
diff --git a/tag.go b/tag.go
index fdb1a66..df5ac25 100644
--- a/tag.go
+++ b/tag.go
@@ -40,7 +40,25 @@ type ListEPResponse struct {
 
 // ListTagOptions are the input parameters used when calling the ListTags API endpoint.
 type ListTagOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
+
 	Query string `url:"query,omitempty"`
 }
 
diff --git a/team.go b/team.go
index 433c714..1f36a4f 100644
--- a/team.go
+++ b/team.go
@@ -23,7 +23,25 @@ type ListTeamResponse struct {
 
 // ListTeamOptions are the input parameters used when calling the ListTeams API endpoint.
 type ListTeamOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
+
 	Query string `url:"query,omitempty"`
 }
 
@@ -221,7 +239,24 @@ type Member struct {
 
 // ListMembersOptions are the optional parameters for a members request.
 type ListMembersOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
 }
 
 // ListMembersResponse is the response from the members endpoint.
diff --git a/user.go b/user.go
index 22c610d..dfc18a5 100644
--- a/user.go
+++ b/user.go
@@ -55,12 +55,30 @@ type ContactMethod struct {
 // ListUsersResponse is the data structure returned from calling the ListUsers API endpoint.
 type ListUsersResponse struct {
 	APIListObject
-	Users []User
+	Users []User `json:"users"`
 }
 
 // ListUsersOptions is the data structure used when calling the ListUsers API endpoint.
 type ListUsersOptions struct {
-	APIListObject
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
+
 	Query    string   `url:"query,omitempty"`
 	TeamIDs  []string `url:"team_ids,omitempty,brackets"`
 	Includes []string `url:"include,omitempty,brackets"`
diff --git a/vendor.go b/vendor.go
index cc88fc8..a5da3f9 100644
--- a/vendor.go
+++ b/vendor.go
@@ -33,8 +33,24 @@ type ListVendorResponse struct {
 
 // ListVendorOptions is the data structure used when calling the ListVendors API endpoint.
 type ListVendorOptions struct {
-	APIListObject
-	Query string `url:"query,omitempty"`
+	// Limit is the pagination parameter that limits the number of results per
+	// page. PagerDuty defaults this value to 25 if omitted, and sets an upper
+	// bound of 100.
+	Limit uint `url:"limit,omitempty"`
+
+	// Offset is the pagination parameter that specifies the offset at which to
+	// start pagination results. When trying to request the next page of
+	// results, the new Offset value should be currentOffset + Limit.
+	Offset uint `url:"offset,omitempty"`
+
+	// Total is the pagination parameter to request that the API return the
+	// total count of items in the response. If this field is omitted or set to
+	// false, the total number of results will not be sent back from the PagerDuty API.
+	//
+	// Setting this to true will slow down the API response times, and so it's
+	// recommended to omit it unless you've a specific reason for wanting the
+	// total count of items in the collection.
+	Total bool `url:"total,omitempty"`
 }
 
 // ListVendors lists existing vendors.


Test patch:
diff --git a/business_service_test.go b/business_service_test.go
index 60cbb19..0f8268c 100644
--- a/business_service_test.go
+++ b/business_service_test.go
@@ -20,13 +20,18 @@ func TestBusinessService_List(t *testing.T) {
 	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 	opts := ListBusinessServiceOptions{
-		APIListObject: listObj,
+		Limit:  listObj.Limit,
+		Offset: listObj.Offset,
 	}
 	res, err := client.ListBusinessServices(opts)
 	if err != nil {
 		t.Fatal(err)
 	}
 	want := &ListBusinessServicesResponse{
+		Limit:  listObj.Limit,
+		Offset: listObj.Offset,
+		More:   listObj.More,
+		Total:  listObj.Total,
 		BusinessServices: []*BusinessService{
 			{
 				ID: "1",
diff --git a/extension_schema_test.go b/extension_schema_test.go
index 955ae3d..6414124 100644
--- a/extension_schema_test.go
+++ b/extension_schema_test.go
@@ -17,8 +17,8 @@ func TestExtensionSchema_List(t *testing.T) {
 	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 	opts := ListExtensionSchemaOptions{
-		APIListObject: listObj,
-		Query:         "foo",
+		Limit:  listObj.Limit,
+		Offset: listObj.Offset,
 	}
 
 	res, err := client.ListExtensionSchemas(opts)
diff --git a/extension_test.go b/extension_test.go
index 6400e2c..c42e954 100644
--- a/extension_test.go
+++ b/extension_test.go
@@ -18,8 +18,9 @@ func TestExtension_List(t *testing.T) {
 	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 	opts := ListExtensionOptions{
-		APIListObject: listObj,
-		Query:         "foo",
+		Limit:  listObj.Limit,
+		Offset: listObj.Offset,
+		Query:  "foo",
 	}
 
 	res, err := client.ListExtensions(opts)
diff --git a/incident_test.go b/incident_test.go
index cbab69d..90c1879 100644
--- a/incident_test.go
+++ b/incident_test.go
@@ -583,8 +583,9 @@ func TestIncident_ListIncidentAlertsWithOpts(t *testing.T) {
 	id := "1"
 
 	alertOpts := ListIncidentAlertsOptions{
-		APIListObject: listObj,
-		Includes:      []string{},
+		Limit:    listObj.Limit,
+		Offset:   listObj.Offset,
+		Includes: []string{},
 	}
 
 	res, err := client.ListIncidentAlertsWithOpts(id, alertOpts)
@@ -721,10 +722,11 @@ func TestIncident_ListLogEntries(t *testing.T) {
 	client := defaultTestClient(server.URL, "foo")
 	id := "1"
 	entriesOpts := ListIncidentLogEntriesOptions{
-		APIListObject: listObj,
-		Includes:      []string{},
-		IsOverview:    true,
-		TimeZone:      "UTC",
+		Limit:      listObj.Limit,
+		Offset:     listObj.Offset,
+		Includes:   []string{},
+		IsOverview: true,
+		TimeZone:   "UTC",
 	}
 	res, err := client.ListIncidentLogEntries(id, entriesOpts)
 
@@ -761,12 +763,13 @@ func TestIncident_ListLogEntriesSinceUntil(t *testing.T) {
 	client := defaultTestClient(server.URL, "foo")
 	id := "1"
 	entriesOpts := ListIncidentLogEntriesOptions{
-		APIListObject: listObj,
-		Includes:      []string{},
-		IsOverview:    true,
-		TimeZone:      "UTC",
-		Since:         "2020-03-27T22:40:00-0700",
-		Until:         "2020-03-28T22:50:00-0700",
+		Limit:      listObj.Limit,
+		Offset:     listObj.Offset,
+		Includes:   []string{},
+		IsOverview: true,
+		TimeZone:   "UTC",
+		Since:      "2020-03-27T22:40:00-0700",
+		Until:      "2020-03-28T22:50:00-0700",
 	}
 	res, err := client.ListIncidentLogEntries(id, entriesOpts)
 
diff --git a/log_entry_test.go b/log_entry_test.go
index ff89096..f35ea83 100644
--- a/log_entry_test.go
+++ b/log_entry_test.go
@@ -18,10 +18,11 @@ func TestLogEntry_List(t *testing.T) {
 	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 	entriesOpts := ListLogEntriesOptions{
-		APIListObject: listObj,
-		Includes:      []string{},
-		IsOverview:    true,
-		TimeZone:      "UTC",
+		Limit:      listObj.Limit,
+		Offset:     listObj.Offset,
+		Includes:   []string{},
+		IsOverview: true,
+		TimeZone:   "UTC",
 	}
 	res, err := client.ListLogEntries(entriesOpts)
 
diff --git a/maintenance_window_test.go b/maintenance_window_test.go
index 1611cf3..a478b3f 100644
--- a/maintenance_window_test.go
+++ b/maintenance_window_test.go
@@ -18,12 +18,13 @@ func TestMaintenanceWindow_List(t *testing.T) {
 	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 	opts := ListMaintenanceWindowsOptions{
-		APIListObject: listObj,
-		Query:         "foo",
-		Includes:      []string{},
-		TeamIDs:       []string{},
-		ServiceIDs:    []string{},
-		Filter:        "foo",
+		Limit:      listObj.Limit,
+		Offset:     listObj.Offset,
+		Query:      "foo",
+		Includes:   []string{},
+		TeamIDs:    []string{},
+		ServiceIDs: []string{},
+		Filter:     "foo",
 	}
 	res, err := client.ListMaintenanceWindows(opts)
 
diff --git a/notification_test.go b/notification_test.go
index 99fb3ee..2e817c6 100644
--- a/notification_test.go
+++ b/notification_test.go
@@ -18,11 +18,12 @@ func TestNotification_List(t *testing.T) {
 	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 	opts := ListNotificationOptions{
-		APIListObject: listObj,
-		Includes:      []string{},
-		Filter:        "foo",
-		Since:         "bar",
-		Until:         "baz",
+		Limit:    listObj.Limit,
+		Offset:   listObj.Offset,
+		Includes: []string{},
+		Filter:   "foo",
+		Since:    "bar",
+		Until:    "baz",
 	}
 	res, err := client.ListNotifications(opts)
 
diff --git a/on_call_test.go b/on_call_test.go
index 27502ce..2bce7e2 100644
--- a/on_call_test.go
+++ b/on_call_test.go
@@ -18,7 +18,8 @@ func TestOnCall_List(t *testing.T) {
 	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 	opts := ListOnCallOptions{
-		APIListObject:       listObj,
+		Limit:               listObj.Limit,
+		Offset:              listObj.Offset,
 		TimeZone:            "UTC",
 		Includes:            []string{},
 		UserIDs:             []string{},
diff --git a/priorities_test.go b/priorities_test.go
index d07619a..5ba0146 100644
--- a/priorities_test.go
+++ b/priorities_test.go
@@ -1,6 +1,7 @@
 package pagerduty
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -18,9 +19,9 @@ func TestPriorities_List(t *testing.T) {
 	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 
-	res, err := client.ListPriorities()
+	res, err := client.ListPrioritiesWithContext(context.Background(), ListPrioritiesOptions{})
 
-	want := &Priorities{
+	want := &ListPrioritiesResponse{
 		APIListObject: listObj,
 		Priorities: []PriorityProperty{
 			{
diff --git a/schedule_test.go b/schedule_test.go
index 7d3596c..af6b94d 100644
--- a/schedule_test.go
+++ b/schedule_test.go
@@ -18,8 +18,9 @@ func TestSchedule_List(t *testing.T) {
 	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 	opts := ListSchedulesOptions{
-		APIListObject: listObj,
-		Query:         "foo",
+		Limit:  listObj.Limit,
+		Offset: listObj.Offset,
+		Query:  "foo",
 	}
 	res, err := client.ListSchedules(opts)
 
@@ -103,14 +104,12 @@ func TestSchedule_Get(t *testing.T) {
 	})
 
 	client := defaultTestClient(server.URL, "foo")
-	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 
 	input := "1"
 	opts := GetScheduleOptions{
-		APIListObject: listObj,
-		TimeZone:      "UTC",
-		Since:         "foo",
-		Until:         "bar",
+		TimeZone: "UTC",
+		Since:    "foo",
+		Until:    "bar",
 	}
 	res, err := client.GetSchedule(input, opts)
 
@@ -171,21 +170,18 @@ func TestSchedule_ListOverrides(t *testing.T) {
 		_, _ = w.Write([]byte(`{"overrides": [{"id": "1"}]}`))
 	})
 
-	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 	opts := ListOverridesOptions{
-		APIListObject: listObj,
-		Since:         "foo",
-		Until:         "bar",
-		Editable:      false,
-		Overflow:      false,
+		Since:    "foo",
+		Until:    "bar",
+		Editable: false,
+		Overflow: false,
 	}
 	schedID := "1"
 
 	res, err := client.ListOverrides(schedID, opts)
 
 	want := &ListOverridesResponse{
-		APIListObject: listObj,
 		Overrides: []Override{
 			{
 				ID: "1",
@@ -258,12 +254,10 @@ func TestSchedule_ListOnCallUsers(t *testing.T) {
 		_, _ = w.Write([]byte(`{"users": [{"id": "1"}]}`))
 	})
 
-	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 	opts := ListOnCallUsersOptions{
-		APIListObject: listObj,
-		Since:         "foo",
-		Until:         "bar",
+		Since: "foo",
+		Until: "bar",
 	}
 	schedID := "1"
 
diff --git a/service_test.go b/service_test.go
index dbcc530..ac5644f 100644
--- a/service_test.go
+++ b/service_test.go
@@ -21,12 +21,13 @@ func TestService_List(t *testing.T) {
 	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 	opts := ListServiceOptions{
-		APIListObject: listObj,
-		TeamIDs:       []string{},
-		TimeZone:      "foo",
-		SortBy:        "bar",
-		Query:         "baz",
-		Includes:      []string{},
+		Limit:    listObj.Limit,
+		Offset:   listObj.Offset,
+		TeamIDs:  []string{},
+		TimeZone: "foo",
+		SortBy:   "bar",
+		Query:    "baz",
+		Includes: []string{},
 	}
 	res, err := client.ListServices(opts)
 
@@ -73,12 +74,13 @@ func TestService_ListPaginated(t *testing.T) {
 	listObj := APIListObject{Limit: 1, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 	opts := ListServiceOptions{
-		APIListObject: listObj,
-		TeamIDs:       []string{},
-		TimeZone:      "foo",
-		SortBy:        "bar",
-		Query:         "baz",
-		Includes:      []string{},
+		Limit:    listObj.Limit,
+		Offset:   listObj.Offset,
+		TeamIDs:  []string{},
+		TimeZone: "foo",
+		SortBy:   "bar",
+		Query:    "baz",
+		Includes: []string{},
 	}
 	res, err := client.ListServicesPaginated(context.Background(), opts)
 
diff --git a/tag_test.go b/tag_test.go
index b434267..a1c8d03 100644
--- a/tag_test.go
+++ b/tag_test.go
@@ -19,8 +19,9 @@ func TestTag_List(t *testing.T) {
 	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 	opts := ListTagOptions{
-		APIListObject: listObj,
-		Query:         "MyTag",
+		Limit:  listObj.Limit,
+		Offset: listObj.Offset,
+		Query:  "MyTag",
 	}
 	res, err := client.ListTags(opts)
 	if err != nil {
@@ -267,7 +268,8 @@ func TestTag_GetTagsForEntity(t *testing.T) {
 	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 
 	opts := ListTagOptions{
-		APIListObject: listObj,
+		Limit:  listObj.Limit,
+		Offset: listObj.Offset,
 	}
 	res, err := client.GetTagsForEntity(e, eid, opts)
 	if err != nil {
diff --git a/team_test.go b/team_test.go
index 5f9a385..1f16068 100644
--- a/team_test.go
+++ b/team_test.go
@@ -21,8 +21,9 @@ func TestTeam_List(t *testing.T) {
 	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 	opts := ListTeamOptions{
-		APIListObject: listObj,
-		Query:         "foo",
+		Limit:  listObj.Limit,
+		Offset: listObj.Offset,
+		Query:  "foo",
 	}
 	res, err := client.ListTeams(opts)
 
diff --git a/user_test.go b/user_test.go
index a366591..7c44500 100644
--- a/user_test.go
+++ b/user_test.go
@@ -18,10 +18,11 @@ func TestUser_List(t *testing.T) {
 	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 	opts := ListUsersOptions{
-		APIListObject: listObj,
-		Query:         "foo",
-		TeamIDs:       []string{},
-		Includes:      []string{},
+		Limit:    listObj.Limit,
+		Offset:   listObj.Offset,
+		Query:    "foo",
+		TeamIDs:  []string{},
+		Includes: []string{},
 	}
 	res, err := client.ListUsers(opts)
 
diff --git a/vendor_test.go b/vendor_test.go
index 534c81b..6ac50b5 100644
--- a/vendor_test.go
+++ b/vendor_test.go
@@ -18,8 +18,8 @@ func TestVendor_List(t *testing.T) {
 	listObj := APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
 	client := defaultTestClient(server.URL, "foo")
 	opts := ListVendorOptions{
-		APIListObject: listObj,
-		Query:         "foo",
+		Limit:  listObj.Limit,
+		Offset: listObj.Offset,
 	}
 	res, err := client.ListVendors(opts)