# Assets

An asset represents a system you test, such as a vehicle, satellite or test stand.
It collects that system’s datasets, runs, events and attachments across every test.

## Connect the client

This guide assumes you have [installed](https://dev.nominal.io/python/guides/install.md) the client and setup your [API keys](https://dev.nominal.io/python/guides/api-keys-setup.md).

```python
from nominal.core import NominalClient

client = NominalClient.from_profile("default")
```

## Create or find an asset

Properties identify the physical system, such as a serial number.
[`get_or_create_asset_by_properties()`](https://dev.nominal.io/python/ref/generated/nominal.core.NominalClient.md#nominal.core.NominalClient.get_or_create_asset_by_properties "nominal.core.NominalClient.get_or_create_asset_by_properties") returns the asset with those properties, creating it if none exists, so a script can run more than once:

```python
asset = client.get_or_create_asset_by_properties(
    {"serial": "QC-456"},
    name="Quadcopter 456",
    description="Airborne observation platform",
    labels=["camera", "airborne"],
)
```

It raises `ValueError` if more than one asset has those properties.
To always create a new asset, use [`create_asset()`](https://dev.nominal.io/python/ref/generated/nominal.core.NominalClient.md#nominal.core.NominalClient.create_asset "nominal.core.NominalClient.create_asset").

## Add data with data scopes

An asset holds each dataset under a data scope name, such as `"telemetry"`.
Use the same data scope name for the same kind of data on every asset, so workbook templates and checklists work across them.

[`get_or_create_dataset()`](https://dev.nominal.io/python/ref/generated/nominal.core.Asset.md#nominal.core.Asset.get_or_create_dataset "nominal.core.Asset.get_or_create_dataset") returns the dataset under a data scope name, creating it if needed.

The examples upload `flight.csv`. To run them as written, save this sample under that name:

```text
time,altitude,temperature,tail_number
2026-09-10T14:38:19Z,100.0,20.0,N456QC
2026-09-10T14:38:20Z,101.5,20.2,N456QC
2026-09-10T14:38:21Z,103.0,20.4,N456QC
```

```python
dataset = asset.get_or_create_dataset("telemetry", name="Quadcopter 456 telemetry")
file = dataset.add_tabular_data("flight.csv", timestamp_column="time", timestamp_type="iso_8601")
file.poll_until_ingestion_completed()  # raises NominalIngestError if the ingest fails
```

Running the upload again adds another file to the same dataset.
See [Datasets](https://dev.nominal.io/python/guides/datasets/overview.md) for file formats and waiting for ingestion.

To attach a dataset that already exists, use [`add_dataset()`](https://dev.nominal.io/python/ref/generated/nominal.core.Asset.md#nominal.core.Asset.add_dataset "nominal.core.Asset.add_dataset"):

```python
asset.add_dataset("ground-station", client.get_dataset("<DATASET_RID>"))
```

[`remove_data_scopes()`](https://dev.nominal.io/python/ref/generated/nominal.core.Asset.md#nominal.core.Asset.remove_data_scopes "nominal.core.Asset.remove_data_scopes") detaches data by data scope name or by dataset. It doesn’t delete the dataset.

```python
asset.remove_data_scopes(names=["ground-station"])
```

## Find assets

[`search_assets()`](https://dev.nominal.io/python/ref/generated/nominal.core.NominalClient.md#nominal.core.NominalClient.search_assets "nominal.core.NominalClient.search_assets") combines its filters with AND:

```python
assets = client.search_assets(labels=["airborne"], properties={"serial": "QC-456"})
```

`search_text` is a fuzzy match, so results may not contain the text verbatim.
Use `exact_substring` when they must.

To retrieve one asset, use its RID from the asset’s page in Nominal:

```python
asset = client.get_asset("<ASSET_RID>")
```

## Update metadata and attachments

[`update()`](https://dev.nominal.io/python/ref/generated/nominal.core.Asset.md#nominal.core.Asset.update "nominal.core.Asset.update") replaces each field you pass and leaves the others unchanged.
To add a label, merge it with the existing ones:

```python
asset.update(labels=[*asset.labels, "new-motor-vendor"])
```

Attach files that describe the system across tests, such as a specification:

```python
asset.add_attachments([client.create_attachment("spec.pdf")])
```

Attach files about a single test to its [run](https://dev.nominal.io/python/guides/runs.md) instead.

## Archive an asset

[`archive()`](https://dev.nominal.io/python/ref/generated/nominal.core.Asset.md#nominal.core.Asset.archive "nominal.core.Asset.archive") hides the asset from views and searches without deleting its data.
[`unarchive()`](https://dev.nominal.io/python/ref/generated/nominal.core.Asset.md#nominal.core.Asset.unarchive "nominal.core.Asset.unarchive") restores it.

```python
asset.archive()
```
