0.60

ReferenceClient librariesRead data

Read data

Read stored samples with the Python and TypeScript clients: a time range, the latest samples, fewer samples, CSV and TDMS exports, and large time ranges with an iterator.

Read the samples that a Core stores. A read loads all of its samples into memory. For a time range that is too large for memory, use an iterator. To get samples as they arrive, stream data.

Read a time range

Give read a time range and one or more channels. The time range includes its start and excludes its end, as Time types shows.

Enterprise
from datetime import datetime, timezone

import synnax as sy

tr = sy.TimeRange(
    datetime(2026, 10, 3, 12, 0, tzinfo=timezone.utc),
    datetime(2026, 10, 3, 14, 0, tzinfo=timezone.utc),
)

# One channel gives a MultiSeries.
temperature = client.read(tr, "temperature")
print(temperature.to_numpy())

# A list of channels gives a Frame.
frame = client.read(tr, ["time", "temperature", "pressure"])
# The columns are the channel names, in the order you request them. The index is the
# UTC timestamps of "time", which is not a column.
df = frame.to_df()

A channel that you retrieved also has read:

channel = client.channels.retrieve("temperature")
temperature = channel.read(tr)

A list of channels always gives a Frame, even a list of one channel. When a channel has no data in the time range, one channel gives an empty MultiSeries, and a list gives an empty series for that channel. In Python, a frame that you read by key also finds a channel by its name. To work with the result, see Series and frames.

Read the latest samples

Enterprise
# The last sample of one channel.
last = client.read_latest("temperature")

# The last 10 samples of each channel.
frame = client.read_latest(["temperature", "pressure"], n=10)

When a channel has fewer than n samples, the read returns the samples that exist.

Read fewer samples

A reduction tells the Core to send fewer samples. Use one to load a long time range for a plot or a quick check.

Reduction Keeps Accepted by
Stride Every factor-th sample of each channel. read, iterators, and exports
Limit About point_limit points for each channel. read and iterators
Enterprise
# Every 10th sample.
frame = client.read(
    tr, ["time", "temperature"], reduction=sy.StrideReduction(factor=10)
)

# About 2,000 points: the lowest and highest sample of each group.
frame = client.read(
    tr,
    ["time", "temperature"],
    reduction=sy.LimitReduction(aggregation="min_max", point_limit=2_000),
)

A limit splits each channel into groups. The aggregation sets the points that each group gives:

  • min_max -> the lowest and highest sample, in the order they occurred. The Python client uses it when you give no aggregation.
  • average -> the mean of the samples.
  • decimate -> the first sample.

A limit does not reduce string, JSON, UUID, or bytes channels.

Export to CSV

The Core writes the CSV file, so your code never holds the whole export in memory:

Enterprise
client.read_csv(tr, ["temperature", "pressure"], dest="tank.csv")

The file name must end in .csv.

The export adds the index channel of each channel that you give. The first line holds the channel names. Each index channel has a column, followed by a column for each channel that it timestamps. Each line holds the samples of one timestamp, in time order. A cell is empty where its channel has no sample at that timestamp. Lines end in \n.

To export fewer samples, give a stride as the reduction. An export does not take a limit. A channel with no index channel, such as a virtual channel, makes the export fail.

Export to TDMS

Export samples to an NI TDMS file:

Enterprise
client.read_tdms(tr, ["temperature", "pressure"], dest="tank.tdms")

The file name must end in .tdms.

The file has a group for each index channel. The group holds the index channel, then the float channels that it timestamps. A float channel holds NaN where it has no sample, and NI tools read NaN as no value. Each channel of a different data type gets a group of its own, with the timestamps of its samples, then the samples. Reductions and channels with no index work as in a CSV export.

Read large time ranges

An iterator reads a time range in chunks, so your code holds one chunk in memory at a time:

Enterprise
with client.open_iterator(tr, ["time", "temperature"]) as iterator:
    for frame in iterator:
        print(len(frame["temperature"]))

The with block closes the iterator, also when the code raises an error.

Each chunk holds up to 100,000 samples of each channel by default. Set the chunk size when you open the iterator:

Enterprise
iterator = client.open_iterator(tr, ["time", "temperature"], chunk_size=10_000)

Move the iterator yourself

A loop reads the time range from start to end. To move in other ways, call the methods below. Each returns false when no data is there. A seek only moves the iterator: call next or prev after it to read. They put the data that they read in iterator.value, a frame.

Python TypeScript Does
seek_first() seekFirst() Moves to the start of the time range’s data. Reads nothing.
seek_last() seekLast() Moves to the end of the time range’s data. Reads nothing.
seek_ge(stamp) seekGE(stamp) Moves to stamp, or the first data after it. Reads nothing.
seek_le(stamp) seekLE(stamp) Moves to stamp, or the last data before it. Reads nothing.
next(span) next(span) Reads forward by span.
prev(span) prev(span) Reads backward by span.
valid() valid() Returns true when value holds data.

With sy.AUTO_SPAN as the span, next and prev read one chunk. In TypeScript, the span is optional, and one chunk is the default.

Enterprise
with client.open_iterator(tr, ["time", "temperature"]) as iterator:
    iterator.seek_first()
    while iterator.next(sy.TimeSpan.MINUTE):
        print(iterator.value["temperature"].to_numpy().mean())

This code reads the time range one minute at a time. The loop stops at the first minute that has no data. To read past gaps, use sy.AUTO_SPAN in Python, or no span in TypeScript.

Iterator options

Enterprise
chunk_sizeint = 100000

The largest number of samples of each channel that one chunk reads. With a stride, the count is of the samples before the stride. With a limit, the count is of the points.

reductionReduction = None

Makes the Core send fewer samples, as Read fewer samples shows. With no reduction, the iterator keeps all samples.