foxglove.client (foxglove-client package)
=========================================
``foxglove.client`` is the Python client for the Foxglove API. It is published separately from the
SDK, as the `foxglove-client `__ package, and its source
is in the `foxglove-python `__ repository. The
``torch`` and ``ray`` extras of ``foxglove-sdk`` install it.
This page describes ``foxglove-client`` 0.23. It covers the methods of
:py:class:`~foxglove.client.Client` that work with datasets, episodes, data coverage, and custom
properties.
Pass timezone-aware ``datetime`` values for time arguments. The client converts them with
``astimezone()``, so a naive ``datetime`` is read as local time. Times in results, such as
``created_at`` and ``start_time``, are ``datetime`` objects. Result dictionaries use snake_case
keys. Values that the client passes through unchanged, such as ``creation_context`` and a
recording's ``location``, keep the API's field names.
The REST API that these methods call is documented at https://docs.foxglove.dev/api.
The examples on this page use the ``client`` that the first example creates.
.. currentmodule:: foxglove.client
.. py:class:: Client
Client for the Foxglove API.
.. py:method:: __init__(token, host="api.foxglove.dev")
Create a client.
:param str token: Required. API key sent with every request.
:param str host: Optional, default ``"api.foxglove.dev"``. Host name of the API.
.. code-block:: python
import os
from foxglove.client import Client
client = Client(token=os.environ["FOXGLOVE_API_TOKEN"])
Datasets
--------
.. py:method:: Client.create_dataset(*, project_id, name, description=None, episode_ids=None)
Create a dataset, optionally seeding the first version with episodes.
:param str project_id: Required. Project that owns the dataset.
:param str name: Required. 1–100 characters. Unique in the Project, case-insensitive.
:param str description: Optional, default ``None``. 1–500 characters.
:param list[str] episode_ids: Optional, default ``None``. At most 2,000 episode IDs.
:returns: The dataset, with ``id``, ``project_id``, ``name``, ``description``,
``creator_org_member_id``, ``creator_api_key_id``, ``created_at``, ``updated_at``,
``episode_count``, and the counts ``added``, ``updated``, ``removed``, and
``already_present`` for ``episode_ids``.
:rtype: dict
:raises requests.exceptions.HTTPError: If the API returns an error, for example 409 when the
name is taken.
.. code-block:: python
dataset = client.create_dataset(
project_id="prj_123",
name="Training candidates",
episode_ids=["ep_1"],
)
.. py:method:: Client.get_datasets(*, project_id=None, name=None, sort_by=None, sort_order=None, limit=None, cursor=None)
List datasets.
:param str project_id: Optional, default ``None``. Restrict to one Project.
:param str name: Optional, default ``None``. Case-insensitive substring of the dataset name.
:param str sort_by: Optional, default ``None``, which sorts by ``created_at``. One of ``name``,
``created_at``, ``updated_at``, or ``episode_count``.
:param str sort_order: Optional, default ``None``. ``asc`` or ``desc``.
:param int limit: Optional, default ``None``, which returns up to 2,000 datasets. Page size,
0–2000.
:param str cursor: Optional, default ``None``. Cursor from a previous page.
:returns: A page of datasets, each as :py:meth:`~Client.get_dataset` returns it.
:rtype: Page
:raises requests.exceptions.HTTPError: If the API returns an error.
.. code-block:: python
page = client.get_datasets(project_id="prj_123")
for d in page.auto_paging_iter():
print(d["id"], d["name"], d["episode_count"])
.. py:method:: Client.get_dataset(*, dataset_id)
Return one dataset.
:param str dataset_id: Required. Dataset ID.
:returns: The dataset, with ``id``, ``project_id``, ``name``, ``description``,
``creator_org_member_id``, ``creator_api_key_id``, ``created_at``, ``updated_at``, and
``episode_count``.
:rtype: dict
:raises requests.exceptions.HTTPError: If the API returns an error, for example 404 when the
dataset does not exist.
.. code-block:: python
dataset = client.get_dataset(dataset_id="dset_123")
print(dataset["name"], dataset["episode_count"])
print(dataset["updated_at"].isoformat())
.. py:method:: Client.update_dataset(*, dataset_id, name=None, description=...)
Update a dataset's name, description, or both.
:param str dataset_id: Required. Dataset ID.
:param str name: Optional, default ``None``, which leaves the name unchanged. 1–100 characters.
:param description: Optional. 1–500 characters. Omit it to leave the description unchanged, or
pass ``None`` to clear it.
:type description: str or None
:returns: The updated dataset, as :py:meth:`~Client.get_dataset` returns it.
:rtype: dict
:raises requests.exceptions.HTTPError: If the API returns an error, for example 409 when the
name is taken.
.. code-block:: python
dataset = client.update_dataset(
dataset_id="dset_123", description=None
)
.. py:method:: Client.delete_dataset(*, dataset_id)
Delete a dataset. Its episodes remain.
:param str dataset_id: Required. Dataset ID.
:returns: ``None``.
:raises requests.exceptions.HTTPError: If the API returns an error, for example 404 when the
dataset does not exist.
.. code-block:: python
scratch = client.create_dataset(
project_id="prj_123", name="Scratch"
)
client.delete_dataset(dataset_id=scratch["id"])
.. py:method:: Client.get_dataset_episodes(*, dataset_id, sort_by=None, sort_order=None, limit=None, cursor=None, start=None, end=None, has_missing_recordings=None, recording_id=None, properties_query=None, include_recordings=False, include_properties=False, include_creation_context=False)
List the episodes in a dataset's latest committed version, or in its draft if no version is
committed.
:param str dataset_id: Required. Dataset ID.
:param str sort_by: Optional, default ``None``, which sorts by ``added_at``. One of
``added_at``, ``created_at``, ``start_time``, or ``end_time``.
:param str sort_order: Optional, default ``None``. ``asc`` or ``desc``.
:param int limit: Optional, default ``None``, which returns up to 2,000 episodes. Page size,
0–2000.
:param str cursor: Optional, default ``None``. Cursor from a previous page.
:param datetime.datetime start: Optional, default ``None``. Start of a time range. Episodes
whose time window overlaps the range are returned. Pass it together with ``end``.
:param datetime.datetime end: Optional, default ``None``. End of the time range.
:param bool has_missing_recordings: Optional, default ``None``. ``True`` returns episodes with
a missing recording. ``False`` returns episodes whose recordings are all available.
:param str recording_id: Optional, default ``None``. Return episodes that contain this
recording.
:param str properties_query: Optional, default ``None``. Space-separated ``key:value`` terms.
Episodes that match every term are returned.
:param bool include_recordings: Optional, default ``False``. Add ``recordings`` to each
episode.
:param bool include_properties: Optional, default ``False``. Add ``properties`` to each
episode.
:param bool include_creation_context: Optional, default ``False``. Add ``creation_context`` to
each episode.
:returns: A page of dicts with ``added_at``, ``added_in_version``, ``has_missing_recordings``,
and ``episode``, which has the keys that :py:meth:`~Client.get_episodes` lists.
:rtype: Page
:raises ValueError: If only one of ``start`` and ``end`` is given.
:raises requests.exceptions.HTTPError: If the API returns an error, for example 404 when the
dataset does not exist.
.. code-block:: python
page = client.get_dataset_episodes(dataset_id="dset_123")
for m in page.auto_paging_iter():
print(m["episode"]["id"], m["added_in_version"])
.. py:method:: Client.update_dataset_episodes(*, dataset_id, add=None, remove=None, update=None)
Stage episode additions, removals, and episode version changes in a dataset's draft.
:param str dataset_id: Required. Dataset ID.
:param list add: Optional, default ``None``. At most 2,000 episodes to add. Each item is an
episode ID, which selects the latest episode version, or a dict
``{"id": ..., "version": ...}`` in which ``version`` is optional.
:param list[str] remove: Optional, default ``None``. At most 2,000 episode IDs to remove.
:param list update: Optional, default ``None``. At most 2,000 episodes in the draft whose
episode version changes, in the same forms as ``add``.
:returns: The counts ``added``, ``updated``, ``removed``, and ``already_present``.
:rtype: dict
:raises requests.exceptions.HTTPError: If the API returns an error, for example when an
episode is in ``update`` and also in ``add`` or ``remove``.
.. code-block:: python
client.update_dataset_episodes(
dataset_id="dset_123", add=["ep_new"], remove=["ep_old"]
)
.. py:method:: Client.get_dataset_versions(*, dataset_id, sort_order=None, limit=None, cursor=None)
List a dataset's versions: its committed versions and its draft.
:param str dataset_id: Required. Dataset ID.
:param str sort_order: Optional, default ``None``. ``asc`` or ``desc``.
:param int limit: Optional, default ``None``, which returns up to 2,000 versions. Page size,
0–2000.
:param str cursor: Optional, default ``None``. Cursor from a previous page.
:returns: A page of versions, each with ``version_number``, ``description``, ``created_at``,
``committed_at``, ``committed_by_org_member_id``, ``committed_by_api_key_id``,
``episode_count``, ``added_episode_count``, ``removed_episode_count``, and
``changed_episode_count``. The draft's ``committed_at`` is ``None``.
:rtype: Page
:raises requests.exceptions.HTTPError: If the API returns an error, for example 404 when the
dataset does not exist.
.. code-block:: python
page = client.get_dataset_versions(dataset_id="dset_123")
for v in page.auto_paging_iter():
print(v["version_number"], v["committed_at"])
.. py:method:: Client.get_dataset_version(*, dataset_id, version_number)
Return one dataset version.
:param str dataset_id: Required. Dataset ID.
:param int version_number: Required. Dataset version number.
:returns: The version, with ``version_number``, ``description``, ``created_at``,
``committed_at``, ``committed_by_org_member_id``, ``committed_by_api_key_id``,
``episode_count``, ``added_episode_count``, ``removed_episode_count``,
``changed_episode_count``, and ``has_missing_recordings``.
:rtype: dict
:raises requests.exceptions.HTTPError: If the API returns an error, for example 404 when the
version does not exist.
.. code-block:: python
v = client.get_dataset_version(
dataset_id="dset_123", version_number=2
)
.. py:method:: Client.update_dataset_version(*, dataset_id, version_number, description)
Set the description of a committed version or of the draft.
:param str dataset_id: Required. Dataset ID.
:param int version_number: Required. Dataset version number.
:param description: Required. New description, 1–2048 characters, or ``None`` to clear it.
:type description: str or None
:returns: The version, as :py:meth:`~Client.get_dataset_version` returns it.
:rtype: dict
:raises requests.exceptions.HTTPError: If the API returns an error, for example 404 when the
version does not exist.
.. code-block:: python
client.update_dataset_version(
dataset_id="dset_123",
version_number=2,
description=None,
)
.. py:method:: Client.get_dataset_version_episodes(*, dataset_id, version_number, sort_by=None, sort_order=None, limit=None, cursor=None, start=None, end=None, has_missing_recordings=None, recording_id=None, properties_query=None, include_recordings=False, include_properties=False, include_creation_context=False)
List the episodes in one dataset version, at the episode versions it pins.
:param str dataset_id: Required. Dataset ID.
:param int version_number: Required. Dataset version number.
:param str sort_by: Optional, default ``None``, which sorts by ``added_at``. One of
``added_at``, ``created_at``, ``start_time``, or ``end_time``.
:param str sort_order: Optional, default ``None``. ``asc`` or ``desc``.
:param int limit: Optional, default ``None``, which returns up to 2,000 episodes. Page size,
0–2000.
:param str cursor: Optional, default ``None``. Cursor from a previous page.
:param datetime.datetime start: Optional, default ``None``. Start of a time range. Episodes
whose time window overlaps the range are returned. Pass it together with ``end``.
:param datetime.datetime end: Optional, default ``None``. End of the time range.
:param bool has_missing_recordings: Optional, default ``None``. ``True`` returns episodes with
a missing recording. ``False`` returns episodes whose recordings are all available.
:param str recording_id: Optional, default ``None``. Return episodes that contain this
recording.
:param str properties_query: Optional, default ``None``. Space-separated ``key:value`` terms.
Episodes that match every term are returned.
:param bool include_recordings: Optional, default ``False``. Add ``recordings`` to each
episode.
:param bool include_properties: Optional, default ``False``. Add ``properties`` to each
episode.
:param bool include_creation_context: Optional, default ``False``. Add ``creation_context`` to
each episode.
:returns: A page of dicts with ``added_at``, ``added_in_version``, ``has_missing_recordings``,
and ``episode``, which has the keys that :py:meth:`~Client.get_episodes` lists.
:rtype: Page
:raises ValueError: If only one of ``start`` and ``end`` is given.
:raises requests.exceptions.HTTPError: If the API returns an error, for example 404 when the
version does not exist.
.. code-block:: python
page = client.get_dataset_version_episodes(
dataset_id="dset_123", version_number=2
)
for m in page.auto_paging_iter():
print(m["episode"]["id"], m["episode"]["version"])
.. py:method:: Client.compare_dataset_versions(*, dataset_id, version_number, base_version, limit=None, cursor=None, include_recordings=False, include_properties=False, include_creation_context=False)
Return the episodes added, removed, or changed from ``base_version`` to ``version_number``.
:param str dataset_id: Required. Dataset ID.
:param int version_number: Required. Target version.
:param int base_version: Required. Base version.
:param int limit: Optional, default ``None``, which returns up to 2,000 changes. Page size,
0–2000.
:param str cursor: Optional, default ``None``. ``next_cursor`` from a previous call.
:param bool include_recordings: Optional, default ``False``. Add ``recordings`` to each
episode.
:param bool include_properties: Optional, default ``False``. Add ``properties`` to each
episode.
:param bool include_creation_context: Optional, default ``False``. Add ``creation_context`` to
each episode.
:returns: A dict with ``changes``, ``added_count``, ``removed_count``, ``changed_count``, and
``next_cursor``. Each change has ``added_at``, ``added_in_version``, ``episode``, and
``change``, which is ``added``, ``removed``, or ``changed``. A changed episode also has
``previous_version`` and ``previous_episode``. The counts are totals across all pages.
:rtype: dict
:raises requests.exceptions.HTTPError: If the API returns an error, for example 404 when a
version does not exist.
.. code-block:: python
diff = client.compare_dataset_versions(
dataset_id="dset_123", version_number=2, base_version=1
)
.. py:method:: Client.commit_dataset(*, dataset_id, description=...)
Commit the draft as a new version and open the next draft.
:param str dataset_id: Required. Dataset ID.
:param description: Optional. Description of the committed version, 1–2048 characters. Omit it
to keep the draft's description, or pass ``None`` to clear it.
:type description: str or None
:returns: A dict with ``committed`` and ``editable_version_number``. ``committed`` is the
committed version, with the keys that :py:meth:`~Client.get_dataset_versions` lists.
``editable_version_number`` is the number of the new draft.
:rtype: dict
:raises requests.exceptions.HTTPError: If the API returns an error, for example 409 when the
draft has no changes.
.. code-block:: python
client.commit_dataset(
dataset_id="dset_123", description="Labels batch 12"
)
.. py:method:: Client.discard_dataset(*, dataset_id)
Discard the staged changes in a dataset's draft.
:param str dataset_id: Required. Dataset ID.
:returns: The counts ``discarded_adds``, ``discarded_removes``, and ``discarded_updates``.
:rtype: dict
:raises requests.exceptions.HTTPError: If the API returns an error, for example 404 when the
dataset does not exist.
.. code-block:: python
counts = client.discard_dataset(dataset_id="dset_123")
for name, count in counts.items():
print(name, count)
.. py:method:: Client.restore_dataset_version(*, dataset_id, version_number, force=False)
Stage the changes that make the draft match a committed version.
:param str dataset_id: Required. Dataset ID.
:param int version_number: Required. Committed version to restore.
:param bool force: Optional, default ``False``. Replace the pending changes in the draft.
:returns: The counts ``added``, ``updated``, ``removed``, ``discarded_adds``,
``discarded_removes``, and ``discarded_updates``.
:rtype: dict
:raises requests.exceptions.HTTPError: If the API returns an error, for example 409 when the
draft has pending changes and ``force`` is ``False``.
.. code-block:: python
client.restore_dataset_version(
dataset_id="dset_123", version_number=1
)
.. py:method:: Client.clone_dataset_version(*, dataset_id, version_number, name, description=None, include_history=None)
Create a new dataset from a committed version.
:param str dataset_id: Required. Source dataset ID.
:param int version_number: Required. Committed version to clone.
:param str name: Required. Name of the new dataset, 1–100 characters. Unique in the Project,
case-insensitive.
:param str description: Optional, default ``None``. 1–500 characters.
:param bool include_history: Optional, default ``None``, which the API treats as ``False``.
``True`` copies every version up to and including the cloned one.
:returns: The new dataset, as :py:meth:`~Client.get_dataset` returns it.
:rtype: dict
:raises requests.exceptions.HTTPError: If the API returns an error, for example 409 when the
name is taken.
.. code-block:: python
client.clone_dataset_version(
dataset_id="dset_123",
version_number=2,
name="eval-holdout",
)
.. py:method:: Client.download_dataset(*, dataset_id, version_number, output_directory, topics=None, include_properties=False)
Export a committed version to a directory, as one MCAP file per episode and a
``manifest.json``.
:param str dataset_id: Required. Dataset ID.
:param int version_number: Required. Committed version number.
:param output_directory: Required. An empty directory, or a path that does not exist yet.
:type output_directory: str or os.PathLike
:param list[str] topics: Optional, default ``None``, which exports every topic. Topics to
export.
:param bool include_properties: Optional, default ``False``. Add each episode's custom
properties to the manifest.
:returns: The output directory.
:rtype: pathlib.Path
:raises requests.exceptions.HTTPError: If the API returns an error.
:raises RuntimeError: If the version is not committed, an episode in it has a missing
recording, ``output_directory`` is a file or a directory with contents, or every episode
fails to download.
:raises OSError: If a file cannot be written or a transfer is interrupted.
:Warns: :py:class:`DatasetDownloadWarning` -- If some episodes fail to download. The manifest
records each one.
.. code-block:: python
client.download_dataset(
dataset_id="dset_123",
version_number=2,
output_directory="out",
)
Episodes
--------
.. py:method:: Client.create_episodes(*, project_id, episodes)
Get or create up to 2,000 episodes. An item returns an existing episode when the time range,
recordings, and custom properties of its latest version match.
:param str project_id: Required. Project that owns the episodes.
:param list[dict] episodes: Required. 1–2000 items. Each item has exactly one of
``recordings``, a list of recording IDs, or ``event_id``. An item with ``recordings`` can set
``start_time`` and ``end_time``, and takes its time range from the recordings when they are
omitted. An item with ``event_id`` takes the event's time range and the completed recordings
of its device that overlap it. Any item can set ``properties``, a dict of episode custom
property values. All ``recordings`` lists together hold at most 100,000 recordings, and all
``event_id`` items together resolve to at most 10,000.
:returns: One dict per item, in request order, with ``id`` and ``created``. ``created`` is
``False`` for an existing episode.
:rtype: list[dict]
:raises ValueError: If an item has both or neither of ``recordings`` and ``event_id``, or has
``event_id`` together with ``start_time`` or ``end_time``.
:raises requests.exceptions.HTTPError: If the API returns an error.
.. code-block:: python
from datetime import datetime, timezone
episodes = client.create_episodes(
project_id="prj_123",
episodes=[
{
"recordings": ["rec_1"],
"start_time": datetime(
2026, 11, 2, 18, 0, tzinfo=timezone.utc
),
"end_time": datetime(
2026, 11, 2, 18, 5, tzinfo=timezone.utc
),
"properties": {"task": "fold_towel"},
}
],
)
.. py:method:: Client.get_episodes(*, project_id=None, start=None, end=None, has_missing_recordings=None, recording_id=None, exact_recording_ids=None, properties_query=None, sort_by=None, sort_order=None, limit=None, cursor=None, include_recordings=False, include_properties=False, include_creation_context=False)
List episodes, each at its latest version.
:param str project_id: Optional, default ``None``. Restrict to one Project.
:param datetime.datetime start: Optional, default ``None``. Start of a time range. Episodes
whose time window overlaps the range are returned. Pass it together with ``end``.
:param datetime.datetime end: Optional, default ``None``. End of the time range.
:param bool has_missing_recordings: Optional, default ``None``. ``True`` returns episodes with
a missing recording. ``False`` returns episodes whose recordings are all available.
:param str recording_id: Optional, default ``None``. Return episodes that contain this
recording.
:param list[str] exact_recording_ids: Optional, default ``None``. Return episodes whose
recordings are exactly these 1–50 recording IDs.
:param str properties_query: Optional, default ``None``. Space-separated ``key:value`` terms.
Episodes that match every term are returned.
:param str sort_by: Optional, default ``None``, which sorts by ``created_at``. One of
``created_at``, ``start_time``, or ``end_time``.
:param str sort_order: Optional, default ``None``. ``asc`` or ``desc``.
:param int limit: Optional, default ``None``, which returns up to 2,000 episodes. Page size,
0–2000.
:param str cursor: Optional, default ``None``. Cursor from a previous page.
:param bool include_recordings: Optional, default ``False``. Add ``recordings`` and
``has_missing_recordings`` to each episode.
:param bool include_properties: Optional, default ``False``. Add ``properties`` to each
episode.
:param bool include_creation_context: Optional, default ``False``. Add ``creation_context`` to
each episode.
:returns: A page of episodes, each with ``id``, ``project_id``, ``start_time``, ``end_time``,
``version``, ``latest_version``, ``creator_org_member_id``, ``creator_api_key_id``, and
``created_at``. Each item of ``recordings`` has ``id``, ``path``, ``location``, ``start``,
``end``, ``device_id``, and ``available``.
:rtype: Page
:raises ValueError: If only one of ``start`` and ``end`` is given, or ``exact_recording_ids``
is empty.
:raises requests.exceptions.HTTPError: If the API returns an error.
.. code-block:: python
page = client.get_episodes(project_id="prj_123")
.. py:method:: Client.get_episode(*, episode_id, include_recordings=False, include_properties=False, version=None)
Return one episode.
:param str episode_id: Required. Episode ID.
:param bool include_recordings: Optional, default ``False``. Add ``recordings`` and
``has_missing_recordings``.
:param bool include_properties: Optional, default ``False``. Add ``properties``.
:param int version: Optional, default ``None``, which selects the latest version. Episode
version to return.
:returns: The episode, with ``id``, ``project_id``, ``start_time``, ``end_time``, ``version``,
``latest_version``, ``creator_org_member_id``, ``creator_api_key_id``, ``created_at``, and
``creation_context``. Each item of ``recordings`` has ``id``, ``path``, ``location``,
``start``, ``end``, ``device_id``, and ``available``.
:rtype: dict
:raises requests.exceptions.HTTPError: If the API returns an error, for example 404 when the
episode does not exist.
.. code-block:: python
episode = client.get_episode(episode_id="ep_123")
.. py:method:: Client.update_episode(*, episode_id, applies_to_version=None, start_time=None, end_time=None, recordings=None, properties=None)
Update one episode. An update that changes the episode creates a new episode version.
:param str episode_id: Required. Episode ID.
:param int applies_to_version: Optional, default ``None``. Episode version that the update
applies to. The API rejects the update when it is not the latest version.
:param datetime.datetime start_time: Optional, default ``None``. New inclusive start of the
episode window.
:param datetime.datetime end_time: Optional, default ``None``. New inclusive end of the episode
window.
:param dict recordings: Optional, default ``None``. Recording IDs to add and remove, as
``{"add": [...], "remove": [...]}``.
:param dict properties: Optional, default ``None``. Custom property values to merge into the
episode's properties. A value of ``None`` unsets its key.
:returns: The episode at the resulting version, with the keys that
:py:meth:`~Client.get_episodes` lists.
:rtype: dict
:raises requests.exceptions.HTTPError: If the API returns an error, for example when
``applies_to_version`` is not the latest version.
.. code-block:: python
client.update_episode(
episode_id="ep_123", properties={"outcome": "success"}
)
.. py:method:: Client.update_episodes(*, episodes)
Update up to 2,000 episodes in one transaction. If one update fails, none is applied.
:param list[dict] episodes: Required. At most 2,000 items. Each item has ``id`` and can set
``applies_to_version``, ``start_time``, ``end_time``, ``recordings``, and ``properties``,
which have the same meaning as the parameters of :py:meth:`~Client.update_episode`.
:returns: One dict per item, in request order, with ``id`` and ``version``, the episode's
resulting version.
:rtype: list[dict]
:raises KeyError: If an item has no ``id``.
:raises requests.exceptions.HTTPError: If the API returns an error.
.. code-block:: python
client.update_episodes(
episodes=[
{
"id": "ep_123",
"properties": {"outcome": "success"},
}
]
)
.. py:method:: Client.delete_episode(*, episode_id)
Delete an episode that no dataset holds.
:param str episode_id: Required. Episode ID.
:returns: ``None``.
:raises requests.exceptions.HTTPError: If the API returns an error, for example 409 when a
dataset holds the episode.
.. code-block:: python
client.delete_episode(episode_id="ep_123")
.. py:method:: Client.get_episode_versions(*, episode_id, limit=None, cursor=None)
List an episode's versions, newest first.
:param str episode_id: Required. Episode ID.
:param int limit: Optional, default ``None``, which returns up to 2,000 versions. Page size,
0–2000.
:param str cursor: Optional, default ``None``. Cursor from a previous page.
:returns: A page of dicts with ``version``, ``created_at``, ``creator_org_member_id``,
``creator_api_key_id``, and ``changed_fields``.
:rtype: Page
:raises requests.exceptions.HTTPError: If the API returns an error, for example 404 when the
episode does not exist.
.. code-block:: python
page = client.get_episode_versions(episode_id="ep_123")
.. py:method:: Client.iter_messages(*, device_id=None, device_name=None, session_id=None, session_key=None, start=None, end=None, topics=None, decoder_factories=None, project_id=None, episode_id=None, episode_version=None, recording_id=None, recording_key=None, replay_policy=None, replay_lookback_seconds=None)
Stream decoded messages. Messages are returned in log-time order across the selected topics.
``start`` and ``end`` are required unless ``episode_id`` is given, or ``recording_id`` or
``recording_key`` is given without a device.
:param str device_id: Optional, default ``None``. Device that originated the data. Requires
``start`` and ``end``.
:param str device_name: Optional, default ``None``. Name of the device that originated the
data. Requires ``start`` and ``end``.
:param str session_id: Optional, default ``None``. Session to stream. Requires ``start`` and
``end``.
:param str session_key: Optional, default ``None``. Key of the session to stream. Requires
``start`` and ``end``.
:param datetime.datetime start: Optional, default ``None``. Inclusive start of the stream. Pass
it together with ``end``.
:param datetime.datetime end: Optional, default ``None``. Inclusive end of the stream.
:param list[str] topics: Optional, default ``None``, which streams every topic. Topics to
stream.
:param decoder_factories: Optional, default ``None``, which uses the client's default
decoders: JSON, plus Protobuf, ROS 1, and ROS 2 when the matching ``mcap`` support package is
installed. MCAP decoder factories to use instead.
:type decoder_factories: list[~mcap.decoder.DecoderFactory]
:param str project_id: Optional, default ``None``. Project of the device or session. Required
with ``session_key``, and with ``device_name`` when the name exists in more than one Project.
:param str episode_id: Optional, default ``None``. Episode to stream. Its time range is used
when ``start`` and ``end`` are omitted.
:param int episode_version: Optional, default ``None``, which selects the latest version.
Episode version to stream. Requires ``episode_id``.
:param str recording_id: Optional, default ``None``. Recording to stream.
:param str recording_key: Optional, default ``None``. Key of the recording to stream.
:param str replay_policy: Optional, default ``None``. ``lastPerChannel`` adds the most recent
message on each channel from before ``start``.
:param float replay_lookback_seconds: Optional, default ``None``. How far before ``start`` to
look for those messages. Used with ``replay_policy="lastPerChannel"``.
:returns: A generator of ``(schema, channel, message, decoded_message)`` tuples.
:raises RuntimeError: If no device, session, recording, or episode is given, ``episode_id`` is
combined with another of them, a recording is combined with a session, ``episode_version``
is given without ``episode_id``, or ``start`` and ``end`` are missing when required.
:raises requests.exceptions.HTTPError: If the API returns an error.
.. code-block:: python
for schema, channel, message, decoded in client.iter_messages(
recording_id="rec_123",
topics=["/camera/front"],
):
print(channel.topic, message.log_time)
.. py:method:: Client.download_data(*, device_id=None, device_name=None, session_id=None, session_key=None, start=None, end=None, topics=None, output_format=OutputFormat.mcap, compression_format=None, callback=None, project_id=None, episode_id=None, episode_version=None, include_attachments=None, recording_id=None, recording_key=None, replay_policy=None, replay_lookback_seconds=None)
Download the data of a device, session, recording, or episode as bytes.
``start`` and ``end`` are required unless ``episode_id`` is given, or ``recording_id`` or
``recording_key`` is given without a device.
:param str device_id: Optional, default ``None``. Device that originated the data. Requires
``start`` and ``end``.
:param str device_name: Optional, default ``None``. Name of the device that originated the
data. Requires ``start`` and ``end``.
:param str session_id: Optional, default ``None``. Session to download. Requires ``start`` and
``end``.
:param str session_key: Optional, default ``None``. Key of the session to download. Requires
``start`` and ``end``.
:param datetime.datetime start: Optional, default ``None``. Inclusive start of the time range.
Pass it together with ``end``.
:param datetime.datetime end: Optional, default ``None``. Inclusive end of the time range.
:param list[str] topics: Optional, default ``None``, which downloads every topic. Topics to
download.
:param OutputFormat output_format: Optional, default ``OutputFormat.mcap``. Format of the
returned data.
:param CompressionFormat compression_format: Optional, default ``None``, for which the API
uses LZ4. Compression of the chunks in MCAP output.
:param callback: Optional, default ``None``. A function that the client calls as
``callback(progress=n)`` during the download, where ``n`` is the number of bytes received
so far.
:param str project_id: Optional, default ``None``. Project of the device or session. Required
with ``session_key``, and with ``device_name`` when the name exists in more than one Project.
:param str episode_id: Optional, default ``None``. Episode to download. Its time range is used
when ``start`` and ``end`` are omitted.
:param int episode_version: Optional, default ``None``, which selects the latest version.
Episode version to download. Requires ``episode_id``.
:param bool include_attachments: Optional, default ``None``. Include MCAP attachments. Applies
to MCAP output for a recording, session, or episode.
:param str recording_id: Optional, default ``None``. Recording to download.
:param str recording_key: Optional, default ``None``. Key of the recording to download.
:param str replay_policy: Optional, default ``None``. ``lastPerChannel`` adds the most recent
message on each channel from before ``start``.
:param float replay_lookback_seconds: Optional, default ``None``. How far before ``start`` to
look for those messages. Used with ``replay_policy="lastPerChannel"``.
:returns: The data in ``output_format``.
:rtype: bytes
:raises RuntimeError: If no device, session, recording, or episode is given, ``episode_id`` is
combined with another of them, a recording is combined with a session, ``episode_version``
is given without ``episode_id``, or ``start`` and ``end`` are missing when required.
:raises requests.exceptions.HTTPError: If the API returns an error.
.. code-block:: python
data = client.download_data(
episode_id="ep_123", topics=["/camera/front"]
)
.. py:method:: Client.get_topics(*, device_id=None, device_name=None, start=None, end=None, include_schemas=False, project_id=None, session_id=None, session_key=None, episode_id=None, limit=None, offset=None, recording_id=None, recording_key=None, episode_version=None, sort_by=None, sort_order=None)
List the topics of a device, session, recording, or episode.
``start`` and ``end`` are required with ``device_id`` or ``device_name``.
Session, recording, and episode queries can omit them.
:param str device_id: Optional, default ``None``. Device to list topics for.
:param str device_name: Optional, default ``None``. Name of the device to list topics for.
:param datetime.datetime start: Optional, default ``None``. Start of the time range. Pass it
together with ``end``.
:param datetime.datetime end: Optional, default ``None``. End of the time range.
:param bool include_schemas: Optional, default ``False``. Add ``schema`` to each topic.
:param str project_id: Optional, default ``None``. Restrict to one Project.
:param str session_id: Optional, default ``None``. Session to list topics for.
:param str session_key: Optional, default ``None``. Key of the session to list topics for.
:param str episode_id: Optional, default ``None``. Episode to list topics for. Its time range
is used when ``start`` and ``end`` are omitted.
:param int limit: Optional, default ``None``, which returns up to 2,000 topics. Page size,
0–2000.
:param int offset: Optional, default ``None``. Number of topics to skip.
:param str recording_id: Optional, default ``None``. Recording to list topics for.
:param str recording_key: Optional, default ``None``. Key of the recording to list topics for.
:param int episode_version: Optional, default ``None``, which selects the latest version.
Episode version to list topics for. Requires ``episode_id``.
:param str sort_by: Optional, default ``None``. ``topic`` or ``version``.
:param str sort_order: Optional, default ``None``. ``asc`` or ``desc``.
:returns: One dict per topic, with ``topic``, ``version``, ``encoding``, ``schema_encoding``,
and ``schema_name``. With ``include_schemas``, each also has ``schema``, as ``bytes``
or ``None`` when no schema is available.
:rtype: list[dict]
:raises RuntimeError: If ``episode_version`` is given without ``episode_id``, ``episode_id`` is
combined with a device, session, or recording, or ``start`` and ``end`` are missing when
required.
:raises requests.exceptions.HTTPError: If the API returns an error.
.. code-block:: python
topics = client.get_topics(episode_id="ep_123")
print([t["topic"] for t in topics])
Data coverage
-------------
.. py:method:: Client.get_coverage(*, start=None, end=None, device_id=None, device_name=None, tolerance=None, project_id=None, session_id=None, session_key=None, recording_id=None, recording_key=None, include_edge_recordings=None)
List the time ranges for which Foxglove has data.
:param datetime.datetime start: Optional, default ``None``. Start of the time range. Required
with ``end`` unless a recording or session is given.
:param datetime.datetime end: Optional, default ``None``. End of the time range.
:param str device_id: Optional, default ``None``. Restrict to one device.
:param str device_name: Optional, default ``None``. Restrict to the device with this name.
:param int tolerance: Optional, default ``None``. Minimum gap, in seconds, for two ranges to
count as separate.
:param str project_id: Optional, default ``None``. Restrict to one Project.
:param str session_id: Optional, default ``None``. Restrict to one session.
:param str session_key: Optional, default ``None``. Restrict to the session with this key.
:param str recording_id: Optional, default ``None``. Restrict to one recording.
:param str recording_key: Optional, default ``None``. Restrict to the recording with this key.
:param bool include_edge_recordings: Optional, default ``None``, which the API treats as
``False``. Include recordings from an Edge Site or a device running Foxlet. Each range then
has an ``import_status``.
:returns: One dict per range, with ``device_id``, ``device``, ``start``, ``end``, and
``import_status``. ``device`` is the device summary as the API returns it.
:rtype: list[dict]
:raises requests.exceptions.HTTPError: If the API returns an error.
.. code-block:: python
from datetime import datetime, timezone
ranges = client.get_coverage(
device_id="dev_123",
start=datetime(2026, 11, 2, tzinfo=timezone.utc),
end=datetime(2026, 11, 3, tzinfo=timezone.utc),
)
Custom properties
-----------------
The ``properties`` of an episode use keys that are defined as custom properties with
``resource_type="episode"``.
.. py:method:: Client.create_custom_property(*, key, label, resource_type, value_type, enum_values=None)
Create a custom property.
:param str key: Required. Immutable identifier used in queries. 1–32 characters from
``A-Z``, ``a-z``, ``0-9``, ``_``, and ``-``.
:param str label: Required. Display label, 1–32 characters.
:param str resource_type: Required. ``device``, ``event``, ``session``, or ``episode``.
:param str value_type: Required. ``string``, ``multiline-string``, ``number``, ``enum``,
``boolean``, or ``multi-enum``.
:param list[str] enum_values: Optional, default ``None``. Allowed values. Required when
``value_type`` is ``enum`` or ``multi-enum``.
:returns: The custom property, with ``id``, ``key``, ``label``, ``resource_type``,
``value_type``, ``enum_values``, and ``has_associated_data``.
:rtype: dict
:raises requests.exceptions.HTTPError: If the API returns an error.
.. code-block:: python
client.create_custom_property(
key="outcome",
label="Outcome",
resource_type="episode",
value_type="enum",
enum_values=["success", "failure"],
)
.. py:method:: Client.get_custom_properties(*, resource_type=None)
List custom properties.
:param str resource_type: Optional, default ``None``, which returns every type. ``device``,
``event``, ``session``, or ``episode``.
:returns: The custom properties, each as :py:meth:`~Client.get_custom_property` returns it.
:rtype: list[dict]
:raises requests.exceptions.HTTPError: If the API returns an error.
.. code-block:: python
props = client.get_custom_properties(resource_type="episode")
.. py:method:: Client.get_custom_property(*, custom_property_id)
Return one custom property.
:param str custom_property_id: Required. Custom property ID.
:returns: The custom property, with ``id``, ``key``, ``label``, ``resource_type``,
``value_type``, ``enum_values``, and ``has_associated_data``.
:rtype: dict
:raises requests.exceptions.HTTPError: If the API returns an error.
.. code-block:: python
prop = client.get_custom_property(custom_property_id="cp_123")
.. py:method:: Client.update_custom_property(*, custom_property_id, label=None, enum_values=None)
Update a custom property's label, allowed values, or both.
:param str custom_property_id: Required. Custom property ID.
:param str label: Optional, default ``None``, which leaves the label unchanged. New display
label, 1–32 characters.
:param list[str] enum_values: Optional, default ``None``, which leaves the values unchanged.
New list of allowed values. It contains every existing value and can add or reorder values.
:returns: The custom property, as :py:meth:`~Client.get_custom_property` returns it.
:rtype: dict
:raises requests.exceptions.HTTPError: If the API returns an error.
.. code-block:: python
client.update_custom_property(
custom_property_id="cp_123", label="Outcome"
)
.. py:method:: Client.delete_custom_property(*, custom_property_id)
Delete a custom property.
:param str custom_property_id: Required. Custom property ID.
:returns: ``None``.
:raises requests.exceptions.HTTPError: If the API returns an error.
.. code-block:: python
client.delete_custom_property(custom_property_id="cp_123")
Supporting types
----------------
.. py:class:: Page
One page of results from a list method.
.. py:attribute:: items
The results on this page, as a list.
.. py:attribute:: next_cursor
Cursor of the next page, or ``None`` on the last page.
.. py:attribute:: previous_cursor
Cursor of the previous page, or ``None`` when the API returns none.
.. py:method:: auto_paging_iter()
Yield the results on this page, then fetch each following page and yield its results.
:returns: An iterator over the results.
:raises requests.exceptions.HTTPError: If the API returns an error for a following page.
.. code-block:: python
for dataset in client.get_datasets().auto_paging_iter():
print(dataset["name"])
.. py:class:: DatasetDownloadWarning
Warning that :py:meth:`Client.download_dataset` emits when some episodes fail to download. A
subclass of ``UserWarning``.
.. py:class:: OutputFormat
Output formats for :py:meth:`Client.download_data`. An ``enum.Enum``.
.. py:attribute:: bag
:value: "bag1"
A ``.bag`` file.
.. py:attribute:: mcap
:value: "mcap"
An ``.mcap`` file.
.. py:attribute:: mcap0
:value: "mcap0"
Deprecated. Use ``mcap``.
.. py:class:: CompressionFormat
Chunk compression formats for :py:meth:`Client.download_data`. An ``enum.Enum``.
.. py:attribute:: none
:value: ""
No compression.
.. py:attribute:: zstd
:value: "zstd"
Zstandard compression.
.. py:attribute:: lz4
:value: "lz4"
LZ4 compression.