Understanding units
The Units service in CDF consists of three key components:- Quantity: A specific, measurable property or characteristic of a physical system or substance. For example, temperature, pressure, mass, and length.
- Unit: A standard amount or value of a particular quantity by which other amounts of the same quantity can be measured or expressed. For example,
°Cand°Fare used for temperature, andbarandpsiare used for pressure. - Unit system: A collection of default units for given quantities. Examples include the
SIunit system (the default value isKfor temperature andPascalfor pressure) and theImperialunit system (the default value is°Ffor temperature andpsifor pressure).
Unit structure
Every CDF project comes with a list of standard industry-relevant units. Each unit contains:externalId: A unique identifier structured as{quantity}:{unit}(for example,temperature:deg_c).name: The primary name of the unit, such asDEG_C.longName: A descriptive name for the unit, likedegree Celsius.symbol: The symbol used for the unit, like°C.aliasNames: An array of alternative names or aliases for the unit.quantity: The physical quantity the unit measures likeTemperature.conversion: An object containing conversion factors (multiplierandoffset) for unit conversion.source: The primary source of the unit’s definition, typically a standard organization likequdt.org.sourceReference: A URL reference to the external source for the unit’s definition.
Accessing units
You can access units through two primary APIs:-
The Units API: The primary source of truth for all unit definitions in your CDF project. This API provides direct access to the unit catalog, where units are defined and managed.
-
The Data modeling API: This API stores a copy of each unit in the
cdf_cdm_unitsspace. These nodes are used to assign direct relations fromCogniteTimeSeriesto a valid unit in the catalog.
Integration with time series
Creating time series with units
CDF supports two methods for creating time series with units — by using the Data modeling API or the Time series API. The choice between these methods depends on whether you want your time series to be part of the knowledge graph. For both methods, you can:- Assign units during time series creation.
- Add or update units after the time series has been created.
- Change units at any time during the time series lifecycle.
- Using the Data modeling API
- Using the Time series API
Use this method when you want your time series to be part of the CDF knowledge graph. Note that the
unit field needs to be a direct relation reference to a unit from Cognite’s unit catalog stored in the cdf_cdm_units space.Different field names in the two APIs exist for historical reasons. Naming in the Time series API is maintained for backwards compatibility. Equivalent fields pair like this:Time series API (legacy naming):
unit: Free-text unit label from the source system (for example,"deg C")unitExternalId: Reference to a unit from the unit catalog (for example,"temperature:deg_c")
sourceUnit: Free-text unit label from the source system (for example,"deg C")unit: Direct relation reference to a unit from the catalog (for example,("cdf_cdm_units", "temperature:deg_c"))
Querying time series with units
Filtering time series
Once a time series is created (or updated) with units from the catalog, it’s possible to filter both onunitExternalId and unitQuantity when using the Time series API.
The example below shows valid time series queries in the Python SDK:
unit field, which is a direct relation reference to a unit from Cognite’s unit catalog stored in the cdf_cdm_units space.
The example below shows how to filter CogniteTimeSeries instances in the data modeling service (DMS) based on the direct relation reference to a unit.
unitExternalId and unitQuantity, so you can get counts of the time series according to units and quantities.
The example below shows valid time series aggregations in the Python SDK.
Retrieving data with unit conversion
For time series with associated units from the catalog, you can query data points in a compatible unit (units from the same quantity). The example below displays the data point queries with unit conversion in the Python SDK.Integration with data models
Creating data models with units
1
Define containers with units
You can associate float properties with specific units from the unit catalog within data models. This relationship occurs when you create or update a container.
The example below demonstrates creating a container with float properties assigned to a specific unit.Assigning
unit.externalId and unit.sourceUnit to a float-type container property is optional. While unit.externalId must correspond to an entry in the unit catalog, unit.sourceUnit is a free text string where users can store the original name or alias used for the unit in the source system.2
Create views
With the container in place, create or update a view that maps the container properties you want to expose. The Python example below builds The view definition doesn’t contain unit details. The unit information is stored in the container definition and automatically inherited by the view.
PumpSpecificationsView from PumpSpecificationsContainer:3
Create data model
You use data models to organize collections of views. The example below shows how to create a data model using the Python SDK:
The
@unit directive associates a property with a specific unit. This works precisely like assigning units using DMS; the externalId argument must correspond to an entry in the unit catalog.Querying data models with units
Using the Data modeling API
When querying data, setincludeTyping to true to retrieve information about the units associated with all properties included in the response.
To add unit conversion into a response, specify either unit.externalId or unit.unitSystemName for each property requiring conversion within the targetUnits argument. When you set includeTyping to true, the typing information will reflect the units of the data included in the request. For example, if maxPressure was stored as pressure:bar, a request to convert to pressure:pa will result in both the data and typing information being in pressure:pa.
When applying a filter to a property included in targetUnits, the filter will be unit-aware, ensuring that the value passed on the filter aligns with the unit of the data. For instance, if maxPressure was stored as pressure:bar and a request to convert to pressure:pa with a filter greater than or equal to 3520000.0 will filter the data to instances where maxPressure is greater than or equal to 3520000.0 Pascal.
All the data modeling query endpoints support typing information, unit conversion, and unit-aware filtering.
The following sections show examples of valid queries.
List instances with unit conversion and unit-aware filtering
The example below shows a list instance with unit conversion and unit-aware filtering.Query instances with unit conversion and unit-aware filtering
The example below shows a query instance with unit conversion and unit-aware filtering.Aggregate data across nodes/edges with unit conversion and unit-aware filtering
The example below shows aggregate data across nodes/edges with unit conversion and unit-aware filtering.Using GraphQL
Apart from querying data using the DMS endpoints, use GraphQL to fetch data from views and time series data points. The GraphQL API supports unit conversion and unit-aware filtering. A query can settargetUnit or targetUnitSystem on at most 10 fields. If you exceed that limit, the request fails with targetUnits size must be between 1 and 10. See Limits and restrictions.
The following examples include valid queries.
List query with unit conversion and unit-aware filtering
The example below shows a list query with unit conversion and unit-aware filtering.Aggregate query with unit conversion and filtering
The example below shows an aggregate query with unit conversion and filtering.Time series data points query with unit conversion
The example below shows time series data points query with unit conversion.You need to define the variables
startTime and endTime before executing the query.Integration with records
Records store high-volume industrial data using the same container schemas as data modeling. You assign units to float properties when you create the container (usingused_for="record"), and the Records API converts values at query time when you request a different unit.
Creating record containers with units
Record containers use the same float-with-unit syntax as data modeling containers —Float64(unit=UnitReference(...)) for 64-bit values and Float32(unit=UnitReference(...)) for 32-bit values. Set used_for="record" to mark the container for use with the Records service. Unit assignment works for both scalar and list float properties.
unit.externalId must match an entry in the CDF unit catalog. The source_unit field is an optional free-text traceability field that captures the original unit name as it appeared in the source system. See Containers, views, polymorphism, data models for more information on container schemas.
Normalizing data before ingestion
Because the Records service does not convert values on write, you must normalize data to the container unit before or during ingestion. Two common patterns: In the extractor — convert source-unit values to the container unit as part of the extraction pipeline before writing. This is the simplest approach when you control the extractor. Staging streams — ingest raw data in source units into separate per-source staging containers (for example, apump_specifications_us container using pressure:psi and temperature:deg_f). A downstream workflow reads from the staging container using targetUnits to align units, then writes normalized values to the canonical production container. This pattern works well when you receive data from heterogeneous sources you don’t control. Because a property’s unit is fixed at container creation time, each source unit needs its own staging container (or its own property within a shared staging container) — you can’t write values in different units to the same property.
Querying records with units
Therecords/sync, records/filter, and records/aggregate endpoints accept a top-level targetUnits parameter that converts both response values and filter inputs to the requested units. You can target a unit system for all eligible properties at once, or specify per-property overrides.
Convert with a unit system
UseunitSystemName to convert all float properties that have a unit defined on the container to the default unit for that system in one step.
Convert per property
Use theproperties form to specify a different target unit or unit system for each property individually.
unit is a choice between { "externalId": "..." } (specific unit) and { "unitSystemName": "..." } (unit system default for that property’s quantity). You can mix both forms in the same request. The targetUnits.properties list accepts up to 1000 entries.
Filter in the target unit
When you settargetUnits, the same conversion applies to filter values. You write filter thresholds in the target unit, and the service converts them to the container unit before comparison. This bidirectionality is a core design principle: filter inputs and response outputs always share the same unit.
The example below filters pumps where maxPressure ≥ 3 520 000 Pa. The stored values are in bar, so the service converts 3 520 000 Pa to 35.2 bar before evaluating the filter — and returns the matching records with maxPressure converted back to Pa.
equals, range, in, not_in, and similar) are unit-aware when the filtered property is included in targetUnits. Properties not listed in targetUnits are compared in their stored container unit.
Aggregate in the target unit
The/records/aggregate endpoint applies the same targetUnits conversion to numeric aggregates. Functions such as avg, sum, min, and max operate on converted values; count is unit-independent and is unaffected. Filter values in the aggregate request are converted in the same way as for /records/filter — you write thresholds in the target unit and the service converts them to the container unit before evaluation.
The example below shows aggregating pressure and temperature across all records, requesting results in Pa and the SI unit system:
Inspect resolved units with includeTyping
Set includeTyping: true to include a typing block in the response. This block shows the schema and resolved unit for each property touched by the request. When targetUnits is set, the type.unit.externalId field in the typing block reflects the target unit, not the storage unit.
The service includes typing information for properties that are:
- returned in
items(for/filterand/syncresponses) - referenced in the request
filter - referenced in
targetUnits - referenced in
aggregates(for/aggregateresponses)
Unlike data modeling instances, the Records API returns typing information only for properties that appear in the current request or response, not for the full container schema. This keeps response sizes manageable for containers with many properties.
Paginate with /records/sync
When paginating with /records/sync, repeat targetUnits in every follow-up request. Cursors carry your position in the change stream but do not store conversion settings.
For more detailed information, see: