
## Managing data products

URL: https://docs.atlan.com/product/capabilities/build-apps/sdks/python/data-mesh/how-tos/manage-data-products

> Create and manage data products in Atlan using the Python SDK (pyatlan) — use DataProduct to define asset collections and output ports.

# DataProduct: create and manage data products

Use `DataProduct` in the Atlan Python SDK to programmatically create and manage data products within your data mesh domains.

## Create new data product

To create a new data product:

### Java

```java showLineNumbers title="Create a data product"
FluentSearch assets = Table.select(client) // (1)
 .where(Table.CERTIFICATE_STATUS.eq(CertificateStatus.VERIFIED))
 .where(Table.ATLAN_TAGS.eq("Marketing"))
 .build();
DataProduct dp = DataProduct.creator("Marketing Influence", // (2)
 DataDomain.refByQualifiedName("default/domain/marketing"), // (3)
 assets) // (4)
 .build(); // (5)
AssetMutationResponse response = dp.save(client); // (6)
```

1. When defining a data product, you must define the assets within it. These are defined through a search, so that the assets included can be automatically managed. In this example, we're selecting all verified tables that have a tag of `Marketing`.
2. You must provide a human-readable name for your data product.
3. You must also provide the domain in which the data product should exist.
4. And finally the search that was defined earlier, to define which assets to include in the data product.
5. You then need to build the object.
6. And then you can `save()` the object you've built to create the new data product in Atlan. Because this operation will persist the asset in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

### Python

```python showLineNumbers title="Create a data product"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import DataProduct, Table
from pyatlan.model.fluent_search import CompoundQuery, FluentSearch
from pyatlan.model.enums import CertificateStatus

client = AtlanClient()

assets = (
 FluentSearch()
 .where(CompoundQuery.active_assets())
 .where(CompoundQuery.asset_type(Table))
 .where(Table.CERTIFICATE_STATUS.eq(CertificateStatus.VERIFIED.value))
 .where(Table.ATLAN_TAGS.eq("Marketing"))
).to_request() # (1)

product = DataProduct.creator(
 name="Marketing Influence", # (2)
 asset_selection=assets, # (3)
 domain_qualified_name="default/domain/marketing" # (4)
)

response = client.asset.save(product) # (5)
```

1. When defining a data product, you must define the assets within it. These are defined through a search, so that the assets included can be automatically managed. In this example, we're selecting all verified tables that have a tag of `Marketing`.
2. You must provide a human-readable name for your data product.
3. You must provide the index search request that was defined earlier, to define which assets to include in the data product.
4. You must also provide the domain in which the data product should exist.
5. And then you can `save()` the object you've built to create the new data product in Atlan.

### Kotlin

```kotlin showLineNumbers title="Create a data product"
val assets = Table.select(client) // (1)
 .where(Table.CERTIFICATE_STATUS.eq(CertificateStatus.VERIFIED))
 .where(Table.ATLAN_TAGS.eq("Marketing"))
 .build()
val dp = DataProduct.creator("Marketing Influence", // (2)
 DataDomain.refByQualifiedName("default/domain/marketing"), // (3)
 assets) // (4)
 .build() // (5)
val response = dp.save(client) // (6)
```

1. When defining a data product, you must define the assets within it. These are defined through a search, so that the assets included can be automatically managed. In this example, we're selecting all verified tables that have a tag of `Marketing`.
2. You must provide a human-readable name for your data product.
3. You must also provide the domain in which the data product should exist.
4. And finally the search that was defined earlier, to define which assets to include in the data product.
5. You then need to build the object.
6. And then you can `save()` the object you've built to create the new data product in Atlan. Because this operation will persist the asset in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

### Raw REST API

```json showLineNumbers title="POST /api/meta/entity/bulk"
 {
 "entities": [
 {
 "typeName": "DataProduct", // (1)
 "attributes": {
 "name": "Marketing Influence", // (2)
 "qualifiedName": "default/domain/8S8vbC9hT6iPLMY2ydXqf/super/product/marketingInfluence", // (3)
 "parentDomainQualifiedName": "default/domain/8S8vbC9hT6iPLMY2ydXqf/super", // (4)
 "superDomainQualifiedName": "default/domain/8S8vbC9hT6iPLMY2ydXqf/super", // (5)
 "dataProductAssetsDSL": "{\"query\": {\"attributes\": [\"__traitNames\", \"connectorName\", \"__customAttributes\", \"certificateStatus\", \"tenantId\", \"anchor\", \"parentQualifiedName\", \"Query.parentQualifiedName\", \"AtlasGlossaryTerm.anchor\", \"databaseName\", \"schemaName\", \"parent\", \"connectionQualifiedName\", \"collectionQualifiedName\", \"announcementMessage\", \"announcementTitle\", \"announcementType\", \"announcementUpdatedAt\", \"announcementUpdatedBy\", \"allowQuery\", \"allowQueryPreview\", \"adminGroups\", \"adminRoles\", \"adminUsers\", \"category\", \"credentialStrategy\", \"connectionSSOCredentialGuid\", \"certificateStatus\", \"certificateUpdatedAt\", \"certificateUpdatedBy\", \"classifications\", \"connectionId\", \"connectionQualifiedName\", \"connectorName\", \"dataType\", \"defaultDatabaseQualifiedName\", \"defaultSchemaQualifiedName\", \"description\", \"displayName\", \"links\", \"link\", \"meanings\", \"name\", \"ownerGroups\", \"ownerUsers\", \"qualifiedName\", \"typeName\", \"userDescription\", \"displayDescription\", \"subDataType\", \"rowLimit\", \"queryTimeout\", \"previewCredentialStrategy\", \"policyStrategy\", \"policyStrategyForSamplePreview\", \"useObjectStorage\", \"objectStorageUploadThreshold\", \"outputPortDataProducts\"], \"dsl\": {\"from\": 0, \"query\": {\"bool\": {\"filter\": {\"bool\": {\"filter\": [{\"term\": {\"__state\": {\"value\": \"ACTIVE\", \"case_insensitive\": false}}}, {\"term\": {\"__typeName.keyword\": {\"value\": \"Table\", \"case_insensitive\": false}}}, {\"term\": {\"certificateStatus\": {\"value\": \"VERIFIED\", \"case_insensitive\": false}}}, {\"term\": {\"__traitNames\": {\"value\": \"Marketing\", \"case_insensitive\": false}}}]}}}}}, \"suppressLogs\": true}, \"filterScrubbed\": true}",
 // (6)
 "dataProductAssetsPlaybookFilter": "{\"condition\":\"AND\",\"isGroupLocked\":false,\"rules\":[]}"
 // (7)
 },
 "relationshipAttributes": {
 "dataDomain": {
 "typeName": "DataDomain",
 "uniqueAttributes": { // (8)
 "qualifiedName": "default/domain/8S8vbC9hT6iPLMY2ydXqf/super"
 }
 }
 }
 }
 ]
}
```

1. The `typeName` must be exactly `DataProduct`.
2. Provide a human-readable name for your data product.
3. Make sure the `qualifiedName` follows the pattern: `<parentDomainQualifiedName>/product/<lowerCamelCaseName>`.
4. Provide a `parentDomainQualifiedName` for the data domain under which you want to create this product.
5. Provide a `superDomainQualifiedName` for the data domain under which you want to create this product. If creating a product under sub-domains, this should be the qualified name of the root-level domain.
6. Provide the DSL that defines the assets to include in the data product as an embedded JSON string.

 :::tip[Use SDK to create data products]
The above data products assets DSL requires a filter as a nested object construct within an outer bool, rather than a list or array. It's recommended to create data products via SDK as it handles this complexity automatically.
 :::
7. Specify the default playbook filter to define which assets are shown in the data product UI.
8. Specify the `qualifiedName` of the data domain under which you want to create this product.

## Retrieve data product

To retrieve a data product by its human-readable name:

### Java

```java showLineNumbers title="Retrieve a data product by its human-readable name"
DataProduct product = DataProduct.findByName( // (1)
 client, "marketingInfluence", List.of("certificateStatus")
).get(0);
```

1. Use `DataProduct.findByName()` method to retrieve a data product by its human-readable name:

 - client through which to access a tenant.
 - name of the data product.
 - (optional) a list of attributes to retrieve for the data product, for example `certificateStatus`.

### Python

```python showLineNumbers title="Retrieve a data product by its human-readable name"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import DataProduct

client = AtlanClient()

product = client.asset.find_product_by_name( # (1)
 name="marketingInfluence",
 attributes=["certificateStatus"]
)

assert product
assert product.certificate_status
```

 1. Use `client.asset.find_product_by_name()` method to retrieve a data product by its human-readable name:

 - name of the data product.
 - (optional) a list of attributes to retrieve
 for the data product, for example `certificateStatus`.

### Kotlin

```kotlin showLineNumbers title="Retrieve a data product by its human-readable name"
val product = DataProduct.findByName( // (1)
 client, "marketingInfluence", listOf("certificateStatus")
).get(0)
```

1. Use `DataProduct.findByName()` method to retrieve a data product by its human-readable name:

 - client through which to access a tenant.
 - name of the data product.
 - (optional) a list of attributes to retrieve for the data product, for example `certificateStatus`.

### Raw REST API

```json showLineNumbers title="POST /api/meta/search/indexsearch"
{
 "dsl": {
 "from": 0,
 "size": 100,
 "aggregations": {},
 "track_total_hits": true,
 "query": {
 "bool": {
 "filter": [
 {
 "term": {
 "name.keyword": {
 "value": "marketingInfluence" // (1)
 }
 }
 },
 {
 "term": {
 "__typeName.keyword": {
 "value": "DataProduct"
 }
 }
 }
 ]
 }
 },
 "sort": [
 {
 "__guid": {
 "order": "asc"
 }
 }
 ]
 },
 "attributes": [
 "certificateStatus" // (2)
 ]
}
```

1. Human-readable name of the data product.
2. (optional) a list of attributes to retrieve
for the data product, for example `certificateStatus`.

## Retrieve all assets linked to data product

To retrieve list of all assets associated with a data product:

### Java

```java showLineNumbers title="Retrieve list of all assets linked to a data product"
DataProduct dp = DataProduct.get(client, 
 "49abc625-9a03-4733-bdfb-ec0cb5315cac", true); // (1)
IndexSearchResponse response = dp.getAssets(client); // (2)
for (Asset result : response)
```

1. Use `DataProduct.get()` method to retrieve the data product by its `GUID`.
2. Use `DataProduct.getAssets()` method to retrieve all assets linked to the data product, providing the `client` used to access the tenant.

### Python

```python showLineNumbers title="Retrieve list of all assets linked to a data product"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import DataProduct, Asset

client = AtlanClient()

product = client.asset.get_by_guid( # (1)
 guid="49abc625-9a03-4733-bdfb-ec0cb5315cac",
 asset_type=DataProduct,
 attributes=[DataProduct.DATA_PRODUCT_ASSETS_DSL],
 related_attributes=[Asset.DESCRIPTION, Asset.USER_DESCRIPTION]
) 
response = product.get_assets(client) # (2)

for assets in response:
 // Do something with the assets retrieved...

assert product
assert response
```

 1. Use `client.asset.get_by_guid()` method to retrieve the data product by its `GUID`.
 2. Use `DataProduct.get_assets()` method to retrieve all assets linked to the data product.

### Kotlin

```kotlin showLineNumbers title="Retrieve list of all assets linked to a data product"
val product = DataProduct.get(client, 
 "49abc625-9a03-4733-bdfb-ec0cb5315cac", true); // (1)
val response = dp.getAssets(client); // (2)
for (result in response)
```

1. Use `DataProduct.get()` method to retrieve the data product by its `GUID`.
2. Use `DataProduct.getAssets()` method to retrieve all assets linked to the data product, providing the `client` used to access the tenant.

### Raw REST API

```json showLineNumbers title="POST /api/meta/search/indexsearch"
{
 "dsl": {
 "from": 0,
 "size": 100,
 "aggregations": {},
 "track_total_hits": true,
 "query": {
 "bool": {
 "filter": {
 "bool": {
 "filter": [ // (1)
 }
 },
 {
 "term": {
 "__typeName.keyword": {
 "value": "Table", 
 "case_insensitive": false 
 }
 }
 }
 ]
 }
 }
 }
 },
 "sort": []
 },
 "attributes": [ // (2)
 "certificateStatus","connectorName", 
 "__customAttributes", "certificateStatus", 
 ]
}
```

1. List the asset type and active status for all assets included in the data product.
2. (optional) a list of attributes to retrieve
for the data product's list of all assets, for example `certificateStatus`.

## Update data product

To update a data product:

### Java

```java showLineNumbers title="Update a data product"
FluentSearch assets = Table.select(client)
 .where(Table.CERTIFICATE_STATUS.eq(CertificateStatus.VERIFIED))
 .where(Table.ATLAN_TAGS.eq("Digital Marketing"))
 .build(); // (1)

DataProduct dp = DataProduct.updater("default/product/marketingInfluence", // (2)
 "Marketing Influence")
 .userDescription("Now with a description!") // (3)
 .assetSelection(Atlan.getDefaultClient(), assets)
 .build(); // (4)
AssetMutationResponse response = dp.save(client); // (5)
```

1. **(Optional)** You can also update the assets within an existing product.
These assets are defined through a search, allowing for automatic management.
In this example, we select all verified tables that are tagged with `Digital Marketing`.
2. Use the `updater()` method to update a data product,
providing the `qualifiedName` and `name` of the data product.
3. You can chain additional enrichments onto the updater, such as:
 - `userDescription`: updating the product's description.
 - `assetSelection`: modifying the assets within the product.
4. You then need to build the object.
5. You can then `save()` the object you've built to update the data product in Atlan. Because this operation will persist the asset in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

### Python

```python showLineNumbers title="Update a data product"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import DataProduct

client = AtlanClient()

assets = (
 FluentSearch()
 .where(CompoundQuery.active_assets())
 .where(CompoundQuery.asset_type(Table))
 .where(Table.CERTIFICATE_STATUS.eq(CertificateStatus.VERIFIED.value))
 .where(Table.ATLAN_TAGS.eq("Digital Marketing"))
).to_request() # (1)

product = DataProduct.updater( # (2)
 qualified_name="default/product/marketingInfluence", # (3)
 name="Marketing Influence", # (4)
 asset_selection=assets, # (5)
)
product.user_description = "Now with a description!" # (6)

response = client.asset.save(product) # (7)
```

1. **(Optional)** You can also update the assets within an existing product.
These assets are defined through a search, allowing for automatic management.
In this example, we select all verified tables that are tagged with `Digital Marketing`.
2. Use the `updater()` method to update a data product.
3. You must provide the `qualifiedName` of the data product.
4. You must provide the `name` of the data product.
5. Optionally, you can provide an index search request to define the assets to include in the data product.
6. You can then add on any other updates, such as changing the user description of the data product.
7. To update the data product in Atlan, call the `save()` method with the object you've built.

### Kotlin

```kotlin showLineNumbers title="Update a data product"
val assets = Table.select(client)
 .where(Table.CERTIFICATE_STATUS.eq(CertificateStatus.VERIFIED))
 .where(Table.ATLAN_TAGS.eq("Digital Marketing"))
 .build(); // (1)

val dp = DataProduct.updater("default/product/marketingInfluence", // (2)
 "Marketing Influence")
 .userDescription("Now with a description!") // (3)
 .assetSelection(Atlan.getDefaultClient(), assets)
 .build() // (4)
val response = dp.save(client) // (5)
```

1. **(Optional)** You can also update the assets within an existing product.
These assets are defined through a search, allowing for automatic management.
In this example, we select all verified tables that are tagged with `Digital Marketing`.
2. Use the `updater()` method to update a data product,
providing the `qualifiedName` and `name` of the data product.
3. You can chain additional enrichments onto the updater, such as:
 - `userDescription`: updating the product's description.
 - `assetSelection`: modifying the assets within the product.
4. You then need to build the object.
5. You can then `save()` the object you've built to update the data product in Atlan. Because this operation will persist the asset in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

### Raw REST API

```json showLineNumbers title="POST /api/meta/entity/bulk"
{
 "entities": [
 {
 "typeName": "DataProduct", // (1)
 "attributes": {
 "name": "Marketing Influence", // (2)
 "qualifiedName": "default/product/marketingInfluence", // (3)
 "userDescription": "Now with a description!", // (4)
 "dataProductAssetsDSL": "{\"query\": {\"attributes\": [\"__traitNames\", \"connectorName\", \"__customAttributes\", \"certificateStatus\", \"tenantId\", \"anchor\", \"parentQualifiedName\", \"Query.parentQualifiedName\", \"AtlasGlossaryTerm.anchor\", \"databaseName\", \"schemaName\", \"parent\", \"connectionQualifiedName\", \"collectionQualifiedName\", \"announcementMessage\", \"announcementTitle\", \"announcementType\", \"announcementUpdatedAt\", \"announcementUpdatedBy\", \"allowQuery\", \"allowQueryPreview\", \"adminGroups\", \"adminRoles\", \"adminUsers\", \"category\", \"credentialStrategy\", \"connectionSSOCredentialGuid\", \"certificateStatus\", \"certificateUpdatedAt\", \"certificateUpdatedBy\", \"classifications\", \"connectionId\", \"connectionQualifiedName\", \"connectorName\", \"dataType\", \"defaultDatabaseQualifiedName\", \"defaultSchemaQualifiedName\", \"description\", \"displayName\", \"links\", \"link\", \"meanings\", \"name\", \"ownerGroups\", \"ownerUsers\", \"qualifiedName\", \"typeName\", \"userDescription\", \"displayDescription\", \"subDataType\", \"rowLimit\", \"queryTimeout\", \"previewCredentialStrategy\", \"policyStrategy\", \"policyStrategyForSamplePreview\", \"useObjectStorage\", \"objectStorageUploadThreshold\", \"outputPortDataProducts\"], \"dsl\": {\"from\": 0, \"query\": {\"bool\": {\"filter\": {\"bool\": {\"filter\": [{\"term\": {\"__state\": {\"value\": \"ACTIVE\", \"case_insensitive\": false}}}, {\"term\": {\"__typeName.keyword\": {\"value\": \"Table\", \"case_insensitive\": false}}}, {\"term\": {\"certificateStatus\": {\"value\": \"VERIFIED\", \"case_insensitive\": false}}}, {\"term\": {\"__traitNames\": {\"value\": \"Digital Marketing\", \"case_insensitive\": false}}}]}}}}}, \"suppressLogs\": true}, \"filterScrubbed\": true}",
 // (5)
 }
 }
 ]
}
```

1. The `typeName` must be exactly `DataProduct`.
2. Human-readable name for your data product.
3. You must provide the the `qualifiedName` of the data product to update.
4. You can add on any other updates, such as changing the user description of the data product.
5. **(Optional)** You can update the product's assets by providing
a DSL that defines the assets to include in the data product, as an embedded JSON string.

 :::tip[Use SDK to update data products]
The above data products assets DSL requires a filter as a
nested object construct within an outer bool, rather than a list or array.
It's recommended to update data products via SDK as it handles this complexity automatically.
 :::

## Delete data product

### Soft-delete (archive)

To soft-delete, or archive, a data product:

### Java

```java showLineNumbers title="Delete a data product"
AssetDeletionResponse response = DataProduct.delete(client, "218c8144-dc39-43a5-b0c0-9eeb4d11e74a"); // (1)
```

1. To archive a data product in Atlan, call the `DataProduct.delete()` method with the GUID of the data product. Because this operation will archive the asset in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

### Python

```python showLineNumbers title="Delete a data product"
from pyatlan.client.atlan import AtlanClient

client = AtlanClient()

client.asset.delete_by_guid("218c8144-dc39-43a5-b0c0-9eeb4d11e74a") # (1)
```

1. To archive a data product in Atlan, call the `asset.delete_by_guid()` method with the GUID of the data product.

### Kotlin

```kotlin showLineNumbers title="Delete a data product"
val response = DataProduct.delete(client, "218c8144-dc39-43a5-b0c0-9eeb4d11e74a") // (1)
```

1. To archive a data product in Atlan, call the `DataProduct.delete()` method with the GUID of the data product. Because this operation will archive the asset in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

### Raw REST API

```json showLineNumbers title="DELETE /api/meta/entity/bulk?guid=218c8144-dc39-43a5-b0c0-9eeb4d11e74a&deleteType=SOFT"
// (1)
```

1. All the details for deleting the data product are specified in the URL directly. Note that you must provide the GUID of the data product to delete it.

### Hard-delete (purge)

To permanently delete (purge) a data product:

### Java

```java showLineNumbers title="Delete a data product"
AssetDeletionResponse response = DataProduct.purge(client, "218c8144-dc39-43a5-b0c0-9eeb4d11e74a"); // (1)
```

1. To permanently delete a data product in Atlan, call the `DataProduct.purge()` method with the GUID of the data product. Because this operation will remove the asset from Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

### Python

```python showLineNumbers title="Delete a product"
from pyatlan.client.atlan import AtlanClient

client = AtlanClient()

client.asset.purge_by_guid("218c8144-dc39-43a5-b0c0-9eeb4d11e74a") # (1)
```

1. To permanently delete a data product in Atlan, call the `asset.purge_by_guid()` method with the GUID of the data product.

### Kotlin

```kotlin showLineNumbers title="Delete a data product"
val response = DataProduct.purge(client, "218c8144-dc39-43a5-b0c0-9eeb4d11e74a") // (1)
```

1. To permanently delete a data product in Atlan, call the `DataProduct.purge()` method with the GUID of the data product. Because this operation will remove the asset from Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

### Raw REST API

```json showLineNumbers title="DELETE /api/meta/entity/bulk?guid=218c8144-dc39-43a5-b0c0-9eeb4d11e74a&deleteType=PURGE"
// (1)
```

1. All the details for deleting the data product are specified in the URL directly. Note that you must provide the GUID of the data product to delete it.

---
