> ## 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.

# Upload a model

> Register a battery model with Ionworks — a built-in PyBaMM model or your own custom one — then bind it to parameters as a parameterized model.

Register a model so you can parameterize and simulate it. A **model** is the structural definition (SPM, DFN, or your own equations); a [**parameterized model**](/build/parameterized-models) binds a model to a cell spec and parameter values — that's the runnable thing.

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

```python theme={null}
import pybamm
from ionworks import Ionworks

client = Ionworks()

# Option A — a built-in PyBaMM model. Pass the object directly (or a
# {"type": "SPM"} config dict); it's stored as a standard model + options.
model = client.model.create({
    "name": "SPM with SEI",
    "description": "SPM + SEI growth submodel",
    "config": pybamm.lithium_ion.SPM(options={"SEI": "ec reaction limited"}),
})

# Option B — your own pybamm.BaseModel subclass (new equations). Uploaded
# as a custom model.
custom_model = client.model.upload_custom(
    MyCustomModel(),             # a pybamm.BaseModel instance (or serialized-JSON path)
    name="My Custom Model",
    chemistry="lithium_ion",     # lithium_ion (default) | lithium_sulfur | ecm | generic
)

# Bind a model to a cell spec + parameter values to get a runnable parameterized model.
parameterized_model = client.parameterized_model.create_or_get("your-cell-spec-id", {
    "name": "LGM50 Chen2020",
    "model_id": model.id,
    "parameters": {...},
})
```

**What's happening**

* **Built-in vs custom.** `client.model.create` takes a **built-in** PyBaMM model — pass the object (`pybamm.lithium_ion.SPM(options=...)`) or a `{"type": "SPM"}` config dict — and stores it as a standard model with its options (`is_custom_model=False`). Reach for `client.model.upload_custom` only for a model class that isn't built in — your own `pybamm.BaseModel` subclass with new equations/submodels — which is stored as a custom model (`is_custom_model=True`).
* Custom uploads need a `chemistry` tag up front — it drives simulation fix-ups and can't be reliably inferred (a Li-S model without it fails at simulation time).
* A `Model` holds no parameter values and can't be simulated alone. Create a `ParameterizedModel` (against a `cell_spec_id`) to make it runnable — see [Run a simulation in a study](/quickstarts/run-in-study).

## Learn more

* [Models](/build/models) and [Parameterized models](/build/parameterized-models)
* [Projects & studies](/core-concepts/projects-studies)
