Rerun comes with the ability to get data out of Rerun from code. This page provides an overview of the API, as well as recipes to load the data in popular packages such as Pandas, Polars, and DuckDB.
Starting a server with recordings
The first step to query data is to start a catalog server and load it with a dataset containing your recording.
See the catalog object model docs for more details on how datasets are organized in Rerun.
import rerun as rr
# Start a server with one or more .rrd files
with rr.server.Server(datasets={"my_dataset": ["recording.rrd"]}) as server:
client = server.client()
dataset = client.get_dataset("my_dataset")The server can host multiple datasets. Each dataset maps to either a list of .rrd files or a directory (which will be scanned for .rrd files):
with rr.server.Server(
datasets={
# Explicit list of RRD files
"dataset1": ["recording1.rrd", "recording2.rrd"],
# Directory containing RRD files
"dataset2": "/path/to/recordings_dir",
}
) as server:
client = server.client()
# Access each dataset by name
ds1 = client.get_dataset("dataset1")
ds2 = client.get_dataset("dataset2")When multiple recordings are loaded into a dataset, each gets mapped to a separate segment whose ID is the corresponding recording ID.
You can also start a longer running server in a separate process and connect to it by its local address. In one file or terminal launch the server and print its address,
server = rr.server.Server()
print(server.url())in a separate file or terminal connect to that url
client = rr.catalog.CatalogClient(server.url())Adding new datasets
New datasets can also be created or appended after the server is launched:
dataset = client.create_dataset(
name="oss_demo",
)
dataset.register([Path("/path/to/recording/recording.rrd").resolve().as_uri()]).wait()Viewing datasets
Either specify the network location with the CLI at launch:
rerun rerun+http://localhost:51234or open the command palette in the viewer (cmd/ctrl + K or via the menu) and pick "Connect to a server…".
Set the scheme to http and enter the hostname and port in the dialog.
Getting an overview
Before querying, info() summarizes what a dataset holds: its segments, layers, timeline ranges, contents and property columns.
It reads only the segment table and the schema, so it stays fast on large datasets.
>>> dataset.info()
DatasetInfo(robot_episodes: 3 segments, 41.1 KiB)
contents: 3 entities, 3 component columns
properties: property:RecordingInfo:start_time, property:episode:operator, property:episode:success
layers:
base: in 3 segments
predictions: in 2 segments
timelines:
capture_time: timestamp, 2026-03-01 09:00:00Z … 2026-03-01 11:02:19Z, in 3 segments
frame: sequence, #0 … #139, in 3 segmentsThe same information is available as fields, e.g. {layer.name: layer.num_segments for layer in dataset.info().layers}.
See DatasetInfo for the full list.
Inspecting the schema
The content of a dataset can be inspected using the schema() method:
schema = dataset.schema()
schema.index_columns() # list of all index columns (timelines)
schema.component_columns() # list of all component columns
schema.column_names() # list of all column names
schema.to_arrow() # convert to a PyArrow schemaTo inspect one segment's schema, use:
segment_schema = dataset.segment(segment_id).schema()Referenced assets are excluded from the segment's schema.
Component column names follow the pattern $entity_path:$Archetype:$field, e.g. /robot/gripper/force:Scalars:scalars.
Data logged without an archetype, such as with rr.AnyValues, omits the $Archetype part, e.g. /robot/meta:mode.
Querying a dataset using reader
The primary means of querying data is the reader() method. In its simplest form, it is used as follows:
df = dataset.reader(index="frame_nr")
print(df)Printing a DataFrame, whether with print(df) or df.show(), prints every byte of blob columns such as images and video.
Exclude blob columns with filter_contents() or select() before printing.
The returned object is a datafusion.DataFrame. Rerun's query APIs heavily rely on DataFusion, which offers a rich set of data filtering, manipulation, and conversion tools.
When calling reader(), an index column must be specified. It can be any of the recording's timelines. Each row of the view will correspond to a unique value of the index column. It is also possible to query the dataset using index=None. In this case, only the static=True data will be returned.
log_time is added automatically by rr.log() and records when each log call was made.
send_columns() does not add it, so querying that data with index="log_time" returns no rows.
By default, when performing a query on a dataset, data for all its segments is returned. An additional "rerun_segment_id" column is added to the dataframe to indicate which segment each row belongs to.
An often used parameter of the reader() method is fill_latest_at=True. When used, all null data will be filled with a latest-at value, similarly to how the viewer works.
With fill_latest_at=True, the result also has a row at each timestamp of the other entities in the view, filled with the latest values.
Those entities are fetched even if you don't select their columns, so restrict the view with filter_contents().
See Where a query runs for what narrows a query.
Querying a subset of a dataset
In general, datasets can be arbitrarily large, and it is often useful to query only a subset of it. This is achieved using DatasetSelection objects:
# Filter by entity paths
selection = dataset.filter_contents(["/world/robot/**", "/sensors/**"])
# Filter by segment IDs (recording IDs)
selection = dataset.filter_segments(["recording_001", "recording_002"])
# Chain filters
selection = dataset.filter_contents(["/world/**"]).filter_segments(["recording_001"])filter_contents() takes entity path filter expressions.
DatasetSelection instances have the exact same reader() method as the original dataset:
df = selection.reader(index="frame_nr")
print(df)Filtering with DataFusion
DataFusion offers a rich set of filtering, projection, and joining capabilities. Check the DataFusion Python documentation for details.
For illustration, here are a few simple examples:
from datafusion import col
df = dataset.reader(index="frame_nr")
# Filter by index range
df = df.filter(col("frame_nr") >= 0).filter(col("frame_nr") <= 100)
# Filter by column not null
df = df.filter(col("/world/robot:Position3D:positions").is_not_null())
# Select specific columns
df = df.select("frame_nr", "/world/robot:Position3D:positions")Querying with SQL
The dataframes returned by reader() belong to the client's DataFusion session, client.ctx.
To query one with SQL, register it as a view:
ctx = client.ctx
ctx.register_view("robot", dataset.filter_contents("/robot/**").reader(index="frame_nr"))
df = ctx.sql('SELECT rerun_segment_id, frame_nr, "/robot/gripper/force:Scalars:scalars"[1] AS force FROM robot')Column names must be double-quoted.
Component columns are lists, and SQL list indices start at 1.
Catalog tables can be queried by name without registering them, e.g. SELECT * FROM "my_table".
Converting to other formats
Likewise, DataFusion offers a rich set of tools to convert a dataframe to various formats. Converting executes the query and loads the full result into memory. Filter and aggregate in DataFusion first, so that only the rows you need are converted. See Common Dataframe Operations for aggregation examples.
Load data to a PyArrow Table
import rerun as rr
with rr.server.Server(datasets={"my_dataset": ["recording.rrd"]}) as server:
dataset = server.client().get_dataset("my_dataset")
table = dataset.reader(index="frame_nr").to_arrow_table()Load data to a Pandas dataframe
import rerun as rr
with rr.server.Server(datasets={"my_dataset": ["recording.rrd"]}) as server:
dataset = server.client().get_dataset("my_dataset")
df = dataset.reader(index="frame_nr").to_pandas()Load data to a Polars dataframe
import rerun as rr
import polars as pl
with rr.server.Server(datasets={"my_dataset": ["recording.rrd"]}) as server:
dataset = server.client().get_dataset("my_dataset")
df = pl.from_arrow(dataset.reader(index="frame_nr").to_arrow_table())Load data to a DuckDB relation
import rerun as rr
import duckdb
with rr.server.Server(datasets={"my_dataset": ["recording.rrd"]}) as server:
dataset = server.client().get_dataset("my_dataset")
table = dataset.reader(index="frame_nr").to_arrow_table()
rel = duckdb.arrow(table)