List, filter, paginate, and retrieve cell specs, instances, and measurements with the Python API
Once data is uploaded, you can read it back using the ionworks-api Python
client. This page covers listing and filtering resources, retrieving full
measurement detail, local caching, in-Python plotting, and error handling.For installation and authentication, see the
Python API client page. For uploading, see
uploading data.
You can find the ID for any cell specification, instance, or measurement
from the data visualization pages in Ionworks Studio. The ID is displayed
in the URL and in the detail panels.
# List cell specifications (first page)specs = client.cell_spec.list()for spec in specs[:5]: print(f" - {spec.name} (form_factor: {spec.form_factor})")# Get a specific cell spec with full nested datafull_spec = client.cell_spec.get(spec_id)print(f"Capacity: {full_spec.ratings['capacity']['value']} " f"{full_spec.ratings['capacity']['unit']}")# List instances for a spec and pick the firstinstances = client.cell_instance.list(spec_id)instance = instances[0]# List measurements for an instancemeasurements = client.cell_measurement.list(instance.id)
Filter by measurement type ("time_series", "properties", "file").
cell_measurement only
created_by_email
str
Case-insensitive substring match on creator email.
All
created_after
str
ISO datetime; records created after this time.
All
created_before
str
ISO datetime; records created before this time.
All
updated_after
str
ISO datetime; records updated after this time.
All
updated_before
str
ISO datetime; records updated before this time.
All
started_after
str
ISO datetime; measurements started after this time.
cell_measurement only
started_before
str
ISO datetime; measurements started before this time.
cell_measurement only
order_by
str
Column to sort by ("name", "created_at", "updated_at", or "start_time" for measurements).
All
order
str
Sort direction: "asc" or "desc".
All
Filter parameters can be combined freely with each other and with the
limit/offset pagination parameters. The .total property on the
returned PaginatedList reflects the total count after filters are
applied.
Use client.urls.measurement() to build a link to a measurement’s detail
page in the Ionworks web app. This is useful when you want to surface a
clickable link from a notebook, script, or report so collaborators can jump
straight to the measurement in Ionworks Studio.
The helper runs entirely locally — no network call. The web-app host is
derived from the client’s api_url so the link points at the same environment
(api.ionworks.com → app.ionworks.com). Set IONWORKS_APP_URL to override,
e.g. for local development where the frontend runs on a different port.A common pattern is to render a link next to each result while iterating
through measurements:
for m in client.cell_measurement.list(instance_id): print(f"{m.name}: {client.urls.measurement(m.id, project_id)}")
Navigator is an opt-in helper that walks the spec → instance → measurement
hierarchy and memoises every list and fetch call in memory. Use it when you
want to iterate over many specs, instances, or measurements in a single
script or notebook and avoid repeating the same API calls.Reach for Navigator when:
You’re writing an analysis script that loops over every measurement on one
or more cell specs.
You want deterministic iteration order — listings are returned sorted by
name.
You want pagination handled automatically without managing limit and
offset yourself.
The underlying sub-clients (client.cell_spec, client.cell_instance,
client.cell_measurement) remain the primary API. Navigator is a thin
layer on top — use it when you want a single cached view of the hierarchy,
and use the sub-clients directly for one-off reads or writes.
from ionworks import Ionworks, Navigatornav = Navigator(Ionworks())# Walk every measurement on every instance of every specfor spec_name in nav.specs(): for inst in nav.instances(spec_name): for m in nav.measurements(inst.id): ts = nav.time_series(m.id) steps = nav.steps(m.id) # ... your analysis ...
Each entity is fetched at most once per Navigator instance. Calling
nav.instances("CellA") twice returns the same list without a second API
round-trip; the same applies to measurements, steps, and time_series.
Battery data is immutable once uploaded, so the only staleness mode is “a
new sibling appeared on the platform.” For long-running notebooks where new
data may have been uploaded mid-session, you can drop part or all of the
cache:
nav.clear() # drop everythingnav.invalidate(spec_name="CellA") # drop CellA + its instances + measurementsnav.invalidate(instance_id="inst_123") # drop one instance + its measurementsnav.invalidate(measurement_id="meas_456") # drop one measurement's steps + time series
Invalidation cascades downward: dropping a spec also drops its instances and
their measurements; dropping an instance also drops its measurements.
Navigator caches in memory for the lifetime of the instance. For
cross-process or cross-session caching of measurement data on disk, see
Local caching below — the two layers compose.
The ionworks-api client automatically caches measurement data to disk so
repeated reads are fast and avoid unnecessary API calls. Caching is enabled
by default and applies to the steps, cycles, steps_and_cycles, and
time_series methods on cell_measurement.When you call a method like client.cell_measurement.steps(measurement_id),
the client checks a local cache directory before making an API request. If a
cached copy exists and hasn’t expired, it’s returned directly. Otherwise, the
client fetches from the API, caches the result, and returns it.Cached data is stored as Parquet files in ~/.ionworksdata_cache by default
and expires after one hour.
Every data-fetching method accepts a use_cache parameter. Set it to False
to force a fresh API call without reading from or writing to the local cache:
DataLoader includes a plot_data() method for quick matplotlib-based
visualization of measurement data. The plot displays voltage and current
over time, with an additional temperature subplot when temperature data is
available.
from ionworksdata import DataLoaderloader = DataLoader.from_db("measurement-id-here")fig, ax = loader.plot_data()
The method returns a matplotlib (Figure, Axes) tuple so you can customize
the plot further. Pass show=True to display the plot immediately:
fig, ax = loader.plot_data(show=True)
plot_data() automatically loads time series data from the server if it
hasn’t been fetched yet.
For the interactive in-browser viewer (with filters, step overlays, SQL), see
visualizing data.
When you pass a pandas or polars DataFrame directly in an API call (for
example, as part of a pipeline configuration), the client enforces a maximum
of 1,000 rows for inline time series data. Larger datasets should be
uploaded as measurements first, then referenced by ID.
from ionworks import MeasurementValidationError, IonworksErrortry: client.pipeline.run(config_with_large_inline_df)except MeasurementValidationError as e: # Specifically handle the size limit violation print(e) # "Time series has 5000 rows, which exceeds the maximum of 1000 rows # for inline data. Upload the data as a measurement using # client.cell_measurement.create() and reference it with # 'db:<measurement_id>' or iwdata.DataLoader.from_db(MEASUREMENT_ID) # instead."except IonworksError as e: print(e)
To work with larger datasets, upload first and reference by ID:
If you have a DataLoader that references a database measurement and you
want to export a self-contained configuration (for example, to share with a
colleague), use to_local() to embed the data inline:
from ionworksdata import DataLoaderloader = DataLoader.from_db("measurement-id-here")local_loader = loader.to_local()# Now to_config() returns the full data instead of a DB referenceconfig = local_loader.to_config()
to_local() fetches all time series and step data from the server
immediately. For very large measurements, this may take a moment.