> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ionworks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Process & upload cycler data

> Convert a raw cycler export into the Ionworks standardized format and upload it as a time-series measurement with the Python SDK.

Turn a raw cycler export (Arbin, Maccor, Neware, Basytec, BioLogic, …) into the Ionworks standardized format and upload it as a measurement — all from Python.

<Note>
  Install and authenticate first: `pip install ionworks-api` and set `IONWORKS_API_KEY` (and `IONWORKS_PROJECT_ID`). See the [Python API client](/api-client) page.
</Note>

```python theme={null}
from ionworks import Ionworks
from ionworksdata import read

client = Ionworks()

# 1. Process the raw file into two standardized frames:
#    - time_series: one row per logged time point (Time [s], Voltage [V],
#      Current [A], Step count, ...)
#    - steps: one row per protocol step, with per-step summaries
#      (step/cycle indices, capacity, energy, min/max voltage, ...)
time_series, steps = read.time_series_and_steps("arbin_export.csv", reader="arbin")

# 2. Create-or-get the cell hierarchy: specification -> instance.
cell_spec = client.cell_spec.create_or_get({"name": "LGM50"})
cell_instance = client.cell_instance.create_or_get(cell_spec.id, {"name": "SN-001"})

# 3. Upload as a time-series measurement (idempotent; strict validation on).
measurement = client.cell_measurement.create_or_get(
    cell_instance.id,
    {
        "measurement": {"name": "cell_SN001_cycling"},
        "time_series": time_series,
        "steps": steps,
    },
    validate_strict=True,
)
print(measurement.id)
```

**What's happening**

* **`time_series` vs `steps`.** `time_series` is the raw signal trace — one row per logged time point. `steps` is a compact per-step summary — one row per protocol step — carrying step/cycle indices and derived quantities (capacity, energy, voltage extremes) that `read.time_series_and_steps` computes as it parses. Steps are what most analyses group and filter on.
* `ionworksdata.read` has readers for the common cyclers; `read.detect(path)` auto-picks one. Prefer `read.time_series_and_steps` — it returns both frames and runs the full processing pipeline in one call.
* **Current sign convention:** Ionworks treats **positive current as discharge**. Different cyclers use different conventions, so check your source and flip the sign if needed (`ionworksdata` provides `set_positive_current_for_discharge`) before uploading.
* `create_or_get` is idempotent — re-running returns the existing spec/instance/measurement instead of duplicating it. A returned object (not the call merely completing) is the proof of success.

## Learn more

* [Preparing data](/data/preparing-data) and [Reading cycler files](/data/reading)
* [Uploading measurements](/data/uploading) and [Raw data](/data/raw-data)
* [Standardized data format](/data/format)
