Skip to main content

An asynchronous client for the Brightcove API.

Project description

brightcove_async

brightcove_async is an asynchronous Python client for the Brightcove video platform APIs. It wraps Brightcove's REST endpoints in asyncio-friendly service classes, handles OAuth2 token management for you, and validates every request and response against Pydantic models.

Features

  • Async-first API built on aiohttp.
  • OAuth2 client-credentials auth with automatic token caching and refresh.
  • Pydantic models for request bodies, query parameters, and responses, giving you validation and editor autocompletion.
  • Per-service rate limiting via aiolimiter.
  • Automatic retries for transient failures (connection drops, 401, 429) with tenacity, including Retry-After support on rate-limited responses.
  • Brightcove HTTP errors mapped to a typed exception hierarchy.
  • Coverage for the CMS, Analytics, Audience, Syndication, Dynamic Ingest, Ingest Profiles, Image, Live, Playback, and Policy APIs.

Installation

pip install brightcove_async

Requires Python 3.13 or newer.

Authentication

The client authenticates with OAuth2 client credentials. Create an API authentication credential in Brightcove Studio, then provide the client ID and secret through environment variables:

export CLIENT_ID="your_client_id"
export CLIENT_SECRET="your_client_secret"

initialise_brightcove_client() reads these by default. To pass credentials explicitly instead, construct a BrightcoveOAuthCreds:

import brightcove_async
from brightcove_async.settings import BrightcoveOAuthCreds

client = brightcove_async.initialise_brightcove_client(
    oauth_creds=BrightcoveOAuthCreds(
        client_id="your_client_id",
        client_secret="your_client_secret",
    ),
)

Tokens are fetched on first use and reused until they expire, so you don't manage them yourself.

Playback API (policy keys)

The Playback API is client-facing and does not use OAuth. It authenticates with an account policy key sent in the BCOV-Policy header. Searching (the q parameter) requires a search-enabled policy key, which Brightcove recommends keeping server-side only.

Supply the key per call, or set it once on the service and override it where needed:

async with client as bc:
    # Set a default policy key for this service...
    bc.playback.policy_key = "BCpkAD..."
    video = await bc.playback.get_video("12345", "67890")

    # ...or pass one per call (e.g. a search-enabled key)
    from brightcove_async.schemas.params import PlaybackVideosParams

    results = await bc.playback.get_videos(
        "12345",
        policy_key="search-enabled-key",
        params=PlaybackVideosParams(q="nature", limit=10),
    )
    for v in results:
        print(v.name)

Quick start

Always use the client as an async context manager. The HTTP session and OAuth client are created on entry and cleaned up on exit.

import asyncio

import brightcove_async
from brightcove_async.schemas import CreateVideoRequestBodyFields, State


async def main() -> None:
    client = brightcove_async.initialise_brightcove_client()
    async with client as bc:
        video = await bc.cms.create_video(
            account_id="12345",
            video_data=CreateVideoRequestBodyFields(
                name="My Video",
                state=State.ACTIVE,
            ),
        )
        print(video.model_dump())


if __name__ == "__main__":
    asyncio.run(main())

Services are exposed as properties on the client (bc.cms, bc.analytics, bc.audience, bc.syndication, bc.dynamic_ingest, bc.ingest_profiles, bc.images, bc.live, bc.policy) and are created lazily on first access.

Schema models can be imported from the top-level brightcove_async.schemas namespace or directly from the submodule:

# Top-level convenience (any model from any service)
from brightcove_async.schemas import Video, Playlist, GetVideosQueryParams

# Direct submodule import (preferred when using many CMS types)
from brightcove_async.schemas.cms_model import VideoVariant, FolderCreateFields
from brightcove_async.schemas.params import GetVideosQueryParams

Usage

CMS

from brightcove_async.schemas import (
    CreateVideoRequestBodyFields,
    UpdateVideoRequestBodyFields,
    GetVideosQueryParams,
    PlaylistInputFields,
    FolderCreateFields,
    RemoteAssetBody,
)

async with client as bc:
    ACCOUNT = "12345"

    # ── Videos ────────────────────────────────────────────────────────────────

    # Single page
    videos = await bc.cms.get_videos(
        account_id=ACCOUNT,
        params=GetVideosQueryParams(limit=20, sort="-created_at"),
    )

    # All videos, fetched concurrently page by page
    all_videos = await bc.cms.get_videos_for_account(account_id=ACCOUNT, page_size=100)

    # Create / update / delete
    video = await bc.cms.create_video(
        account_id=ACCOUNT,
        video_data=CreateVideoRequestBodyFields(name="My Video"),
    )
    await bc.cms.update_video(
        account_id=ACCOUNT,
        video_id=video.id,
        video_data=UpdateVideoRequestBodyFields(tags=["tutorial"]),
    )
    await bc.cms.delete_video(account_id=ACCOUNT, video_ids=[video.id])

    # ── Playlists ──────────────────────────────────────────────────────────────

    playlist = await bc.cms.create_playlist(
        account_id=ACCOUNT,
        playlist_data=PlaylistInputFields(name="My Playlist", type="EXPLICIT"),
    )
    await bc.cms.update_playlist(
        account_id=ACCOUNT,
        playlist_id=playlist.id,
        playlist_data=PlaylistInputFields(name="Renamed"),
    )
    await bc.cms.delete_playlist(account_id=ACCOUNT, playlist_id=playlist.id)

    # ── Folders ────────────────────────────────────────────────────────────────

    folder = await bc.cms.create_folder(
        account_id=ACCOUNT,
        folder_data=FolderCreateFields(name="Archive"),
    )
    await bc.cms.add_video_to_folder(
        account_id=ACCOUNT, folder_id=folder.id, video_id="6789"
    )

    # ── Remote assets (manifests / renditions) ─────────────────────────────────

    manifest = await bc.cms.add_hls_manifest(
        account_id=ACCOUNT,
        video_id="6789",
        body=RemoteAssetBody(remote_url="https://cdn.example.com/master.m3u8"),
    )
    await bc.cms.delete_hls_manifest(
        account_id=ACCOUNT, video_id="6789", asset_id=manifest.id
    )

    # ── Subscriptions ──────────────────────────────────────────────────────────

    from brightcove_async.schemas import SubscriptionCreateFields, Event

    sub = await bc.cms.create_subscription(
        account_id=ACCOUNT,
        subscription_data=SubscriptionCreateFields(
            endpoint="https://my.app/webhook",
            events=[Event.video_change],
        ),
    )
    await bc.cms.delete_subscription(account_id=ACCOUNT, subscription_id=sub.id)

Analytics

from brightcove_async.schemas.params import GetAnalyticsReportParams

async with client as bc:
    report = await bc.analytics.get_analytics_report(
        params=GetAnalyticsReportParams(
            accounts="12345",
            dimensions="video",
            from_="2026-01-01",
            to="2026-01-31",
        ),
    )
    print(report.model_dump())

Audience

from brightcove_async.schemas.params import GetLeadsParams, GetViewEventsParams

async with client as bc:
    leads = await bc.audience.get_leads(
        account_id="12345",
        params=GetLeadsParams(limit=10, sort="created_at"),
    )
    view_events = await bc.audience.get_view_events(
        account_id="12345",
        params=GetViewEventsParams(limit=10),
    )

Dynamic Ingest

from brightcove_async.schemas.dynamic_ingest_model import IngestMediaAssetbody

async with client as bc:
    result = await bc.dynamic_ingest.ingest_videos_and_assets(
        account_id="12345",
        video_id="6789",
        video_or_asset_data=IngestMediaAssetbody(
            master={"url": "https://example.com/master.mp4"},
        ),
    )

Bringing your own session

By default the client opens and closes its own aiohttp.ClientSession. To share a session you manage elsewhere, pass it to BrightcoveClient directly; in that case the client leaves the session open when the context manager exits.

import aiohttp
from brightcove_async.client import BrightcoveClient
from brightcove_async.oauth.oauth import OAuthClient
from brightcove_async.registry import build_service_registry
from brightcove_async.settings import BrightcoveBaseAPIConfig

async with aiohttp.ClientSession() as session:
    client = BrightcoveClient(
        services_registry=build_service_registry(BrightcoveBaseAPIConfig()),
        client_id="your_client_id",
        client_secret="your_client_secret",
        oauth_cls=OAuthClient,
        session=session,
    )
    async with client as bc:
        videos = await bc.cms.get_videos(account_id="12345")

Error handling

HTTP errors are translated into a typed exception hierarchy rooted at BrightcoveError. Catch the base class to handle any API failure, or a specific subclass for finer control.

from brightcove_async.exceptions import (
    BrightcoveError,
    BrightcoveResourceNotFoundError,
    BrightcoveTooManyRequestsError,
)

async with client as bc:
    try:
        video = await bc.cms.get_video_by_id(account_id="12345", video_id="000")
    except BrightcoveResourceNotFoundError:
        ...  # 404
    except BrightcoveTooManyRequestsError as exc:
        print(exc.retry_after)  # seconds, when the server sends Retry-After
    except BrightcoveError as exc:
        print(exc.status_code, exc.endpoint, exc.details)

Status codes map to exceptions as follows:

Status Exception
400 BrightcoveBadValueError
401 BrightcoveAuthError
403 BrightcoveForbiddenError
404 BrightcoveResourceNotFoundError
405 BrightcoveMethodNotAllowedError
409 BrightcoveConflictError
429 BrightcoveTooManyRequestsError
5xx / unknown BrightcoveUnknownError

Connection failures raise BrightcoveConnectionError. Transient errors (BrightcoveConnectionError, BrightcoveAuthError, BrightcoveTooManyRequestsError) are retried automatically before propagating.

Rate limiting

Each service has its own request-per-second budget enforced by an AsyncLimiter. The CMS and Ingest Profiles services default to 4 requests per second; the rest default to 10. The limits live in registry.py and can be tuned per service.

API coverage

CMS (bc.cms)

Resource Methods
Videos get_videos, create_video, update_video, delete_video, get_videos_for_account*, get_video_count, get_video_by_id
Video sources / images get_video_sources, get_video_images, delete_video_image, get_clear_video, get_video_clear_sources
Video variants get_video_variants, create_video_variant, get_video_variant, update_video_variant, delete_video_variant
Audio tracks get_video_audio_tracks, get_video_audio_track, update_video_audio_track, delete_video_audio_track
Digital master get_digital_master_info, delete_digital_master
Ingest jobs get_status_of_ingest_jobs, get_ingest_job_status
Playlist references get_playlists_for_video, remove_video_from_all_playlists
Playlists get_playlists, create_playlist, get_playlist_count, get_playlist, update_playlist, delete_playlist, get_videos_in_playlist, get_video_count_in_playlist
Custom fields get_all_video_fields, get_video_fields, create_custom_field, get_custom_field, update_custom_field, delete_custom_field
Folders get_folders, create_folder, get_folder, update_folder, delete_folder, get_videos_in_folder, add_video_to_folder, remove_video_from_folder
Labels get_labels, create_label, update_label, delete_label
Channels list_channels, get_channel_details, update_channel, list_channel_affiliates, add_affiliate, remove_affiliate
Contracts list_contracts, get_contract, approve_contract
Video shares list_shares, share_video, get_share, unshare_video
Subscriptions get_subscriptions, create_subscription, get_subscription, delete_subscription
Assets get_assets, get_dynamic_renditions
HLS manifests get_hls_manifests, add_hls_manifest, get_hls_manifest, update_hls_manifest, delete_hls_manifest
DASH manifests get_dash_manifests, add_dash_manifest, get_dash_manifest, update_dash_manifest, delete_dash_manifest
HDS manifests get_hds_manifests, add_hds_manifest, get_hds_manifest, update_hds_manifest, delete_hds_manifest
ISM manifests get_ism_manifests, add_ism_manifest, get_ism_manifest, update_ism_manifest, delete_ism_manifest
ISMC manifests get_ismc_manifests, add_ismc_manifest, get_ismc_manifest, update_ismc_manifest, delete_ismc_manifest
Renditions add_rendition, get_rendition, update_rendition, delete_rendition

* Paginated helper that fetches all pages concurrently.

Other services

Service Methods
analytics get_account_engagement, get_player_engagement, get_video_engagement, get_analytics_report, get_available_date_range, get_alltime_video_views
audience get_leads, get_view_events
syndication get_all_syndications, get_syndication, create_syndication, update_syndication, patch_syndication, delete_syndication, get_template, upload_template
dynamic_ingest ingest_videos_and_assets, get_temporary_s3_urls
ingest_profiles get_ingest_profiles
images transform_image
playback Videos: get_video, get_videos, get_related_videos. Playlists: get_playlist. Static URLs: get_hls_manifest, get_dash_manifest, get_hls_vmap, get_dash_vmap, get_highest_mp4, get_lowest_mp4. Authenticates with a policy key (see Authentication), not OAuth.
live Jobs: list_jobs, create_job, get_job, update_job, finish_job, start_job, stop_job, clip_job, force_failover, reset_origin, get_thumbnail, insert_cuepoint, get_job_metrics, get_supported_metrics, get_job_notifications, get_account_notifications. Scheduler: list_schedules, get_autostop_schedule, create_jobstartstop_schedule, get_jobstartstop_schedule, update_jobstartstop_schedule, delete_jobstartstop_schedule, list_scheduled_clips, create_scheduled_clip, get_scheduled_clip, update_scheduled_clip, delete_scheduled_clip. Playback: create_playback_token, generate_batch_sources, generate_playback_url. Sessions: get_session, get_session_events, get_resource_sessions. SSAI: list_ad_configs, create_ad_config, get_ad_config, update_ad_config, delete_ad_config. Settings/misc: list_cdn_tokens, healthcheck
policy create_policy_key, get_policy_key

Development

This project uses uv for dependency management and task running.

uv sync                       # create the environment
uv run pytest                 # run the test suite
uv run ruff check --fix       # lint
uv run ruff format            # format
uv run ty check               # type check
uv run pre-commit run --all-files

Documentation

Contributing

Pull requests are welcome. Please open an issue to discuss a feature or bug fix before submitting a PR, and include tests for any behavior change.

License

MIT License. See LICENSE.txt.

Disclaimer

This project is not affiliated with or endorsed by Brightcove Inc.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

brightcove_async-0.11.0.tar.gz (86.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

brightcove_async-0.11.0-py3-none-any.whl (65.7 kB view details)

Uploaded Python 3

File details

Details for the file brightcove_async-0.11.0.tar.gz.

File metadata

  • Download URL: brightcove_async-0.11.0.tar.gz
  • Upload date:
  • Size: 86.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for brightcove_async-0.11.0.tar.gz
Algorithm Hash digest
SHA256 e444e120c9b8740b0cc9bb4db1b755bed96ddae8e745da4264b4e3486b247cc7
MD5 cf0a9898a629dfdf6945fa9f30dbc8c4
BLAKE2b-256 597ee9558da5a1bf5694aa257c4c8a801db7ff3780e1889830804128760c3ae8

See more details on using hashes here.

Provenance

The following attestation bundles were made for brightcove_async-0.11.0.tar.gz:

Publisher: package-publish.yml on Winne004/brightcove_async

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file brightcove_async-0.11.0-py3-none-any.whl.

File metadata

File hashes

Hashes for brightcove_async-0.11.0-py3-none-any.whl
Algorithm Hash digest
SHA256 932af1f2a60c64440f48d218918d97afe123d4ea79e40461ada210c3f7dbdbd8
MD5 ae68625965a1fbb3357297e53be004f9
BLAKE2b-256 ddb286513793084b921f4c7fe6169cbdfb91a6edd36a7516da277fb2c0bf42d3

See more details on using hashes here.

Provenance

The following attestation bundles were made for brightcove_async-0.11.0-py3-none-any.whl:

Publisher: package-publish.yml on Winne004/brightcove_async

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page