Skip to content

Website schedule embed

Use this guide when your goal is to display live class schedules, appointments, venues, or teacher information on an external website.

Who this is for

  • Companies that want to show their timetable on their own website without using the bsport widget.
  • Development teams building a custom booking or discovery experience on top of bsport schedule data.

What you can do

Four read endpoints provide structured access to schedule data:

Endpoint Description
GET /api/v2/management/classes/ List class sessions with dates, times, and capacity
GET /api/v2/management/appointments/ List booked appointments (not free time slots)
GET /api/v2/management/venues/ List venue details (name, address, location)
GET /api/v2/management/teachers/ List teacher profiles (name, bio, photo)

The classes endpoint defaults to the next 30 days

GET /api/v2/management/classes/ filters on starts_after (defaults to now) and starts_before (defaults to now + 30 days). Omitting both returns only the next 30 days of classes. The example below passes both explicitly, which is what you want for a schedule embed.

Fetch schedule data on your server side and serve it from your own cache. Refresh on a regular interval.

Website visitor request
    -> Your backend (cached schedule data)
    -> Rendered schedule page

Background job (every 15-60 min)
    -> Public API: classes, appointments, venues, teachers
    -> Update cache

Never call the API from client-side JavaScript

Calling the API directly from the browser exposes your API key to visitors. Always proxy requests through your backend.

import requests
import time
from datetime import datetime, timedelta, timezone

# YOUR_ENV_DOMAIN: public.staging.bsport.io or public.production.bsport.io
BASE_URL = "https://YOUR_ENV_DOMAIN"
HEADERS = {
    "X-Api-Key": "YOUR_API_KEY",
    "X-Client-ID": "YOUR_CLIENT_ID",
    "X-Company-ID": "YOUR_COMPANY_ID",
}

def fetch_all(path: str, extra_params: dict = None) -> list:
    results = []
    page = 1
    while True:
        params = {"page_size": 100, "page": page, **(extra_params or {})}
        response = requests.get(f"{BASE_URL}{path}", headers=HEADERS, params=params)
        response.raise_for_status()
        data = response.json()
        results.extend(data["results"])
        page = data["next_page"]
        if page is None:
            break
        time.sleep(0.3)  # stay within rate limit
    return results

def build_schedule_cache() -> list:
    today = datetime.now(timezone.utc)
    next_week = today + timedelta(days=7)

    venues = {v["id"]: v for v in fetch_all("/api/v2/management/venues/")}
    teachers = {t["id"]: t for t in fetch_all("/api/v2/management/teachers/")}
    classes = fetch_all(
        "/api/v2/management/classes/",
        extra_params={
            "starts_after": today.isoformat(),
            "starts_before": next_week.isoformat(),
        },
    )

    for cls in classes:
        cls["venue"] = venues.get(cls.get("venue_id"))
        cls["teacher"] = teachers.get(cls.get("teacher_id"))

    return classes

schedule = build_schedule_cache()
# Store the schedule in your backend cache.

Webhook alternative: event-driven cache invalidation

Use V2 class webhooks to invalidate and refresh your cache as soon as a class changes, instead of waiting for the next scheduled refresh.

Public API event (class.created or class.updated)
    -> your webhook endpoint
    -> trigger build_schedule_cache()
    -> update cache
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/webhook/bsport", methods=["POST"])
def handle_bsport_event():
    raw_body = request.get_data()
    verify_bsport_signature(raw_body, request.headers["Bsport-Signature"])

    payload = request.get_json()
    event_type = payload["event_type"]

    if event_type in ("class.created", "class.updated"):
        schedule = build_schedule_cache()
        store_in_cache(schedule)

    return jsonify({"status": "accepted"}), 200

verify_bsport_signature is defined in Delivery, signatures and retries.

The event data object is the current Class resource. See the event catalogue for the complete payload schema.

API key scope

For a single-company website, use a Company API key scoped to that company. Do not use a Franchisor API key for a single-company embed as it grants broader access than needed.

See Headers and tenant context.

Limitations

Automate bookings for your company

The booking endpoints are intended for your company team's workflows and third-party automation tools, not for client-facing booking interfaces. You cannot accept client bookings directly through the Public API.

Client booking: If your website needs to accept client bookings, you have two options: 1. Embed the bsport widget - use the turnkey bsport booking widget 2. Contact bsport - discuss custom booking solutions with your account manager

Schedule display: You can freely display class schedules, capacity, and availability using the read-only endpoints documented above.

Capacity and waitlist data: Classes expose current_bookings, max_capacity, max_waitlist_size and current_waitlist_size. Appointments represent existing private bookings and do not expose these capacity or waitlist counters. Appointment slots describe duration and credit cost, not free time windows.

Check the API reference for each endpoint to confirm which fields are available.