pybarb-sdk
A Python SDK for consuming the BARB API v3, providing typed wrappers around all metadata and metrics endpoints, and structured error handling.
This library lets you pull TV audience data from BARB (Broadcasters' Audience Research Board) directly into Python — no manual downloads, no spreadsheets, no API knowledge required. You write a few lines of Python code and get back a ready-to-use table of data.
Table of Contents
- Before You Begin
- Quick Start
- Barb API 3.0 Endpoints
- Common Use Cases
- Troubleshooting
- Error Reference
- Exception Classes
- FAQ
- Glossary
- Contributing
- License
Who Is This For?
| I am… | This SDK helps me… |
|---|---|
| A data analyst | Pull BARB audience data straight into a pandas DataFrame for analysis in Jupyter or Excel |
| A developer | Integrate BARB data into dashboards, pipelines, or automated reports |
| A researcher | Fetch programme schedules, panel information, and viewing data without manual API work |
| New to APIs | Get started with a single conn.connect_with_tokens() call — the library handles all the complexity |
You do not need to understand APIs, HTTP, or JSON to use this library.
You just need Python installed, a BARB access token, and the code examples below.
Before You Begin
Five-minute setup checklist. Complete these steps once and you'll be ready to fetch BARB data in any Python script or notebook you write.
Step 1 — Check Python is installed
Open your terminal (on Windows: search for Command Prompt or PowerShell) and type:
python --version
You should see something like Python 3.12.x. If you see command not found or a version below
3.12, download Python here.
Verification:
Run python --version again after installing to confirm the installation was successful.
Step 2 — Set up a Virtual Environment & Install the pybarb-sdk library
A virtual environment keeps the library and its dependencies isolated from other Python projects on your computer. This prevents version conflicts and is highly recommended, especially for beginners.
1. Create a Virtual Environment: Open your terminal and navigate to your project folder:
cd path/to/your/project
Run the following command to create a virtual environment named .venv:
python -m venv .venv
2. Activate the Virtual Environment: Before installing anything, you must activate the virtual environment. You need to do this every time you open a new terminal to work on this project.
- On Windows:
.venv\Scripts\activate
- On macOS and Linux:
source .venv/bin/activate
Once activated, you should see (.venv) at the beginning of your terminal prompt.
3. Install dependencies and the pybarb-sdk library: First, install the required dependencies:
pip install requests==2.32.5 python-dotenv pandas==3.0.1 pytest==8.3.3 pyarrow
Next, install the library:
pip install pybarb
You only need to do this once.
Requirements: Python 3.12+
Core dependencies:
| Package | Version | Purpose |
|---|---|---|
requests |
== 2.32.5 | HTTP calls to BARB API |
pandas |
== 3.0.1 | DataFrame construction and manipulation |
python-dotenv |
Any | .env file support (optional settings) |
pytest |
== 8.3.3 | Testing framework |
pyarrow |
Any | Arrow formatting support |
What you should see after a successful install:
Successfully installed pybarb-sdk-0.5.3 pandas-... requests-...
Verification:
Run pip show pybarb-sdk to verify the library is installed successfully.
Common Errors & Resolutions:
-
Error:
Could not find a version that satisfies the requirement pybarb-sdkResolution: Make sure you are using a supported Python version (3.12+) and that pip is configured correctly. Try updating pip withpython -m pip install --upgrade pip. -
Error:
'pip3' is not recognized as an internal or external commandResolution: Try usingpython -m pip install pybarb-sdkinstead. If that fails, make sure Python and its Scripts folder are added to your system PATH. -
Error:
'pip' is not recognized as an internal or external commandResolution: Make sure Python is added to your system PATH. You can check the option "Add Python to PATH" when installing Python on Windows.
Step 3 — Configuration
By default, the SDK connects to the BARB production API.
Optional configuration (like log level) can be set via environment variables or a .env file.
Environment Variables
| Variable | Required | Description |
|---|---|---|
BARB_API_ROOT |
Yes | API Root URL. Must be https://api.barb.co.uk/api/v3/ |
PYBARB_LOG_LEVEL |
No | Logging level: DEBUG, INFO, WARNING, ERROR (default: INFO) |
BARB_LOG_LEVEL |
No | Alias for PYBARB_LOG_LEVEL |
Example .env file
Create a file named
.env(note the dot at the start) in your project folder.
BARB_API_ROOT='https://api.barb.co.uk/api/v3/'
PYBARB_LOG_LEVEL=INFO
Create your configuration file
The SDK can read optional settings from a file called .env (a plain text file whose name starts
with a dot). This keeps settings out of your Python code.
How to create the .env file:
- Open any plain text editor (Notepad on Windows, TextEdit on Mac, or VS Code).
- Create a new file.
- Add the mandatory API root URL, and optionally the logging level:
BARB_API_ROOT='https://api.barb.co.uk/api/v3/'
PYBARB_LOG_LEVEL=INFO
- Save the file as
.env— exactly that name, including the dot at the start and no file extension.
Where to save the .env file:
Save it in the same folder as your Python script (.py file) or Jupyter notebook.
The library automatically looks for it in the current working directory when your script runs.
Example folder layout:
my_barb_project/
.env <-- optional settings go here
fetch_audiences.py <-- your Python script goes here
Important — Keep this file private. Do not share the
.envfile, do not email it, and do not commit it to Git. If you use Git, add.envto your.gitignorefile so it is never accidentally uploaded.
Verification:
Ensure the .env file exists in your project directory and is not named .env.txt.
Step 4 — Connection
Before you can use this SDK, you must obtain an access token and a refresh token. These are provided by BARB and are generated by following the document shared by the BARB team.
Token Creation guide: https://documenter.getpostman.com/view/52530320/2sBYAswBkZ
Create a Connection once, authenticate, then reuse it with all endpoint clients.
Connect with Browser (Recommended)
Authenticate interactively via the browser using the OAuth 2.0 Authorization Code flow with PKCE. This is the most common way to authenticate if you are running the SDK locally. It will open your default web browser to the Microsoft Entra ID login page. Once you log in, it captures the authorization code and exchanges it for tokens automatically.
Environment Variables Used (all have defaults, but can be overridden):
BARB_OAUTH_TENANT_IDBARB_OAUTH_CLIENT_IDBARB_OAUTH_REDIRECT_URI(defaults tohttp://localhost:54321/callback)BARB_OAUTH_SCOPEBARB_OAUTH_TOKEN_URL
from pybarb.connection.connection import Connection
conn = Connection()
# This will open your web browser for authentication
conn.connect_with_browser(timeout=120)
print("Headers ready for use:", conn.headers)
Connect with Tokens (Alternative)
Use this method when you already have valid access and refresh tokens.
from pybarb.connection.connection import Connection
conn = Connection()
conn.connect_with_tokens(
access_token="<YOUR_ACCESS_TOKEN>",
refresh_token="<YOUR_REFRESH_TOKEN>",
expires_in=3600, # optional (seconds)
)
After this call:
conn.connectedbecomesTrueconn.headersis set (used automatically by all API clients)
Automatic Token Refresh
Access tokens expire. If you provided a refresh_token, the SDK can refresh automatically.
# Call before a long batch of requests
conn.ensure_token_valid()
# Optional: check expiry status
if conn.is_token_expired:
print("Token expired or near expiry")
Connect with Refresh Token (Alternative)
Connect using an existing refresh token to obtain a new access and refresh token pair. This bypasses the browser flow entirely and is ideal for long-running scripts or background services. Upon success, this method will automatically update the BARB_ACCESS_TOKEN and BARB_REFRESH_TOKEN environment variables for subsequent use.
Environment Variables Used:
BARB_REFRESH_TOKEN(required if the token is not explicitly passed to the method)
from pybarb.connection.connection import Connection
conn = Connection()
# Option 1: Uses BARB_REFRESH_TOKEN environment variable automatically
conn.connect_with_refresh_token()
# Option 2: Pass the refresh token explicitly
conn.connect_with_refresh_token(refresh_token="<YOUR_EXISTING_REFRESH_TOKEN>")
print("Successfully refreshed tokens for a new session.")
Connect via Redirect URL (Alternative)
Authenticate by listening for an OAuth2 authorization-code callback without opening a browser. Use this method when you have already opened the Microsoft authorization URL manually or via an external process, and you just want the SDK to capture the callback and exchange it for tokens.
Environment Variables Used:
BARB_OAUTH_REDIRECT_URI(optional, defaults tohttp://localhost:54321/callback)BARB_OAUTH_TOKEN_URL(optional)
from pybarb.connection.connection import Connection
conn = Connection()
# Listens on localhost for the ?code= callback
conn.connect_via_redirect_url(timeout=300)
print("Authentication successful without opening a browser!")
Step 5 — Run your first script
You will need an access token and a refresh token issued by BARB. Pass them directly
to connect_with_tokens(). Create a new file called test_connection.py in your project folder and
paste in the following code:
from pybarb.connection.connection import Connection
from pybarb.metadata.station import Station
conn = Connection()
conn.connect_with_tokens(
access_token="<YOUR_ACCESS_TOKEN>",
refresh_token="<YOUR_REFRESH_TOKEN>",
expires_in=3600, # token lifetime in seconds — enables automatic refresh
)
stations = Station(conn).list_stations()
print(f"Connected successfully! {len(stations)} stations available.")
print("First 5 stations:", stations[:5])
Save the file, then run it from your terminal:
python test_connection.py
What you should see:
Connected successfully! 42 stations available.
First 5 stations: ['BBC1', 'BBC2', 'ITV1', 'Channel 4', 'Channel 5']
Verification: If you see a list of stations, your connection is verified and you are ready to query data.
If you see this, your setup is complete and you are ready to use all the features described in the rest of this guide.
If you see an error instead, check the Troubleshooting section for common problems and how to fix them.
Quick Start
This section shows you how to fetch your first real audience data from BARB in a single script. It assumes you have already completed the steps in the Before You Begin section above.
What this example does: Fetches 15-minute audience figures for BBC1 on a specific day and displays them as a table.
The complete script
Create a new file called get_bbc1_audiences.py in your project folder.
Copy and paste all of the following code into it:
# ─────────────────────────────────────────────────────────────────
# get_bbc1_audiences.py
# Fetches BBC1 audience data for a single day and prints a table.
# ─────────────────────────────────────────────────────────────────
# PART 1 — Import the tools we need
# These lines load the pybarb-sdk modules into your script.
# You must include them exactly as written — do not change them.
from pybarb.connection.connection import Connection
from pybarb.metadata.station import Station
from pybarb.metrics.station.station_audiences import StationAudiences
# PART 2 — Connect to BARB
# Replace the two placeholder values with your actual tokens.
conn = Connection()
conn.connect_with_tokens(
access_token="<YOUR_ACCESS_TOKEN>", # replace with your access token (keep the quotes)
refresh_token="<YOUR_REFRESH_TOKEN>", # replace with your refresh token (keep the quotes)
expires_in=3600, # how long the token is valid for (3600 = 1 hour)
)
print("Connected to BARB successfully.")
# PART 3 — Find the station code for BBC1
# BARB uses numeric codes to identify channels internally.
# We look up BBC1's code by name so we don't need to know the number.
station_client = Station(conn)
station_code = station_client.get_station_code("BBC1")
print(f"BBC1 station code: {station_code}")
# PART 4 — Fetch the audience data
# We ask for 15-minute audience slots for BBC1 on 20th July 2023.
# Change the dates below to any date you want to query.
sa = StationAudiences(conn)
df = sa.get_station_audiences_flat_dataframe(
min_transmission_date="2023-07-20", # start date (format: YYYY-MM-DD)
max_transmission_date="2023-07-20", # end date (format: YYYY-MM-DD)
station_code=station_code, # the station code we looked up above
panel_code=50, # 50 = UK Total panel
time_period_length=15, # 15-minute slots
viewing_status="VOSDAL", # VOSDAL = same-day viewing (live + recorded same day)
)
# PART 5 — Display the results
print(f"\nTotal rows returned: {len(df)}")
print("\nFirst 5 rows of data:")
print(df.head(5).to_string(index=False))
# PART 6 — (Optional) Save the data to a spreadsheet file
# Uncomment the line below to save all the data to a CSV file you can open in Excel:
# df.to_csv("bbc1_audiences_2023-07-20.csv", index=False)
# print("Data saved to bbc1_audiences_2023-07-20.csv")
What each part does (explained simply)
| Part | What it does |
|---|---|
| Part 1 — Imports | Loads the pybarb-sdk toolkit into your script. Think of it like opening a toolbox before starting a job. |
| Part 2 — Connect | Proves to BARB that you are authorised to access data, using your tokens. |
| Part 3 — Station code | Looks up the internal number BARB uses to identify BBC1. Every channel has a unique number. |
| Part 4 — Fetch data | Sends a request to the BARB API and gets back a table of 15-minute audience figures. |
| Part 5 — Display | Prints the first 5 rows so you can see the data immediately. |
| Part 6 — Save | (Optional) Saves all the data to a .csv file you can open in Excel or Google Sheets. |
How to run it
In your terminal, navigate to the folder containing the script:
cd C:\Users\YourName\Documents\barb_project
Then run:
python get_bbc1_audiences.py
What you will see
Connected to BARB successfully.
BBC1 station code: 4934
Total rows returned: 96
First 5 rows of data:
transmission_date station_code panel_code time_period audience_size_hundreds tvr
2023-07-20 4934 50 06:00:00 12 0.02
2023-07-20 4934 50 06:15:00 18 0.03
2023-07-20 4934 50 06:30:00 45 0.08
2023-07-20 4934 50 06:45:00 62 0.11
2023-07-20 4934 50 07:00:00 98 0.17
Understanding the columns
| Column | What it means |
|---|---|
transmission_date |
The date the programme was broadcast |
station_code |
The internal BARB number for the channel (4934 = BBC1) |
panel_code |
The panel the data is from (50 = UK Total) |
time_period |
The start time of the 15-minute slot |
audience_size_hundreds |
Estimated viewers in hundreds. Value of 98 means approximately 9,800 people |
tvr |
Television Viewing Rating — percentage of the panel that watched. 0.17 means 0.17% of the UK Total panel |
How to customise the query
To pull data for a different channel, date range, or time slot length, change these lines:
min_transmission_date="2023-07-20", # change to your start date
max_transmission_date="2023-07-20", # change to your end date (can be same as start for one day)
station_code=station_client.get_station_code("ITV1"), # change "BBC1" to any channel name
panel_code=50, # 50 = UK Total; use Panels(conn).get_panels() to see all options
time_period_length=30, # change to 30 for 30-minute slots, or 60 for hourly
viewing_status="CONSOLIDATED", # change to "CONSOLIDATED" to include catch-up viewing
To see all available channel names, run this once:
print(Station(conn).list_stations())
Barb API 3.0 Endpoints
Metadata Endpoints
Folder: pybarb/metadata/ — Use modules in this folder to access Metadata APIs.
"Metadata" means reference information — the lists of stations, panels, programmes, schedules, and households that describe the BARB universe. Think of it as the "lookup tables" you need before you can make sense of audience numbers.
Every metadata endpoint works the same way:
- Create a client object (e.g.
Station(conn))- Call a method to get the data (e.g.
get_stations())- Optionally, get back a flat table (DataFrame) using the
_flat_dataframe()variant
All metadata classes follow the same pattern:
- Accept a connected
Connectionobject in their constructor. - Raise
ApiErroron validation failures or bad API responses. - Provide a
get_*()method returning rawlist[dict]and aget_*_flat_dataframe()method returning a normalisedpd.DataFrame.
Stations
File to import: pybarb.metadata.station
TV channels / broadcast stations (e.g. BBC1, ITV, Channel 4). Use this to look up the numeric station code you'll need when fetching audience data.
API endpoint: GET /meta/stations
from pybarb.metadata.station import Station
station_client = Station(conn)
| Method | Returns | Description |
|---|---|---|
get_stations() |
list[dict[str, Any]] |
Returns the full station list |
list_stations(regex_filter=None) |
list[str] |
Returns station names, optionally filtered by regex |
get_station_code(station_name) |
int | str |
Returns the station code for an exact name match |
stations_data = station_client.get_stations()
bbc_stations = station_client.list_stations(regex_filter="^BBC")
code = station_client.get_station_code("BBC1")
Viewing Stations
File to import: pybarb.metadata.viewing_stations
Viewing stations represent the channels as viewers see them (which may differ slightly from broadcast stations due to regional splits and platform variations).
API endpoint: GET /meta/viewing-stations
from pybarb.metadata.viewing_stations import ViewingStations
vs = ViewingStations(conn)
| Method | Returns | Description |
|---|---|---|
get_viewing_stations() |
list[dict[str, Any]] |
Returns all viewing stations |
list_viewing_stations(regex_filter=None) |
list[dict[str, Any]] |
Returns stations optionally filtered by name regex |
get_viewing_stations_flat_data_frame() |
pd.DataFrame |
Returns all viewing stations as a flat DataFrame |
df = vs.get_viewing_stations_flat_data_frame()
bbc1_stations = vs.list_viewing_stations("BBC1 Midlands")
Panels
File to import: pybarb.metadata.panels
A panel is a representative sample of UK households whose TV viewing is measured by BARB. Different panels cover different regions (e.g. London, Scotland, Wales). Each panel has a code (e.g.
50for the UK Total panel) that you'll need when fetching audience metrics.
API endpoint: GET /meta/panels
from pybarb.metadata.panels import Panels
panels_client = Panels(conn)
| Method | Returns | Description |
|---|---|---|
get_panels() |
list[dict[str, Any]] |
Returns all panel records |
list_panels(regex_filter=None) |
list[dict[str, Any]] |
Returns panels optionally filtered by region regex |
get_panel_code(panel_region) |
str |
Returns the panel code for an exact region match |
all_panels = panels_client.get_panels()
code = panels_client.get_panel_code("London - ITV,C4,ITV Breakfast")
Split Station Factor
File to import: pybarb.metadata.split_station_factor
Some stations broadcast the same content to multiple regions simultaneously. The split station factor adjusts audience numbers to account for this, so figures are not double-counted across regional breakdowns.
API endpoint: GET /meta/split-station-factors
from pybarb.metadata.split_station_factor import SplitStationFactor
ssf = SplitStationFactor(conn)
| Method | Returns | Description |
|---|---|---|
get_split_station_factor() |
list[dict[str, Any]] |
Returns all split station factor records |
list_split_station_factor(regex_filter=None) |
list[dict[str, Any]] |
Returns records optionally filtered by station name regex |
data = ssf.list_split_station_factor("ITV Border England")
Households
File to import: pybarb.metadata.households
Details about the households in BARB's measurement panels — including the types of TV-connected devices each household has. Useful for understanding the composition of the panel.
API endpoint: GET /meta/households
from pybarb.metadata.households import Households
hh = Households(conn)
| Method | Returns | Description |
|---|---|---|
get_households(panel_start_date, panel_end_date, ...) |
list[dict] |
Raw household records |
get_households_flat_dataframe(panel_start_date, ...) |
pd.DataFrame |
Flattened household DataFrame (devices list expanded into rows) |
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
panel_start_date |
str |
Yes | Start date in YYYY-MM-DD format |
panel_end_date |
str |
Yes | End date in YYYY-MM-DD format |
last_updated_greater_than |
str |
No | ISO datetime filter for incremental loads |
panel_code |
str |
No | Filter by panel code |
panel_region |
str |
No | Filter by panel region |
df = hh.get_households_flat_dataframe(
panel_start_date="2025-01-01",
panel_end_date="2025-05-01",
)
print(df.head(5).to_string(index=False))
Panel Members
File to import: pybarb.metadata.panel_members
The individual people within BARB panel households, along with their demographic weights. Weights are used to scale up panel results to represent the full UK population.
API endpoint: GET /meta/panel-members
from pybarb.metadata.panel_members import PanelMembers
pm = PanelMembers(conn)
| Method | Returns | Description |
|---|---|---|
get_panel_members(panel_start_date, panel_end_date, ...) |
list[dict] |
Raw panel member records |
get_panel_members_flat_dataframe(panel_start_date, ...) |
pd.DataFrame |
Flattened DataFrame (panel_member_weights list expanded into rows) |
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
panel_start_date |
str |
Yes | Start date in YYYY-MM-DD format |
panel_end_date |
str |
Yes | End date in YYYY-MM-DD format |
last_updated_greater_than |
str |
No | ISO datetime filter for incremental loads |
panel_code |
str |
No | Filter by panel code |
panel_region |
str |
No | Filter by panel region |
df = pm.get_panel_members_flat_dataframe(
panel_start_date="2025-01-01",
panel_end_date="2025-01-01",
panel_code="1",
)
Spot Schedule
File to import: pybarb.metadata.spot_schedule
The spot schedule lists all TV advertising spots (commercial breaks) that were scheduled to air on a given day and channel. Each "spot" is a single advertisement placement.
API endpoint: GET /meta/spot/schedules
from pybarb.metadata.spot_schedule import SpotSchedule
ss = SpotSchedule(conn)
| Method | Returns | Description |
|---|---|---|
get_spot_schedule(min_scheduled_date, max_scheduled_date, ...) |
list[dict] |
Raw spot schedule records |
get_spot_schedule_flat_dataframe(min_scheduled_date, ...) |
pd.DataFrame |
Flattened spot schedule DataFrame |
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
min_scheduled_date |
str |
Yes | Start date in YYYY-MM-DD format |
max_scheduled_date |
str |
Yes | End date in YYYY-MM-DD format |
station_code |
str |
No | Filter by station code |
last_updated_greater_than |
str |
No | ISO datetime filter for incremental loads |
df = ss.get_spot_schedule_flat_dataframe(
min_scheduled_date="2025-01-01",
max_scheduled_date="2025-01-01",
station_code="30",
)
Programme Schedule
File to import: pybarb.metadata.programme_schedule
The programme schedule shows what programmes were broadcast, on which channel, and when. Think of it as the TV listings guide, but in data form.
API endpoint: GET /meta/programme/schedules
from pybarb.metadata.programme_schedule import ProgrammeSchedule
ps = ProgrammeSchedule(conn)
| Method | Returns | Description |
|---|---|---|
get_programme_schedule(max_schedule_date, min_schedule_date, ...) |
list[dict] |
Raw programme schedule records |
get_programme_schedule_flat_dataframe(max_schedule_date, ...) |
pd.DataFrame |
Flattened schedule DataFrame (station_schedule list expanded into rows) |
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
max_schedule_date |
str |
Yes | End date in YYYY-MM-DD format |
min_schedule_date |
str |
Yes | Start date in YYYY-MM-DD format |
station_code |
str | list[str] |
No | Single code or list of codes |
last_updated_greater_than |
str |
No | ISO datetime filter for incremental loads |
df = ps.get_programme_schedule_flat_dataframe(
max_schedule_date="2024-01-01",
min_schedule_date="2024-01-01",
station_code=["10", "20"],
)
Target Audience Categories
File to import: pybarb.metadata.target_audience_categories
Audience categories are demographic groupings (e.g. adults 16–34, housewives with children) that BARB uses to report viewing figures. This endpoint returns the category definitions for a given panel and date range.
API endpoint: GET /meta/target-audience-categories
from pybarb.metadata.target_audience_categories import TargetAudienceCategories
tac = TargetAudienceCategories(conn)
| Method | Returns | Description |
|---|---|---|
get_target_audience_categories(max_date, min_date, panel_code) |
list[dict] |
Raw category records |
get_target_audience_categories_flat_dataframe(max_date, ...) |
pd.DataFrame |
Flattened categories DataFrame |
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
max_date |
str |
Yes | End date in YYYY-MM-DD format |
min_date |
str |
Yes | Start date in YYYY-MM-DD format |
panel_code |
int | str | list[int | str] |
Yes | Up to 10 panel codes (list or comma-separated) |
df = tac.get_target_audience_categories_flat_dataframe(
max_date="2025-01-01",
min_date="2025-01-01",
panel_code=[1, 2, 3, 4, 5],
)
Programme Content Details
File to import: pybarb.metadata.programme_content_details
Search for programmes by name to find their BARB content identifiers. The search string must be at least 3 characters long.
API endpoint: GET /meta/programme/content-details
from pybarb.metadata.programme_content_details import ProgrammeContentDetails
pcd = ProgrammeContentDetails(conn)
| Method | Parameters | Returns | Description |
|---|---|---|---|
list_programme_content_details(search_string) |
search_string: str (min 3 chars) |
list[dict] |
Searches programme content details by name |
results = pcd.list_programme_content_details("music")
Transmission Log Programme Details
File to import: pybarb.metadata.transmission_log_programme_details
Similar to Programme Content Details, but searches the transmission log — the record of what was actually broadcast (as opposed to what was scheduled).
API endpoint: GET /meta/transmission-log/programme-details
from pybarb.metadata.transmission_log_programme_details import TransmissionLogProgrammeDetails
tlpd = TransmissionLogProgrammeDetails(conn)
| Method | Parameters | Returns | Description |
|---|---|---|---|
list_transmission_log_programme_details(search_string) |
search_string: str (min 3 chars) |
list[dict] |
Searches transmission log programme details |
results = tlpd.list_transmission_log_programme_details("news")
Buyers
A list of all buyer names available in the BARB API.
API endpoint: GET /meta/buyers
from pybarb.metadata.buyers import Buyers
buyers_client = Buyers(conn)
| Method | Returns | Description |
|---|---|---|
get_buyers() |
list[str] |
Fetch all buyer names |
get_buyers_dataframe() |
pd.DataFrame |
Fetch all buyers as a flat DataFrame |
# Get as a list of strings
buyers_list = buyers_client.get_buyers()
print(f"First 5 buyers: {buyers_list[:5]}")
# Get as a DataFrame
df = buyers_client.get_buyers_dataframe()
print(f"Total buyers: {len(df)}")
print(df.head(5).to_string(index=False))
Advertisers
A list of advertisers, their brands, and commercial numbers.
API endpoint: GET /meta/advertisers
from pybarb.metadata.advertisers import Advertisers
advertisers_client = Advertisers(conn)
| Method | Returns | Description |
|---|---|---|
get_advertisers() |
list[dict[str, Any]] |
Raw advertiser records |
get_advertisers_dataframe() |
pd.DataFrame |
Flat DataFrame with one row per commercial number |
# Get as a raw list of dictionaries
adv_data = advertisers_client.get_advertisers()
# Get as a DataFrame (flattened to one row per commercial number)
df = advertisers_client.get_advertisers_dataframe()
print(f"Total commercial numbers: {len(df)}")
print(df.head(5).to_string(index=False))
Metrics Endpoints
Folder: pybarb/metrics/ — Use modules in this folder to access Metrics APIs.
"Metrics" means the actual audience numbers — how many people watched a programme, a commercial spot, or a channel during a given time period. This is the core data BARB is known for.
All three metrics endpoints return data in the same way:
get_*()→ raw data dictionary (for advanced use)get_*_flat_dataframe()→ first page as a ready-to-use tableget_all_*_flat_dataframe()→ all pages combined into one table
Station Audiences
File to import: pybarb.metrics.station.station_audiences
Pagination Note: The BARB API returns data in chunks (pages). The get_all_*_flat_dataframe() method handles pagination automatically by fetching all pages and returning them combined into one table. Alternatively, for manual pagination, use the x_next token returned in the raw JSON response to fetch the next set of records via the get_*_next_page(x_next=...) method.
How many people watched a specific channel during each time slot on a given day. You need to specify the channel (
station_code), the panel (panel_code), and the time slot length (e.g.15minutes).
API endpoint: GET /metrics/station/audiences
from pybarb.metrics.station.station_audiences import StationAudiences
sa = StationAudiences(conn)
| Method | Returns | Description |
|---|---|---|
get_station_audiences(...) |
dict[str, Any] |
Raw JSON payload containing the stations_audiences list |
get_station_audiences_flat_dataframe(...) |
pd.DataFrame |
Flattened DataFrame (audience_views list expanded into rows per time period) |
get_all_station_audiences_flat_dataframe(...) |
pd.DataFrame |
Automatically paginates and returns all records combined |
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
min_transmission_date |
str |
Yes | — | Start date in YYYY-MM-DD format |
max_transmission_date |
str |
Yes | — | End date in YYYY-MM-DD format |
station_code |
int | str |
Yes | — | Station code |
panel_code |
int | str |
Yes | — | Panel code |
time_period_length |
int | str |
Yes | — | Time period length in minutes |
viewing_status |
str |
Yes | — | e.g. "VOSDAL", "CONSOLIDATED" |
use_polling_days |
bool | str |
No | True |
Use polling days |
x_next |
str |
No | None |
Pagination token for next page |
limit |
int | str |
No | 500 |
Page size limit |
last_updated_greater_than |
str |
No | None |
ISO datetime filter |
df = sa.get_station_audiences_flat_dataframe(
min_transmission_date="2023-07-20",
max_transmission_date="2023-07-20",
station_code=4934,
panel_code=50,
time_period_length=15,
viewing_status="VOSDAL",
limit=500,
)
print(df.head(5).to_string(index=False))
Programme Ratings
File to import: pybarb.metrics.programme.programme_ratings
Pagination Note: The BARB API returns data in chunks (pages). The get_all_*_flat_dataframe() method handles pagination automatically by fetching all pages and returning them combined into one table. Alternatively, for manual pagination, use the x_next token returned in the raw JSON response to fetch the next set of records via the get_*_next_page(x_next=...) method.
How many people watched each individual programme on a given day. Results include audience size (in hundreds) and TVR (Television Viewing Rating — the percentage of the panel that watched).
API endpoint: GET /metrics/programme/ratings
from pybarb.metrics.programme.programme_ratings import ProgrammeRatings
pr = ProgrammeRatings(conn)
| Method | Returns | Description |
|---|---|---|
get_programme_ratings(...) |
dict[str, Any] |
Raw JSON payload containing the programme_ratings list |
get_programme_ratings_flat_dataframe(...) |
pd.DataFrame |
Flattened DataFrame (audience_views expanded into rows). Empty DataFrame if no results. |
get_all_programme_ratings_flat_dataframe(...) |
pd.DataFrame |
Automatically paginates and returns all records combined |
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
min_transmission_date |
str |
Yes | — | Start date in YYYY-MM-DD format |
max_transmission_date |
str |
Yes | — | End date in YYYY-MM-DD format |
panel_code |
int | str |
Yes | — | Panel code |
consolidated |
bool | str |
No | False |
Whether to use consolidated data |
x_next |
str |
No | None |
Pagination token for next page |
limit |
int | str |
No | 500 |
Page size limit |
last_updated_greater_than |
str |
No | None |
ISO datetime filter |
# Raw JSON
data = pr.get_programme_ratings(
min_transmission_date="2026-05-06",
max_transmission_date="2026-05-06",
panel_code=50,
consolidated=False,
limit=500,
)
# Flat DataFrame
df = pr.get_programme_ratings_flat_dataframe(
min_transmission_date="2026-05-06",
max_transmission_date="2026-05-06",
panel_code=50,
consolidated=False,
limit=500,
)
if not df.empty:
print(df.head(5).to_string(index=False))
else:
print("No data returned for this date range.")
Spot Impact
File to import: pybarb.metrics.spot_impact
Pagination Note: The BARB API returns data in chunks (pages). The get_all_*_flat_dataframe() method handles pagination automatically by fetching all pages and returning them combined into one table. Alternatively, for manual pagination, use the x_next token returned in the raw JSON response to fetch the next set of records via the get_*_next_page(x_next=...) method.
How many people saw each individual advertisement (spot) during a commercial break. This is used to measure advertising campaign effectiveness.
API endpoint: GET /metrics/spot/impacts
from pybarb.metrics.spot_impact import SpotImpact
si = SpotImpact(conn)
| Method | Returns | Description |
|---|---|---|
get_spot_impact(...) |
list[dict[str, Any]] |
Raw list of spot impact event records |
get_spot_impact_flat_dataframe(...) |
pd.DataFrame |
Flattened DataFrame (audience_views list expanded into rows) |
get_all_spot_impact_flat_dataframe(...) |
pd.DataFrame |
Automatically paginates and returns all records combined |
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
min_transmission_date |
str |
Yes | — | Start date in YYYY-MM-DD format |
max_transmission_date |
str |
Yes | — | End date in YYYY-MM-DD format |
station_code |
str |
No | None |
Filter by station code (comma separated) |
panel_code |
str |
No | None |
Filter by panel code (comma separated) |
advertiser_name |
str |
No | None |
Filter by advertiser name |
buyer_name |
str |
No | None |
Filter by buyer name |
consolidated |
bool |
No | True |
Whether to use consolidated data |
standardise_audiences |
str | bool |
No | None |
Standardise audiences (e.g. using_duration) |
use_reporting_days |
str | bool |
No | True |
Use reporting days instead of standard |
deduplicate_spots |
bool |
No | True |
Clean API duplicates prioritizing OMSN and non-macro regions |
last_updated_greater_than |
str |
No | None |
ISO datetime filter for incremental loads |
limit |
int |
No | None |
Page size limit |
is_staggercast_station_code |
bool |
No | None |
Filter staggercast stations |
x_next |
str |
No | None |
Pagination token for next page (manual) |
import gc
import json
from rich import print_json
from pybarb.connection.connection import Connection
from pybarb.utils.logging_config import get_logger
logger = get_logger(__name__)
def _section(title: str) -> None:
"""Print a clearly visible section banner."""
print(f"\n{'=' * 60}\n {title}\n{'=' * 60}")
def demo_spot_impact(conn: Connection) -> None:
"""Demo: Spot Impact metrics endpoint."""
_section("SPOT IMPACT")
from pybarb.metrics.spot_impact import SpotImpact
client = SpotImpact(conn)
# ── Raw JSON ──────────────────────────────────────────────────────────────
data = client.get_spot_impact(
min_transmission_date='2026-05-10',
max_transmission_date='2026-05-10',
# station_code=30,
deduplicate_spots=False, # True by default; set False to retrieve raw API duplicates
limit=9999,
advertiser_name='POSTCODE LOTTERY'
# is_staggercast_station_code='false',
)
print_json(data=data)
# ── DataFrame (With Deduplication) ────────────────────────────────────────
df_dedup = client.get_spot_impact_flat_dataframe(
min_transmission_date='2026-05-10',
max_transmission_date='2026-05-10',
# station_code=30,
deduplicate_spots=True, # Automatically clean duplicates
limit=9999,
advertiser_name='POSTCODE LOTTERY'
# is_staggercast_station_code='false',
)
# ── DataFrame (Raw with Duplicates) ───────────────────────────────────────
df_raw = client.get_spot_impact_flat_dataframe(
min_transmission_date='2026-05-10',
max_transmission_date='2026-05-10',
# station_code=30,
deduplicate_spots=False, # Fetch raw data including API duplicates
limit=9999,
advertiser_name='POSTCODE LOTTERY'
# is_staggercast_station_code='false',
)
if not df_dedup.empty and not df_raw.empty:
print(f"\nRows after deduplication: {len(df_dedup)}")
print(f"Raw rows (including duplicates): {len(df_raw)}")
print(f"Duplicate records removed: {len(df_raw) - len(df_dedup)}")
print("\nFirst 5 rows (deduplicated data):")
print(df_dedup.head(5).to_string(index=False))
else:
print("No data available.")
# ── Explicit cleanup ──────────────────────────────────────────────────────
del data, df_dedup, df_raw, client
gc.collect()
def main() -> None:
try:
# ── Establish Connection ──────────────────────────────────────────────────
conn = Connection()
conn.connect_with_browser()
# ── Execute Demo ──────────────────────────────────────────────────────────
demo_spot_impact(conn)
except FileNotFoundError as e:
logger.error("Error: %s", e)
except json.JSONDecodeError:
logger.error("Error: Failed to decode creds.json. Ensure it's valid JSON.")
except Exception:
logger.exception("Unexpected error occurred")
if __name__ == '__main__':
main()
Bulk Endpoints
Folder: pybarb/bulk/ — Use modules in this folder to access Bulk APIs.
Bulk endpoints return signed file URLs linking to Parquet files instead of direct JSON data payloads. This is designed for downloading very large datasets.
Every bulk endpoint works the same way:
- Create a client object (e.g.
ProgrammeSchedule(conn))- Fetch the JSON response containing signed URLs using
get_*()or as a flat DataFrame usingget_*_dataframe().- Download the actual Parquet files using
download_*_files(). You can specify a customdownload_diror it will default to adownloads/directory in your current working directory.- Load the downloaded Parquet files into a single Pandas DataFrame using
load_parquet_files_to_dataframe(file_paths).- (Optional) Flatten any nested JSON data within the Parquet files using
flatten_*_dataframe(df).
Downloading and Loading Data
Unlike Metadata and Metrics endpoints, Bulk endpoints require you to download files before you can analyze the actual data. The API will return one or more signed URLs, or it might return an empty list if no data is available for the given filters.
Example: Handling Multiple Files and No-Data Responses
from pathlib import Path
from pybarb.bulk.programme_schedule import ProgrammeSchedule
import pandas as pd
# 1. Create the client
bulk_ps = ProgrammeSchedule(conn)
try:
# 2. Download the files (this handles the signed URLs automatically)
# The default location is ./downloads/ if download_dir is not specified.
downloaded_files = bulk_ps.download_programme_schedule_files(
min_scheduled_date="2026-05-01",
max_scheduled_date="2026-05-31",
download_dir=Path("./my_custom_downloads")
)
# 3. Check if any data was returned
if not downloaded_files:
print("No files were returned for this date range.")
else:
print(f"Successfully downloaded {len(downloaded_files)} files.")
# 4. Load all files into a single DataFrame
df = bulk_ps.load_parquet_files_to_dataframe(downloaded_files)
# 5. Flatten any nested structures
flat_df = bulk_ps.flatten_programme_schedule_dataframe(df)
print(flat_df.head())
except Exception as e:
# Handle failed API calls or download failures
print(f"An error occurred: {e}")
Expected JSON Response (containing Signed URLs):
[
{
"scheduled_date": "2026-05-01",
"results": [
"https://barb-api-files.s3.eu-west-1.amazonaws.com/bulk/programme/schedules/2026/05/01/file1.parquet?X-Amz-Algorithm=...",
"https://barb-api-files.s3.eu-west-1.amazonaws.com/bulk/programme/schedules/2026/05/01/file2.parquet?X-Amz-Algorithm=..."
]
}
]
Programme Schedule Bulk
File to import: pybarb.bulk.programme_schedule
Programme schedule bulk files containing details of broadcasted programmes.
API endpoint: GET /api/v3/bulk/programme/schedules
from pathlib import Path
from pybarb.bulk.programme_schedule import ProgrammeSchedule
# 1. Initialize client
ps = ProgrammeSchedule(conn)
# 2. Download files
files = ps.download_programme_schedule_files(
min_scheduled_date="2026-05-01",
max_scheduled_date="2026-05-31",
station_code="4934",
download_dir=Path("./downloads/programme_schedule")
)
# 3. Load and flatten
if files:
df = ps.load_parquet_files_to_dataframe(files)
flat_df = ps.flatten_programme_schedule_dataframe(df)
print(flat_df.head())
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
min_scheduled_date |
str |
Yes | Start date in YYYY-MM-DD format |
max_scheduled_date |
str |
Yes | End date in YYYY-MM-DD format |
station_code |
str |
No | Filter by station code |
last_updated_greater_than |
str |
No | ISO datetime filter |
Spot Schedule Bulk
File to import: pybarb.bulk.spot_schedule
Spot schedule bulk files containing details of commercial advertisement spots.
API endpoint: GET /api/v3/bulk/spot/schedules
from pathlib import Path
from pybarb.bulk.spot_schedule import SpotSchedule
# 1. Initialize client
ss = SpotSchedule(conn)
# 2. Download files
files = ss.download_spot_schedule_files(
min_scheduled_date="2026-05-01",
max_scheduled_date="2026-05-31",
station_code="30",
download_dir=Path("./downloads/spot_schedule")
)
# 3. Load and flatten
if files:
df = ss.load_parquet_files_to_dataframe(files)
flat_df = ss.flatten_spot_schedule_dataframe(df)
print(flat_df.head())
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
min_scheduled_date |
str |
Yes | Start date in YYYY-MM-DD format |
max_scheduled_date |
str |
Yes | End date in YYYY-MM-DD format |
station_code |
str |
No | Filter by station code |
last_updated_greater_than |
str |
No | ISO datetime filter |
Programme Ratings Bulk
File to import: pybarb.bulk.programme_ratings
Programme ratings bulk files containing audience size and TVR for programmes.
API endpoint: GET /api/v3/bulk/programme/ratings
from pathlib import Path
from pybarb.bulk.programme_ratings import ProgrammeRatings
# 1. Initialize client
pr = ProgrammeRatings(conn)
# 2. Download files
files = pr.download_programme_ratings_files(
min_transmission_date="2026-05-01",
max_transmission_date="2026-05-31",
download_dir=Path("./downloads/programme_ratings")
)
# 3. Load and flatten
if files:
df = pr.load_parquet_files_to_dataframe(files)
flat_df = pr.flatten_programme_ratings_dataframe(df)
print(flat_df.head())
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
min_transmission_date |
str |
Yes | Start date in YYYY-MM-DD format |
max_transmission_date |
str |
Yes | End date in YYYY-MM-DD format |
last_updated_greater_than |
str |
No | ISO datetime filter |
Spot Impacts Bulk
File to import: pybarb.bulk.spot_impacts
Spot impacts bulk files for analyzing advertisement effectiveness.
API endpoint: GET /api/v3/bulk/spot/impacts
from pathlib import Path
from pybarb.bulk.spot_impacts import SpotImpacts
# 1. Initialize client
si = SpotImpacts(conn)
# 2. Download files
files = si.download_spot_impacts_files(
min_transmission_date="2026-05-01",
max_transmission_date="2026-05-31",
download_dir=Path("./downloads/spot_impacts")
)
# 3. Load and flatten
if files:
df = si.load_parquet_files_to_dataframe(files)
flat_df = si.flatten_spot_impacts_dataframe(df)
print(flat_df.head())
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
min_transmission_date |
str |
Yes | Start date in YYYY-MM-DD format |
max_transmission_date |
str |
Yes | End date in YYYY-MM-DD format |
last_updated_greater_than |
str |
No | ISO datetime filter |
Station Audience Bulk
File to import: pybarb.bulk.station_audiences
Station audience bulk files detailing viewers per channel.
API endpoint: GET /api/v3/bulk/stations/audience
from pathlib import Path
from pybarb.bulk.station_audiences import StationAudiences
# 1. Initialize client
sa = StationAudiences(conn)
# 2. Download files
files = sa.download_station_audiences_files(
min_transmission_date="2026-05-01",
max_transmission_date="2026-05-31",
download_dir=Path("./downloads/station_audiences")
)
# 3. Load and flatten
if files:
df = sa.load_parquet_files_to_dataframe(files)
flat_df = sa.flatten_station_audiences_dataframe(df)
print(flat_df.head())
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
min_transmission_date |
str |
Yes | Start date in YYYY-MM-DD format |
max_transmission_date |
str |
Yes | End date in YYYY-MM-DD format |
last_updated_greater_than |
str |
No | ISO datetime filter |
Programme Audience Bulk
File to import: pybarb.bulk.programme_audience
Programme audience bulk files.
API endpoint: GET /api/v3/bulk/programme/audiences
from pathlib import Path
from pybarb.bulk.programme_audience import ProgrammeAudience
# 1. Initialize client
pa = ProgrammeAudience(conn)
# 2. Download files
files = pa.download_programme_audience_files(
min_session_date="2026-05-01",
max_session_date="2026-05-31",
panel_code="50",
download_dir=Path("./downloads/programme_audience")
)
# 3. Load and flatten
if files:
df = pa.load_parquet_files_to_dataframe(files)
flat_df = pa.flatten_programme_audience_dataframe(df)
print(flat_df.head())
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
min_session_date |
str |
Yes | Start date in YYYY-MM-DD format |
max_session_date |
str |
Yes | End date in YYYY-MM-DD format |
panel_code |
str |
No | Filter by panel code |
last_updated_greater_than |
str |
No | ISO datetime filter |
Spot Audience Bulk
File to import: pybarb.bulk.spot_audience
Spot audience bulk files.
API endpoint: GET /api/v3/bulk/spot/audiences
from pathlib import Path
from pybarb.bulk.spot_audience import SpotAudience
# 1. Initialize client
saud = SpotAudience(conn)
# 2. Download files
files = saud.download_spot_audience_files(
min_session_date="2026-05-01",
max_session_date="2026-05-31",
panel_code="50",
download_dir=Path("./downloads/spot_audience")
)
# 3. Load and flatten
if files:
df = saud.load_parquet_files_to_dataframe(files)
flat_df = saud.flatten_spot_audience_dataframe(df)
print(flat_df.head())
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
min_session_date |
str |
Yes | Start date in YYYY-MM-DD format |
max_session_date |
str |
Yes | End date in YYYY-MM-DD format |
panel_code |
str |
No | Filter by panel code |
last_updated_greater_than |
str |
No | ISO datetime filter |
Viewing Bulk
File to import: pybarb.bulk.viewing
Bulk viewing files detailing household viewing events.
API endpoint: GET /api/v3/bulk/viewing
from pathlib import Path
from pybarb.bulk.viewing import Viewing
# 1. Initialize client
vw = Viewing(conn)
# 2. Download files
files = vw.download_viewing_files(
min_session_date="2026-05-01",
max_session_date="2026-05-31",
panel_code="50",
download_dir=Path("./downloads/viewing")
)
# 3. Load and flatten
if files:
df = vw.load_parquet_files_to_dataframe(files)
flat_df = vw.flatten_viewing_dataframe(df)
print(flat_df.head())
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
min_session_date |
str |
Yes | Start date in YYYY-MM-DD format |
max_session_date |
str |
Yes | End date in YYYY-MM-DD format |
panel_code |
str |
No | Filter by panel code |
last_updated_greater_than |
str |
No | ISO datetime filter |
Common Use Cases
Below are ready-to-copy recipes for the most common things people do with this library. Find the one that matches what you want, paste it into your script, and swap the dates / channel names for your own values.
"I want to know how many people watched BBC1 on a specific day"
from pybarb.connection.connection import Connection
from pybarb.metadata.station import Station
from pybarb.metrics.station.station_audiences import StationAudiences
conn = Connection()
conn.connect_with_tokens(
access_token="<YOUR_ACCESS_TOKEN>",
refresh_token="<YOUR_REFRESH_TOKEN>",
expires_in=3600,
)
# Find BBC1's station code
station_code = Station(conn).get_station_code("BBC1")
# Fetch 15-minute slot audiences for a single day
sa = StationAudiences(conn)
df = sa.get_all_station_audiences_flat_dataframe(
min_transmission_date="2023-07-20",
max_transmission_date="2023-07-20",
station_code=station_code,
panel_code=50, # 50 = UK Total panel
time_period_length=15, # 15-minute slots
viewing_status="VOSDAL",
)
print(f"Total time slots: {len(df)}")
print(df[["time_period", "audience_size_hundreds", "tvr"]].head(10).to_string(index=False))
"I want programme-by-programme audience figures for a channel"
from pybarb.connection.connection import Connection
from pybarb.metrics.programme.programme_ratings import ProgrammeRatings
conn = Connection()
conn.connect_with_tokens(
access_token="<YOUR_ACCESS_TOKEN>",
refresh_token="<YOUR_REFRESH_TOKEN>",
expires_in=3600,
)
pr = ProgrammeRatings(conn)
df = pr.get_all_programme_ratings_flat_dataframe(
min_transmission_date="2023-07-20",
max_transmission_date="2023-07-20",
panel_code=50,
consolidated=False, # True = includes catch-up viewing within 28 days
limit=500,
)
if df.empty:
print("No data found for this date range.")
else:
# Show top programmes by audience size
top = df.sort_values("audience_size_hundreds", ascending=False).head(10)
print(top[["programme_name", "transmission_date", "audience_size_hundreds", "tvr"]].to_string(index=False))
"I want a list of all TV channels available in BARB"
from pybarb.connection.connection import Connection
from pybarb.metadata.station import Station
conn = Connection()
conn.connect_with_tokens(
access_token="<YOUR_ACCESS_TOKEN>",
refresh_token="<YOUR_REFRESH_TOKEN>",
expires_in=3600,
)
# All channels
all_channels = Station(conn).list_stations()
print(f"Total channels: {len(all_channels)}")
print("\n".join(all_channels[:20])) # first 20
# BBC channels only
bbc_channels = Station(conn).list_stations(regex_filter="^BBC")
print("\nBBC channels:", bbc_channels)
"I want to see what programmes aired on a channel on a given day"
from pybarb.connection.connection import Connection
from pybarb.metadata.station import Station
from pybarb.metadata.programme_schedule import ProgrammeSchedule
conn = Connection()
conn.connect_with_tokens(
access_token="<YOUR_ACCESS_TOKEN>",
refresh_token="<YOUR_REFRESH_TOKEN>",
expires_in=3600,
)
station_code = Station(conn).get_station_code("ITV1")
ps = ProgrammeSchedule(conn)
df = ps.get_programme_schedule_flat_dataframe(
min_schedule_date="2024-01-15",
max_schedule_date="2024-01-15",
station_code=str(station_code),
)
print(df[["programme_name", "start_time", "duration_minutes"]].to_string(index=False))
"I want to save audience data to a CSV file"
from pybarb.connection.connection import Connection
from pybarb.metadata.station import Station
from pybarb.metrics.station.station_audiences import StationAudiences
conn = Connection()
conn.connect_with_tokens(
access_token="<YOUR_ACCESS_TOKEN>",
refresh_token="<YOUR_REFRESH_TOKEN>",
expires_in=3600,
)
station_code = Station(conn).get_station_code("Channel 4")
sa = StationAudiences(conn)
df = sa.get_all_station_audiences_flat_dataframe(
min_transmission_date="2023-07-20",
max_transmission_date="2023-07-20",
station_code=station_code,
panel_code=50,
time_period_length=30,
viewing_status="VOSDAL",
)
# Save to CSV — open in Excel or any spreadsheet tool
df.to_csv("channel4_audiences_2023-07-20.csv", index=False)
print(f"Saved {len(df)} rows to CSV.")
"I want to see the audience for a specific TV advertisement (spot)"
from pybarb.connection.connection import Connection
from pybarb.metrics.spot_impact import SpotImpact
conn = Connection()
conn.connect_with_tokens(
access_token="<YOUR_ACCESS_TOKEN>",
refresh_token="<YOUR_REFRESH_TOKEN>",
expires_in=3600,
)
si = SpotImpact(conn)
df = si.get_all_spot_impact_flat_dataframe(
min_transmission_date="2025-04-12",
max_transmission_date="2025-04-12",
station_code="30",
consolidated=False,
limit=500,
)
if not df.empty:
print(f"Total spots: {len(df)}")
print(df.head(5).to_string(index=False))
else:
print("No spot data found.")
"I want to see the reach for specific programmes"
from pybarb.connection.connection import Connection
from pybarb.metrics.programme.programme_reach import ProgrammeReach
conn = Connection()
conn.connect_with_tokens(
access_token="<YOUR_ACCESS_TOKEN>",
refresh_token="<YOUR_REFRESH_TOKEN>",
expires_in=3600,
)
pr = ProgrammeReach(conn)
df = pr.get_all_programme_reach_flat_dataframe(
programme_ids="1234,5678", # Replace with your target programme IDs
audience_names="Adults,Children", # Replace with your target audience names
start_date="2026-05-01",
end_date="2026-05-07"
)
if not df.empty:
print(f"Total reach records: {len(df)}")
print(df.head(5).to_string(index=False))
else:
print("No reach data found for these programmes.")
"I want to see the reach for specific TV advertisement spots"
from pybarb.connection.connection import Connection
from pybarb.metrics.spot.spot_reach import SpotReach
conn = Connection()
conn.connect_with_tokens(
access_token="<YOUR_ACCESS_TOKEN>",
refresh_token="<YOUR_REFRESH_TOKEN>",
expires_in=3600,
)
sr = SpotReach(conn)
df = sr.get_all_spot_reach_flat_dataframe(
clock_numbers="ABC1234,XYZ9876", # Replace with your target clock numbers
audience_names="Adults,Children", # Replace with your target audience names
start_date="2026-05-01",
end_date="2026-05-07"
)
if not df.empty:
print(f"Total spot reach records: {len(df)}")
print(df.head(5).to_string(index=False))
else:
print("No spot reach data found.")
"I want to fetch data for a large date range without running out of memory"
For very large date ranges, use the manual pagination approach to process one page at a time:
from pybarb.connection.connection import Connection
from pybarb.metrics.programme.programme_ratings import ProgrammeRatings
import pandas as pd
conn = Connection()
conn.connect_with_tokens(
access_token="<YOUR_ACCESS_TOKEN>",
refresh_token="<YOUR_REFRESH_TOKEN>",
expires_in=3600,
)
pr = ProgrammeRatings(conn)
all_pages = []
result = pr.get_programme_ratings(
min_transmission_date="2023-01-01",
max_transmission_date="2023-12-31",
panel_code=50,
limit=500,
)
all_pages.append(pd.DataFrame(result["programme_ratings"]))
x_next = result["x_next"]
page_num = 1
while x_next:
result = pr.get_programme_ratings_next_page(
x_next=x_next,
min_transmission_date="2023-01-01",
max_transmission_date="2023-12-31",
panel_code=50,
limit=500,
)
page_num += 1
page_df = pd.DataFrame(result["programme_ratings"])
page_df.to_csv(f"page_{page_num:04d}.csv", index=False) # save each page separately
print(f"Page {page_num}: {len(page_df)} records saved.")
x_next = result["x_next"]
print(f"Done. {page_num} pages fetched.")
Troubleshooting
Something not working? Check the table below for the most common problems and how to fix them. If your error isn't listed here, look at the full Error Reference section or check the error message — it usually tells you exactly what went wrong.
RuntimeError: access_token must not be blank.
What it means: You called connect_with_tokens() without providing a valid access token.
How to fix:
- Make sure you are passing a non-empty string to
access_token=. - Verify you have obtained a valid token from BARB before calling
connect_with_tokens().
RuntimeError: Unauthorized: invalid API credentials (status 401).
What it means: Your access token has expired or is invalid.
How to fix:
- Obtain a fresh access token and call
connect_with_tokens()again. - If you provided a
refresh_token, callconn.ensure_token_valid()to automatically refresh. - Contact BARB to confirm your account is still active.
ApiError: No <resource> returned.
What it means: The query returned zero results — not an error in your code.
How to fix:
- Check that your date range contains data (BARB data may have a delay of 1–2 days).
- Check your
station_codeandpanel_codeare correct. - Try a shorter date range (start with a single day:
min_date = max_date).
ApiError: Connection headers not set. Call connect_with_tokens() first.
What it means: You tried to fetch data before authenticating.
How to fix: Make sure you always call conn.connect_with_tokens() before
creating any data client objects.
conn = Connection()
conn.connect_with_tokens(
access_token="<YOUR_ACCESS_TOKEN>",
refresh_token="<YOUR_REFRESH_TOKEN>",
) # ← Must be called before anything else
sa = StationAudiences(conn)
ModuleNotFoundError: No module named 'pybarb'
What it means: The library is not installed, or you are using a different Python environment.
How to fix:
- Run
pip install pybarb-sdkagain in the same terminal you use to run your script. - If you use a virtual environment (
.venv), make sure it is activated before installing and running. - Check
python --versionmatches the Python you used to install the library.
ApiError: Too many requests to Barb API (status 429).
What it means: You have made too many API requests in a short period.
How to fix: The SDK automatically retries with exponential backoff (up to 3 retries). If this
error is still raised after retries, wait a few minutes before running your script again. Avoid
running the same large query multiple times in parallel.
I am connecting to the wrong BARB environment (UAT vs Production)
Symptoms: Your data appears to be coming from the wrong environment.
How to fix:
- Pass the environment base URL explicitly when you create the connection:
from pybarb.connection.connection import Connection
conn = Connection(api_root="https://api.barb.co.uk/api/v3/")
- Then authenticate as normal using
connect_with_tokens().
If you are using a .env file for other settings (like logging), it must be in the current working directory when you run your script.
You can check which directory Python is looking in with:
import os
print(os.getcwd()) # Should be the folder containing your .env file
I get a large number of rows and the script is very slow
How to fix:
- Narrow your date range — start with a single day.
- Lower the
limitparameter (e.g.limit=100) to fetch fewer records per page. - Save results to CSV after fetching so you don't need to re-fetch on the next run.
Error Reference
When something goes wrong, the library raises an error (exception) with a clear message explaining what happened. The two types of errors are:
ApiError— the request was understood but failed (e.g. wrong date format, no data found)RuntimeError— the connection itself failed (e.g. token expired, network unreachable)
All client errors raised are instances of ApiError (see Exception Classes).
Low-level connection failures raise RuntimeError.
Connection Errors
Raised as RuntimeError from connect_with_tokens() and token-refresh methods.
connect_with_tokens() errors
| Raised by | Condition | Message |
|---|---|---|
connect_with_tokens() |
access_token is blank |
access_token must not be blank. |
connect_with_tokens() |
refresh_token is blank |
refresh_token must not be blank. |
Token refresh errors (ensure_token_valid / _do_token_refresh)
| Raised by | Condition | Message |
|---|---|---|
ensure_token_valid() |
Token expired + no refresh token | Access token has expired and no refresh token is available. Re-authenticate using connect_with_tokens(). |
_do_token_refresh() |
Network error during refresh | Unable to reach Barb API. Check your internet connection. |
_do_token_refresh() |
Timeout during refresh | Request to Barb API timed out. Please try again. |
_do_token_refresh() |
Non-200 response from token endpoint | HTTP status error (see HTTP Status Errors) |
_do_token_refresh() |
Missing access_token in response |
Authentication response did not include an access token. |
HTTP Status Errors
These are standard web error codes. The most common ones you might see:
- 401 — your credentials are wrong or expired
- 429 — you're making too many requests (SDK retries automatically)
- 404 — the endpoint URL is wrong
Returned when the BARB API responds with a non-200 HTTP status code.
Format: "<message> (status <code>).".
| Status Code | Message |
|---|---|
400 |
Bad request to Barb API |
401 |
Unauthorized: invalid API credentials |
403 |
Forbidden: access denied by Barb API |
404 |
Barb API endpoint not found |
429 |
Too many requests to Barb API |
500 |
Barb API internal server error |
502 |
Barb API gateway error |
503 |
Barb API is temporarily unavailable |
504 |
Barb API gateway timeout |
| other | Barb API request failed |
Metadata Endpoint Errors
All raised as ApiError.
General (shared across all metadata endpoints)
| Error Key | Message | Cause |
|---|---|---|
headers_missing |
Connection headers not set. Call connect() first. |
.connect() was not called before making requests |
network_error |
Unable to fetch <resource>. Check your internet connection. |
Network exception during the API call |
malformed_json |
Invalid <resource> response from Barb API. |
Response body was not valid JSON |
payload_not_list |
Unexpected <resource> payload type: <type> |
Response parsed but was not a list |
no_results |
No <resource> returned. |
API returned an empty list |
Station-specific
| Error Key | Message | Cause |
|---|---|---|
invalid_station_name |
station_name must be a non-empty string. |
Blank or non-string station name passed |
station_not_found |
Station name '{name}' not found. |
No station matched the given name |
station_multiple_matches |
Multiple stations matched name '{name}'. |
More than one exact match found |
Panel-specific
| Error Key | Message | Cause |
|---|---|---|
panel_region_required |
panel_region must be a non-empty string. |
Blank or non-string region |
panel_not_found |
Panel region '{region}' not found. |
No panel matched the region |
panel_multiple_matches |
Multiple panels matched region '{region}'. |
More than one exact match |
invalid_regex |
Invalid regex pattern for panel_region. |
Regex compilation failed |
Households / Panel Members
| Error Key | Message | Cause |
|---|---|---|
panel_start_date_required |
panel_start_date is required. |
panel_start_date is blank/missing |
panel_end_date_required |
panel_end_date is required. |
panel_end_date is blank/missing |
Spot Schedule
| Error Key | Message | Cause |
|---|---|---|
min_scheduled_date_required |
min_scheduled_date is required. |
min_scheduled_date is blank/missing |
max_scheduled_date_required |
max_scheduled_date is required. |
max_scheduled_date is blank/missing |
Programme Schedule
| Error Key | Message | Cause |
|---|---|---|
min_schedule_date_required |
min_schedule_date is required. |
min_schedule_date is blank/missing |
max_schedule_date_required |
max_schedule_date is required. |
max_schedule_date is blank/missing |
Target Audience Categories
| Error Key | Message | Cause |
|---|---|---|
max_date_required |
max_date is required. |
max_date is blank/missing |
min_date_required |
min_date is required. |
min_date is blank/missing |
panel_code_required |
panel_code is required. |
panel_code is None |
panel_code_limit |
panel_code accepts a maximum of 10 comma separated values. |
More than 10 panel codes provided |
Search Endpoints (Programme Content / Transmission Log)
| Error Key | Message | Cause |
|---|---|---|
search_string_required |
search_string may not be blank. |
Empty or whitespace-only search string |
search_string_min_length |
search_string must be at least 3 characters long. |
Search string shorter than 3 chars |
Metrics Endpoint Errors
Applies to StationAudiences, ProgrammeRatings, and SpotImpact.
| Error Key | Message | Cause |
|---|---|---|
headers_missing |
Connection headers not set. Call connect() first. |
connect() not called |
min_transmission_date_required |
min_transmission_date is required. |
min_transmission_date is blank/missing |
max_transmission_date_required |
max_transmission_date is required. |
max_transmission_date is blank/missing |
station_code_required |
station_code is required. |
station_code is None or empty |
panel_code_required |
panel_code is required. |
panel_code is None or empty |
network_error |
Unable to fetch <resource>. Check your internet connection. |
Network exception during the API call |
malformed_json |
Invalid <resource> response from Barb API. |
Response body was not valid JSON |
payload_not_dict |
Unexpected <resource> payload type: <type> |
Response was not a dict |
no_results |
No <resource> returned. |
API returned no records |
Exception Classes
ApiError
from pybarb.utils import ApiError
Custom exception raised for all BARB API business-logic and validation failures.
| Attribute | Type | Description |
|---|---|---|
message |
str |
Human-readable error description |
status_code |
int | None |
HTTP status code if caused by an HTTP error |
response_body |
str | None |
Raw API response body for debugging |
Full error handling example
from pybarb.connection.connection import Connection
from pybarb.metadata.station import Station
from pybarb.metrics.station.station_audiences import StationAudiences
from pybarb.utils import ApiError
try:
conn = Connection()
conn.connect_with_tokens(
access_token="<YOUR_ACCESS_TOKEN>",
refresh_token="<YOUR_REFRESH_TOKEN>",
expires_in=3600,
)
station_client = Station(conn)
code = station_client.get_station_code("BBC1")
sa = StationAudiences(conn)
df = sa.get_station_audiences_flat_dataframe(
min_transmission_date="2023-07-20",
max_transmission_date="2023-07-20",
station_code=code,
panel_code=50,
time_period_length=15,
viewing_status="VOSDAL",
)
print(df.head(5).to_string(index=False))
except ApiError as e:
print(f"API error [{e.status_code}]: {e}")
if e.response_body:
print(f"Response body: {e.response_body}")
except RuntimeError as e:
print(f"Connection error: {e}")
FAQ
Answers to the questions we hear most often.
Q: Do I need a BARB subscription to use this library?
A: Yes. Access tokens are issued by BARB to organisations that have a data licence.
Contact BARB to enquire about access. Once granted, BARB will
provide you with an access token and refresh token to use with connect_with_tokens().
Q: What does "panel code 50" mean?
A: Panel code 50 refers to the UK Total panel — the combined national sample that represents
all UK TV households. Other panel codes cover specific regions (e.g. London, Scotland, Wales).
Use Panels(conn).get_panels() to see all available panels and their codes.
Q: What is the difference between VOSDAL and Consolidated viewing?
A:
- VOSDAL (Viewing on Same Day as Live) = people who watched a programme on the same day it aired, whether live or recorded and played back the same day.
- Consolidated = all viewing within 28 days of broadcast, including catch-up and time-shifted viewing.
For most ratings comparisons (e.g. "how did last night's show perform?"), use VOSDAL. For a fuller picture of total reach, use Consolidated.
Q: Why are audience figures in "hundreds"?
A: BARB reports audience sizes scaled to hundreds of viewers. So a value of 500 means
approximately 50,000 viewers. This is a longstanding industry convention. To convert:
actual_viewers ≈ audience_size_hundreds × 100.
Q: What is a TVR?
A: TVR stands for Television Viewing Rating. It is the percentage of the relevant population
(panel) that watched a particular programme or time slot. A TVR of 5.0 means 5% of the panel
watched. TVR is the standard currency for buying and selling TV advertising.
Q: How far back does the data go?
A: This depends on your BARB data licence and the specific endpoint. Contact BARB for details
about your data access window. When testing, start with recent dates (within the last 30–60 days)
to verify your query returns results before expanding to longer ranges.
Q: Can I use this in a Jupyter notebook?
A: Yes. Install pybarb-sdk as normal, place your .env file in the same folder as your
notebook (or set environment variables in the notebook cell), and use the same code examples.
The DataFrame output renders as a formatted table in Jupyter automatically.
Q: Can I export the data to Excel?
A: Yes. Once you have a DataFrame, use:
df.to_excel("output.xlsx", index=False)
You will need the openpyxl library: pip install openpyxl.
Q: How do I know when the last data update was?
A: Most endpoints accept a last_updated_greater_than parameter (ISO datetime string) that
lets you fetch only records updated after a given timestamp. This is useful for incremental
pipeline loads — store the timestamp of your last run and pass it on the next run to get only new
or changed records.
Q: Why does get_station_code("BBC One") raise a station_not_found error?
A: Station names must match exactly as they appear in the BARB data. Use
Station(conn).list_stations() to print all available station names and find the exact spelling
(e.g. "BBC1" rather than "BBC One").
Q: The script takes a long time — is it frozen?
A: Large date ranges can return thousands of pages of data. Add some progress output to your
script so you can see it is working:
page = 1
while x_next:
...
print(f"Fetched page {page} ({len(df)} total rows so far)")
page += 1
Glossary
New to TV measurement or APIs? Here are plain-English definitions for the key terms used throughout this documentation.
| Term | Plain-English Meaning |
|---|---|
| API | A way for programs to talk to each other over the internet. The BARB API is a service that lets your Python code request audience data from BARB's servers. |
| SDK | Software Development Kit — a ready-made library of code that makes it easier to use an API. Instead of writing complex HTTP requests yourself, you just call simple Python methods. |
| DataFrame | A table of data in Python (provided by the pandas library). Like a spreadsheet, it has rows and columns and can be filtered, sorted, and exported to CSV or Excel. |
| Access Token | A temporary password (usually valid for 1 hour) that proves you are allowed to use the BARB API. Pass it to connect_with_tokens() to authenticate. The SDK can automatically refresh it using a refresh token when it expires. |
| Refresh Token | A longer-lived token used to obtain a new access token when the current one expires — without needing to log in again. |
| Panel | A representative sample of UK households whose TV viewing is measured by BARB. Results from the panel are weighted to represent the full UK population. |
| Panel Code | A number identifying which BARB panel to query (e.g. 50 = UK Total). |
| Station Code | A number identifying a specific TV channel/station (e.g. BBC1 has its own code). |
| VOSDAL | "Viewing on Same Day as Live" — viewing that happened on the same day the programme was broadcast (as opposed to catch-up or recorded viewing). |
| Consolidated | Viewing figures that include catch-up and recorded viewing within 28 days of broadcast, in addition to live viewing. |
| TVR | Television Viewing Rating — the percentage of the panel's population who watched a programme or time slot. A TVR of 10 means 10% of the panel watched. |
| Audience Size (hundreds) | The estimated number of viewers, expressed in hundreds. A value of 500 means approximately 50,000 viewers. |
| x-next / Pagination | When there are too many records to return at once, the API splits results into pages. x-next is the link to the next page. The SDK can follow these links automatically. |
| Spot | A single advertisement placement in a commercial break. |
| Spot Impact | The audience figures for a specific advertisement spot — i.e. how many people saw that particular ad. |
.env file |
A plain text file (named .env) where you store optional configuration values (for example log level). The library reads this file automatically so you never have to hard-code settings in your code. |
| Rate Limiting (429) | The API limits how many requests can be made in a short period. If you exceed this, it responds with a 429 error. The SDK automatically waits and retries when this happens. |
Contributing
Contributions are welcome. Please open an issue or pull request on Bitbucket.
Before submitting a pull request:
- Install dev dependencies:
pip install -r dev-requirements.txt - Run the test suite:
python -m pytest - Check coverage:
python -m coverage run --source=pybarb -m pytest && python -m coverage report - Lint and format:
ruff check src/ && black src/
License
This project is licensed under the MIT License.
Release files for pybarb 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pybarb-1.0.0.tar.gz | 119.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pybarb-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 228.9 kB
Release files / pybarb-1.0.0.tar.gz
| Download URL | pybarb-1.0.0.tar.gz |
|---|---|
| Size | 119.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
423a40b2414e2124dc34373e30e647c32641a88345ca73dfbf038e1ab3814bbf
|
|
BLAKE2b-256 checksum How to use checksums |
c4e19bbd1d5f3eec751e4b7f0786f2fd2b56455ee923df2e1b765bea3e4cbbf8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / pybarb-1.0.0-py3-none-any.whl
| Download URL | pybarb-1.0.0-py3-none-any.whl |
|---|---|
| Size | 109.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fa10f9d69da2259e39cb9b3f5d73f9c6ee2537830cf84867564bad98988285b7
|
|
BLAKE2b-256 checksum How to use checksums |
f5354e171a459a698cb6c090de6ba2fc3b89a4d2874e67080a08da6f12d43fae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|