Library Examples

Archiving information

from datetime import datetime, timedelta

import polars as pl
from pytz import UTC

from epicsarchiver import ArchiverAppliance

%matplotlib inline
Matplotlib is building the font cache; this may take a moment.
archiver = ArchiverAppliance("archiver.example.org")
pv = "EXAMPLE:TEMPERATURE"

Getting Data

_, events = archiver.get_events(pv, datetime.now(tz=UTC) - timedelta(seconds=1), datetime.now(tz=UTC))
events
[ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.0, secondsintoyear=8640000, year=2026, nanos=0, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.194709171154326, secondsintoyear=8640010, year=2026, nanos=841470984, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.35867804544976, secondsintoyear=8640020, year=2026, nanos=909297426, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.466019542983613, secondsintoyear=8640030, year=2026, nanos=141120008, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.499786801520752, secondsintoyear=8640040, year=2026, nanos=756802495, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.45464871341284, secondsintoyear=8640050, year=2026, nanos=958924274, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.337731590275574, secondsintoyear=8640060, year=2026, nanos=279415498, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.167494075077954, secondsintoyear=8640070, year=2026, nanos=656986598, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.97081292828621, secondsintoyear=8640080, year=2026, nanos=989358246, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.778739778352573, secondsintoyear=8640090, year=2026, nanos=412118485, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.621598752346035, secondsintoyear=8640100, year=2026, nanos=544021110, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.52419896305524, secondsintoyear=8640110, year=2026, nanos=999990206, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.50191769558208, secondsintoyear=8640120, year=2026, nanos=536572918, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.558272672139925, secondsintoyear=8640130, year=2026, nanos=420167036, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.68436668106384, secondsintoyear=8640140, year=2026, nanos=990607355, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.860292250900535, secondsintoyear=8640150, year=2026, nanos=650287840, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.058274602425247, secondsintoyear=8640160, year=2026, nanos=287903316, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.247056675569304, secondsintoyear=8640170, year=2026, nanos=961397491, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.396833931924576, secondsintoyear=8640180, year=2026, nanos=750987246, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.483959836015742, secondsintoyear=8640190, year=2026, nanos=149877209, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.49467912331169, secondsintoyear=8640200, year=2026, nanos=912945250, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.42729945404414, secondsintoyear=8640210, year=2026, nanos=836655638, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.29245859644588, secondsintoyear=8640220, year=2026, nanos=8851309, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.11144495705012, secondsintoyear=8640230, year=2026, nanos=846220404, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.91283660938851, secondsintoyear=8640240, year=2026, nanos=905578362, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.727989444555316, secondsintoyear=8640250, year=2026, nanos=132351750, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.586086765457175, secondsintoyear=8640260, year=2026, nanos=762558450, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.509531884966755, secondsintoyear=8640270, year=2026, nanos=956375928, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.51041113542434, secondsintoyear=8640280, year=2026, nanos=270905788, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
 ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.588585702515648, secondsintoyear=8640290, year=2026, nanos=663633884, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')])]
df = archiver.get_data(pv, datetime.now(tz=UTC) - timedelta(seconds=30), datetime.now(tz=UTC))
df.head()
shape: (5, 7)
datepvvalseveritystatusfield_valuesheaders
datetime[ns, UTC]strf64i32i32list[struct[2]]list[struct[2]]
2026-04-11 00:00:00 UTC"EXAMPLE:TEMPERATURE"28.014[{"EGU","degC"}, {"PREC","2"}][]
2026-04-11 00:00:10.841470984 UTC"EXAMPLE:TEMPERATURE"28.19470914[{"EGU","degC"}, {"PREC","2"}][]
2026-04-11 00:00:20.909297426 UTC"EXAMPLE:TEMPERATURE"28.35867814[{"EGU","degC"}, {"PREC","2"}][]
2026-04-11 00:00:30.141120008 UTC"EXAMPLE:TEMPERATURE"28.4660214[{"EGU","degC"}, {"PREC","2"}][]
2026-04-11 00:00:40.756802495 UTC"EXAMPLE:TEMPERATURE"28.49978714[{"EGU","degC"}, {"PREC","2"}][]

Async Fetch Data

from epicsarchiver.retrieval.client.async_archiver_retrieval import AsyncArchiverRetrieval
from pytz import timezone
tz = timezone("Europe/Stockholm")
async with AsyncArchiverRetrieval(archiver.hostname) as a_archiver:
    print(await a_archiver.get_events(pv, datetime.now(tz=tz) - timedelta(microseconds=100), datetime.now(tz=tz)))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[9], line 2
      1 async with AsyncArchiverRetrieval(archiver.hostname) as a_archiver:
----> 2     print(await a_archiver.get_events(pv, datetime.now(tz=tz) - timedelta(microseconds=100), datetime.now(tz=tz)))

File ~/checkouts/readthedocs.org/user_builds/epicsarchiver-retrieval-client/envs/latest/lib/python3.12/site-packages/epicsarchiver/retrieval/client/async_archiver_retrieval.py:208, in AsyncArchiverRetrieval.get_events(self, pv, start, end, processor)
    195 """Get a list of events from the archiver for specified pv and time period.
    196 
    197 Args:
   (...)    205     list[ArchiveEvent]: List of events in time period.
    206 """
    207 # http://slacmshankar.github.io/epicsarchiver_docs/userguide.html
--> 208 metadata, events = await self.get_archive_data(pv, start, end, processor)
    209 LOG.debug("Metadata: %s", metadata)
    210 return events

File ~/checkouts/readthedocs.org/user_builds/epicsarchiver-retrieval-client/envs/latest/lib/python3.12/site-packages/epicsarchiver/retrieval/client/async_archiver_retrieval.py:236, in AsyncArchiverRetrieval.get_archive_data(self, pv, start, end, processor)
    234 # http://slacmshankar.github.io/epicsarchiver_docs/userguide.html
    235 pv_request = processor.calc_pv_name(pv) if processor else pv
--> 236 r = await self.get_data_raw(pv_request, start, end)
    237 pb_data = await r.content.read()
    238 return parse_pb_data(pb_data)

File ~/checkouts/readthedocs.org/user_builds/epicsarchiver-retrieval-client/envs/latest/lib/python3.12/site-packages/epicsarchiver/retrieval/client/async_archiver_retrieval.py:119, in AsyncArchiverRetrieval.get_data_raw(self, pv, start, end, fetch_latest_metadata)
    117 if fetch_latest_metadata:
    118     params["fetchLatestMetadata"] = "true"
--> 119 return await self._get(self.data_url, params=params)

File ~/checkouts/readthedocs.org/user_builds/epicsarchiver-retrieval-client/envs/latest/lib/python3.12/site-packages/epicsarchiver/common/async_service.py:92, in ServiceClient._get(self, endpoint, params)
     90 LOG.debug("GET url: %s", url)
     91 try:
---> 92     return await self.session.get(
     93         url, params=params, raise_for_status=True, ssl=False
     94     )
     95 except ClientConnectionError as e:
     96     raise ArchiverConnectionError(
     97         base_url=self.base_url,
     98     ) from e

File ~/.asdf/installs/python/3.12.12/lib/python3.12/unittest/mock.py:2302, in AsyncMockMixin._execute_mock_call(self, *args, **kwargs)
   2300         raise result
   2301 elif iscoroutinefunction(effect):
-> 2302     result = await effect(*args, **kwargs)
   2303 else:
   2304     result = effect(*args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/epicsarchiver-retrieval-client/envs/latest/lib/python3.12/site-packages/aioresponses/core.py:531, in aioresponses._request_mock(self, orig_self, method, url, *args, **kwargs)
    528 request_call = self._build_request_call(method, *args, **kwargs)
    529 self.requests[key].append(request_call)
--> 531 response = await self.match(method, url, **kwargs)
    533 if response is None:
    534     if self.passthrough_unmatched:

File ~/checkouts/readthedocs.org/user_builds/epicsarchiver-retrieval-client/envs/latest/lib/python3.12/site-packages/aioresponses/core.py:461, in aioresponses.match(self, method, url, allow_redirects, **kwargs)
    459 for key, matcher in self._matches.items():
    460     if matcher.match(method, url):
--> 461         response_or_exc = await matcher.build_response(
    462             url, allow_redirects=allow_redirects, **kwargs
    463         )
    464         break
    465 else:

File ~/checkouts/readthedocs.org/user_builds/epicsarchiver-retrieval-client/envs/latest/lib/python3.12/site-packages/aioresponses/core.py:203, in RequestMatch.build_response(self, url, **kwargs)
    200     return self.exception
    202 result = self if result is None else result
--> 203 resp = self._build_response(
    204     url=url,
    205     method=result.method,
    206     request_headers=kwargs.get("headers"),
    207     status=result.status,
    208     body=result.body,
    209     content_type=result.content_type,
    210     payload=result.payload,
    211     headers=result.headers,
    212     response_class=result.response_class,
    213     reason=result.reason)
    214 return resp

File ~/checkouts/readthedocs.org/user_builds/epicsarchiver-retrieval-client/envs/latest/lib/python3.12/site-packages/aioresponses/core.py:172, in RequestMatch._build_response(self, url, method, request_headers, status, body, content_type, payload, headers, response_class, reason)
    170     _headers.update(headers)
    171 raw_headers = self._build_raw_headers(_headers)
--> 172 resp = response_class(method, url, **kwargs)
    174 for hdr in _headers.getall(hdrs.SET_COOKIE, ()):
    175     resp.cookies.load(hdr)

TypeError: ClientResponse.__init__() missing 1 required keyword-only argument: 'stream_writer'
async with AsyncArchiverRetrieval(archiver.hostname) as a_archiver:
    print(await a_archiver.get_all_events([pv, "EXAMPLE:TEMPERATURE2", "EXAMPLE:TEMPERATURE3"], datetime.now(tz=tz) - timedelta(microseconds=100), datetime.now(tz=tz)))

Displaying and Calculating Summaries

import matplotlib.pyplot as plt

plt.plot(df["date"], df["val"])
plt.xlabel("time")
plt.ylabel("val")
plt.tight_layout()
# NBVAL_IGNORE_OUTPUT
from epicsarchiver.retrieval.client.processor import Processor, ProcessorName
df_mean = archiver.get_data(
    pv,
    datetime.now(tz=UTC) - timedelta(seconds=6000),
    datetime.now(tz=UTC),
    Processor(ProcessorName.MEAN, 20),
)
plt.plot(df_mean["date"], df_mean["val"])
plt.xlabel("time")
plt.ylabel("val (mean, 20s bins)")
plt.tight_layout()
# NBVAL_IGNORE_OUTPUT

Raw PB Response

For full control you can fetch the raw protobuf response with get_data_raw and decode it yourself with parse_pb_data. This is the building block used by get_events and get_data.

from epicsarchiver.retrieval.pb import parse_pb_data

# get_data_raw returns the underlying HTTP response; its body is the raw
# Archiver Appliance PB byte stream, which parse_pb_data turns into events.
response = archiver.get_data_raw(
    pv, datetime.now(tz=UTC) - timedelta(seconds=30), datetime.now(tz=UTC)
)
meta, raw_events = parse_pb_data(response.content)
meta

Searching for PV Names

search returns the PV names matching a regex pattern. Optionally restrict the results to PVs that recorded data in a time range with start/end, and cap the number of results with limit.

# Optionally pass start/end to filter by time range and limit to cap results.
archiver.search("EXAMPLE:.*", limit=10)

Exporting Events to Other Formats

write_events serialises a list of events to a binary stream in one of the Format options used by the export command: JSON, CSV, ARROW, or PARQUET.

import io

from epicsarchiver.write.export_format import Format, write_events

# Write the events to an in-memory buffer as CSV. Format also supports
# JSON, ARROW, and PARQUET. These formats require the [polars] extra.
buffer = io.BytesIO()
write_events(buffer, Format.CSV, events=raw_events, meta=meta)
print("\n".join(buffer.getvalue().decode().splitlines()[:4]))

Reading a Local PB File

read_pb_file parses an Archiver Appliance .pb file from disk into the same (metadata, events) tuple returned by parse_pb_data, without contacting a server.

from pathlib import Path

from epicsarchiver.retrieval.pb import read_pb_file

# Persist the raw response to a local .pb file, then read it back.
_pb_path = Path("example.pb")
_pb_path.write_bytes(response.content)
file_meta, file_events = read_pb_file(str(_pb_path))
_pb_path.unlink()  # clean up the temporary file
file_events[:3]