Pagination and filtering¶
The examples in this guide use V2. For V1, see Pagination and filtering (V1).
List endpoints return paginated results. Use pagination to control result size and filtering to narrow the dataset to what your integration needs.
Pagination¶
Most list endpoints accept:
page: the page number to return. Defaults to1.page_size: how many results per page. Defaults to50, maximum100.
curl -X GET \
-H 'X-Api-Key: YOUR_API_KEY' \
-H 'X-Client-ID: YOUR_CLIENT_ID' \
-H 'X-Company-ID: YOUR_COMPANY_ID' \
'https://YOUR_ENV_DOMAIN/api/v2/management/clients/?page=2&page_size=50'
Replace YOUR_ENV_DOMAIN with public.staging.bsport.io (staging) or public.production.bsport.io (production). See Environments.
Response shape¶
A paginated response wraps the records in an envelope:
{
"count": 253,
"next": "https://YOUR_ENV_DOMAIN/api/v2/management/clients/?page=3&page_size=50",
"next_page": 3,
"previous": "https://YOUR_ENV_DOMAIN/api/v2/management/clients/?page=1&page_size=50",
"results": []
}
| Field | Meaning |
|---|---|
count |
Total number of records matching the request, across all pages. |
next |
Full URL of the next page, or null on the last page. |
next_page |
Page number of the next page, or null on the last page. |
previous |
Full URL of the previous page, or null on the first page. |
results |
The records for the current page. |
Paginate through every page¶
Stop when next_page is null. Prefer this over comparing the length of
results against page_size: the envelope states the end of the collection
directly, so your loop does not depend on a full final page.
def fetch_all(path, params=None):
results = []
page = 1
while page is not None:
response = requests.get(
f"{BASE_URL}{path}",
headers=HEADERS,
params={**(params or {}), "page": page, "page_size": 100},
)
response.raise_for_status()
body = response.json()
results.extend(body["results"])
page = body["next_page"]
return results
Tune page size for performance
Start with a moderate page_size (25-100) and adjust based on latency and
the amount of data you need to process.
Ordering¶
Use the ordering parameter to sort results. Prefix a field with - (dash) for
descending order, and separate several fields with commas to sort on more than one
key.
Ordering is available on a defined set of list endpoints, and each one accepts its own list of sortable fields:
| Endpoint | Sortable fields |
|---|---|
/api/v2/management/clients/ |
client_account_balance, email, fullname, joined_date, lastname |
/api/v2/management/bookings/ |
created_at, class_start_at |
/api/v2/management/client-class-passes/ |
available_credits_amount, purchased_date, starting_date, ending_date |
/api/v2/management/client-subscriptions/ |
created_at, first_billing_date, name, recurrent_price |
/api/v2/management/class-passes/ |
name, price |
/api/v2/management/subscriptions/ |
name, recurrent_price |
/api/v2/management/client-notes/ |
creation_date, id |
/api/v2/management/tags/ |
tag_group_id, id |
/api/v2/management/tag-groups/ |
id, name |
/api/v2/management/appointment-passes/ |
name, price |
/api/v2/management/client-appointment-passes/ |
available_credits_amount, purchased_at |
/api/v2/management/coupons/ |
created_at, expiration_date, name |
/api/v2/management/coupon-usages/ |
discount_amount, used_at |
/api/v2/management/gift-cards/ |
fixed_amount, name, validity_days |
/api/v2/management/invoices/ |
created_at, issued_at, total_amount |
/api/v2/management/invoice-items/ |
id, total_amount |
Each endpoint's API Reference entry lists the same set, and is authoritative if the two ever differ.
Unsupported ordering fields are ignored, not rejected
A field that an endpoint does not support is discarded rather than returning
an error. The request succeeds with 200 and the results come back in the
endpoint's default order. A misspelled field name therefore fails silently:
check the sort order of the response when you first wire up ordering.
Filtering¶
Filters are exposed as query parameters on each endpoint. Common patterns include:
*_idsfor multiple IDs (comma-separated, e.g.client_ids=123,456)*_afterand*_beforeon a timestamp field (e.g.created_afterforcreated_at)*_after_dateand*_before_dateon a date field (e.g.joined_after_dateforjoined_date)
See Date and timestamp filters.
Default filters¶
Some endpoints apply a default filter when you omit the parameter, so an unfiltered request does not always return the complete dataset.
GET /api/v2/management/classes/ and /api/v2/management/appointments/ filter on the start datetime through
starts_after and starts_before (ISO 8601). Each one applies its own default when
omitted:
| Parameter | Omitted value |
|---|---|
starts_after |
now |
starts_before |
now + 30 days |
A request to /classes/ with no parameters therefore returns only the next 30
days of classes. Pass starts_after explicitly to read classes that have already
taken place, and starts_before to read further ahead.
curl -X GET \
-H 'X-Api-Key: YOUR_API_KEY' \
-H 'X-Client-ID: YOUR_CLIENT_ID' \
-H 'X-Company-ID: YOUR_COMPANY_ID' \
'https://YOUR_ENV_DOMAIN/api/v2/management/classes/?starts_after=2026-01-01&starts_before=2026-03-31T23:59:59.999999'
Date and timestamp filters¶
Two filter families take different formats. Read the table to pick the format for the parameter you send.
| Filter family | Format | Where it applies |
|---|---|---|
*_after_date, *_before_date |
Date only, YYYY-MM-DD. Do not send a timestamp. |
clients (joined_*), client-class-passes (purchased_*) |
*_after, *_before |
ISO 8601 timestamp, preferably with its UTC offset | bookings (created_*, class_start_*), client-appointment-passes (purchased_*), invoices (created_*, issued_*), invoice-items (invoice_issued_*), coupon-usages (used_*), classes and appointments (starts_*) |
Both bounds are inclusive. A timestamp without an offset is read in the company's time zone, or
in the time zone X-Timezone-Name names when you send it, and so is a plain date sent to a
timestamp filter: created_before=2026-03-11 means midnight
at the start of March 11, 2026, so it leaves that day out. See
Dates, time zones and amounts.
Filtering example¶
Combine filters as query parameters on the same request.
curl -X GET \
-H 'X-Api-Key: YOUR_API_KEY' \
-H 'X-Client-ID: YOUR_CLIENT_ID' \
-H 'X-Company-ID: YOUR_COMPANY_ID' \
'https://YOUR_ENV_DOMAIN/api/v2/management/bookings/?client_id=123&class_start_after=2025-01-01'
Validate filters per endpoint
The filter set differs per endpoint. Names follow the patterns above, and the date format depends on the filter family. Check the API reference for the endpoint before adding a filter to your integration.
Expanding related resources¶
Supported Management GET list and detail endpoints can replace selected related-resource IDs
with their complete objects. Repeat expand[] for multiple fields and use dot notation
for nested relationships, up to two levels.
curl -X GET \
-H 'X-Api-Key: YOUR_API_KEY' \
-H 'X-Client-ID: YOUR_CLIENT_ID' \
-H 'X-Company-ID: YOUR_COMPANY_ID' \
'https://YOUR_ENV_DOMAIN/api/v2/management/bookings/?expand[]=client_id&expand[]=class_id.teacher_id'
Without expansion, class_id is an integer. With expand[]=class_id, the same
class_id field contains the class object. An unsupported field or a path deeper than
two levels returns 400 Bad Request. Each endpoint's API Reference lists its supported
expansion paths. An absent relationship remains null; an unresolved related resource
keeps its scalar ID.