Skip to content

Inventory

albert.collections.inventory.InventoryCollection

InventoryCollection(*, session: AlbertSession)

Bases: BaseCollection

Manage Inventory Items in the Albert platform.

An Inventory Item is a catalog entry for a physical or formulated material tracked in Albert. Every item belongs to one of four categories:

  • RawMaterials: purchased substances used as ingredients (e.g. a solvent or pigment), typically linked to a manufacturing Company and one or more CAS numbers.
  • Consumables: supplies consumed during lab work (e.g. gloves, vials).
  • Equipment: instruments and apparatus.
  • Formulas: mixtures designed in Albert. Formulas are created through the Worksheet collection (WorksheetCollection), not here; create rejects Formula items.

Inventory Items are referenced throughout the platform by their Inventory ID (format INV..., e.g. "INVA9999999"). They are the building blocks that Worksheets, Tasks, and Property Data all point back to.

This collection is accessed as client.inventory.

Example

from albert import Albert
from albert.resources.inventory import InventoryCategory
client = Albert()
# Find raw materials mentioning "titanium dioxide"
items = client.inventory.get_all(
    text="titanium dioxide",
    category=InventoryCategory.RAW_MATERIALS,
    max_items=25,
)
for item in items:
    print(item.id, item.name)

Parameters:

Name Type Description Default
session AlbertSession

The authenticated Albert session used for API calls.

required

Attributes:

Name Type Description
base_path str

The base API route for inventory requests.

Methods:

Name Description
create

Create a new inventory item (raw material, consumable, or equipment).

get_by_id

Get a single fully populated item by its ID.

get_by_ids

Get many items by their IDs in batches.

search

Fast, lightweight search returning partial items (best for lookups/counts).

get_all

Same filters as search, but returns fully populated items (slower).

update

Update an existing item.

delete

Delete an item by its ID.

merge

Merge duplicate item(s) into a single parent item.

exists

Check whether an item with the same name and company already exists.

get_match_or_none

Return the existing item matching name + company, or None.

add_specs

Attach inventory reference specs to an item (deprecated; prefer client.attributes).

get_specs

Get inventory reference specs for items (deprecated; prefer client.attributes).

get_all_facets

Get facet groups (aggregated filter counts) for a query.

get_facet_by_name

Get a single named facet group for a query.

Parameters:

Name Type Description Default
session AlbertSession

The authenticated Albert session used for API calls.

required
Source code in src/albert/collections/inventory.py
def __init__(self, *, session: AlbertSession):
    """Initialize an InventoryCollection.

    Parameters
    ----------
    session : AlbertSession
        The authenticated Albert session used for API calls.
    """
    super().__init__(session=session)
    self.base_path = f"/api/{InventoryCollection._api_version}/inventories"

base_path

base_path = (
    f"/api/{InventoryCollection._api_version}/inventories"
)

merge

merge(
    *,
    parent_id: InventoryId,
    child_id: InventoryId | list[InventoryId],
    modules: list[InventoryMergeModule] | None = None,
) -> None

Merge one or more duplicate inventory items into a single parent item.

Use this to consolidate duplicates: the child item(s) are folded into the parent, and their data (as selected by modules) is carried over. The child items are removed as standalone entries.

Example

client.inventory.merge(parent_id="INVA9999999", child_id=["INVA9999998", "INVA9999997"])

Parameters:

Name Type Description Default
parent_id InventoryId

The item to keep. All merged data ends up here.

required
child_id InventoryId or list[InventoryId]

The duplicate item(s) to merge into the parent. At least one is required.

required
modules list[InventoryMergeModule]

Which categories of data to carry over from the children (e.g. pricing, notes). Defaults to all modules.

None

Returns:

Type Description
None
Source code in src/albert/collections/inventory.py
@validate_call
def merge(
    self,
    *,
    parent_id: InventoryId,
    child_id: InventoryId | list[InventoryId],
    modules: list[InventoryMergeModule] | None = None,
) -> None:
    """Merge one or more duplicate inventory items into a single parent item.

    Use this to consolidate duplicates: the child item(s) are folded into the
    parent, and their data (as selected by ``modules``) is carried over. The
    child items are removed as standalone entries.

    !!! example
        ```python
        client.inventory.merge(parent_id="INVA9999999", child_id=["INVA9999998", "INVA9999997"])
        ```

    Parameters
    ----------
    parent_id : InventoryId
        The item to keep. All merged data ends up here.
    child_id : InventoryId or list[InventoryId]
        The duplicate item(s) to merge into the parent. At least one is required.
    modules : list[InventoryMergeModule], optional
        Which categories of data to carry over from the children (e.g. pricing,
        notes). Defaults to all modules.

    Returns
    -------
    None
    """

    # assume "all" modules if not specified explicitly
    modules = modules if modules is not None else ALL_MERGE_MODULES

    # define merge endpoint
    url = f"{self.base_path}/merge"

    child_ids = ensure_list(child_id) or []
    if not child_ids:
        raise ValueError("At least one child inventory id is required for merge operations.")
    child_inventories = [{"id": i} for i in child_ids]

    # define payload using the class
    payload = MergeInventory(
        parent_id=parent_id,
        child_inventories=child_inventories,
        modules=modules,
    )

    # post request
    self.session.post(url, json=payload.model_dump(mode="json", by_alias=True))

exists

exists(*, inventory_item: InventoryItem) -> bool

Check whether a matching inventory item already exists.

A match is determined by name and company, the same way create detects duplicates. Useful before creating an item to avoid duplicates.

Example

from albert.resources.inventory import InventoryItem, InventoryCategory
from albert.resources.companies import Company
candidate = InventoryItem(
    name="Acetone",
    category=InventoryCategory.RAW_MATERIALS,
    company=Company(name="Acme Chemicals"),
)
client.inventory.exists(inventory_item=candidate)
# True

Parameters:

Name Type Description Default
inventory_item InventoryItem

The item to look for. Its name and company are used to match.

required

Returns:

Type Description
bool

True if a matching item exists, False otherwise.

Source code in src/albert/collections/inventory.py
def exists(self, *, inventory_item: InventoryItem) -> bool:
    """Check whether a matching inventory item already exists.

    A match is determined by name and company, the same way [`create`][albert.collections.inventory.InventoryCollection.create]
    detects duplicates. Useful before creating an item to avoid duplicates.

    !!! example
        ```python
        from albert.resources.inventory import InventoryItem, InventoryCategory
        from albert.resources.companies import Company
        candidate = InventoryItem(
            name="Acetone",
            category=InventoryCategory.RAW_MATERIALS,
            company=Company(name="Acme Chemicals"),
        )
        client.inventory.exists(inventory_item=candidate)
        # True
        ```

    Parameters
    ----------
    inventory_item : InventoryItem
        The item to look for. Its ``name`` and ``company`` are used to match.

    Returns
    -------
    bool
        True if a matching item exists, False otherwise.
    """
    hit = self.get_match_or_none(inventory_item=inventory_item)
    return bool(hit)

get_match_or_none

get_match_or_none(
    *, inventory_item: InventoryItem
) -> InventoryItem | None

Return the existing item matching name and company, or None.

Like exists, but returns the matched item itself so you can reuse its ID instead of creating a duplicate.

Example

existing = client.inventory.get_match_or_none(inventory_item=candidate)
existing.id if existing else "no match"
# 'INVA9999999'

Parameters:

Name Type Description Default
inventory_item InventoryItem

The item to match. Its name and company are used to match.

required

Returns:

Type Description
InventoryItem or None

The matching item, or None if no match is found.

Source code in src/albert/collections/inventory.py
def get_match_or_none(self, *, inventory_item: InventoryItem) -> InventoryItem | None:
    """Return the existing item matching name and company, or None.

    Like [`exists`][albert.collections.inventory.InventoryCollection.exists], but returns the matched item itself so you can reuse
    its ID instead of creating a duplicate.

    !!! example
        ```python
        existing = client.inventory.get_match_or_none(inventory_item=candidate)
        existing.id if existing else "no match"
        # 'INVA9999999'
        ```

    Parameters
    ----------
    inventory_item : InventoryItem
        The item to match. Its ``name`` and ``company`` are used to match.

    Returns
    -------
    InventoryItem or None
        The matching item, or None if no match is found.
    """
    company = inventory_item.company
    company_id = company.id if company is not None else None
    company_name = company.name if company is not None else None

    hits = self.get_all(
        text=inventory_item.name,
        company=[company] if isinstance(company, Company) else None,
        max_items=100,
    )

    for inv in hits:
        if inv.name != inventory_item.name:
            continue
        inv_company = inv.company
        # Prefer matching on company id; fall back to name when the id is
        # unavailable (e.g. an unsaved Company passed without an id).
        if company_id is not None:
            matched = inv_company is not None and inv_company.id == company_id
        else:
            matched = (inv_company.name if inv_company else None) == company_name
        if matched:
            return inv
    return None

create

create(
    *,
    inventory_item: InventoryItem,
    avoid_duplicates: bool = True,
) -> InventoryItem

Create a new inventory item.

Use this to add a raw material, consumable, or equipment item to the catalog. Formula items are not supported here; build those through the Worksheet collection.

Any tags or company on the item that do not yet exist in Albert are created automatically before the item is registered (see CompanyCollection and TagCollection).

Example

from albert.resources.inventory import InventoryItem, InventoryCategory
from albert.resources.companies import Company
item = InventoryItem(
    name="Titanium Dioxide",
    category=InventoryCategory.RAW_MATERIALS,
    company=Company(name="Acme Chemicals"),
)
created = client.inventory.create(inventory_item=item)
created.id
# 'INVA9999999'

Parameters:

Name Type Description Default
inventory_item InventoryItem

The item to create. name and category are required. For raw materials, set company to the manufacturing Company and cas to the relevant CAS numbers.

required
avoid_duplicates bool

When True (default), if an item with the same name and company already exists, that existing item is returned instead of creating a duplicate. Set to False to force creation.

True

Returns:

Type Description
InventoryItem

The newly created item, populated with its assigned Inventory ID.

Raises:

Type Description
NotImplementedError

If inventory_item.category is Formulas.

Source code in src/albert/collections/inventory.py
def create(
    self,
    *,
    inventory_item: InventoryItem,
    avoid_duplicates: bool = True,
) -> InventoryItem:
    """Create a new inventory item.

    Use this to add a raw material, consumable, or equipment item to the
    catalog. Formula items are not supported here; build those through the
    Worksheet collection.

    Any tags or company on the item that do not yet exist in Albert are
    created automatically before the item is registered (see
    [`CompanyCollection`][albert.collections.companies.CompanyCollection] and
    [`TagCollection`][albert.collections.tags.TagCollection]).

    !!! example
        ```python
        from albert.resources.inventory import InventoryItem, InventoryCategory
        from albert.resources.companies import Company
        item = InventoryItem(
            name="Titanium Dioxide",
            category=InventoryCategory.RAW_MATERIALS,
            company=Company(name="Acme Chemicals"),
        )
        created = client.inventory.create(inventory_item=item)
        created.id
        # 'INVA9999999'
        ```

    Parameters
    ----------
    inventory_item : InventoryItem
        The item to create. ``name`` and ``category`` are required. For raw
        materials, set ``company`` to the manufacturing Company and ``cas`` to
        the relevant CAS numbers.
    avoid_duplicates : bool, optional
        When True (default), if an item with the same name and company already
        exists, that existing item is returned instead of creating a duplicate.
        Set to False to force creation.

    Returns
    -------
    InventoryItem
        The newly created item, populated with its assigned Inventory ID.

    Raises
    ------
    NotImplementedError
        If ``inventory_item.category`` is ``Formulas``.
    """
    category = (
        inventory_item.category
        if isinstance(inventory_item.category, str)
        else inventory_item.category.value
    )
    if category == InventoryCategory.FORMULAS.value:
        # This will need to interact with worksheets
        raise NotImplementedError("Registrations of formulas not yet implemented")
    tag_collection = TagCollection(session=self.session)
    if inventory_item.tags is not None and inventory_item.tags != []:
        all_tags = [
            tag_collection.get_or_create(tag=t) if t.id is None else t
            for t in inventory_item.tags
        ]
        inventory_item.tags = all_tags
    if inventory_item.company and inventory_item.company.id is None:
        company_collection = CompanyCollection(session=self.session)
        inventory_item.company = company_collection.get_or_create(
            company=inventory_item.company
        )
    # Check to see if there is a match on name + Company already
    if avoid_duplicates:
        existing = self.get_match_or_none(inventory_item=inventory_item)
        if isinstance(existing, InventoryItem):
            logging.warning(
                f"Inventory item already exists with name {existing.name} and company {existing.company.name}, returning existing item."
            )
            return existing
    response = self.session.post(
        self.base_path,
        json=inventory_item.model_dump(by_alias=True, exclude_none=True, mode="json"),
    )

    # ACL is populated after the create response is sent by the API.
    return self.get_by_id(id=response.json()["albertId"])

get_by_id

get_by_id(*, id: InventoryId) -> InventoryItem

Get a single, fully populated inventory item by its ID.

For retrieving many items at once, use get_by_ids. To find items without knowing their IDs, use search or get_all.

Example

item = client.inventory.get_by_id(id="INVA9999999")
item.name
# 'Titanium Dioxide'

Parameters:

Name Type Description Default
id InventoryId

The Inventory ID (format INV..., e.g. "INVA9999999").

required

Returns:

Type Description
InventoryItem

The fully populated item.

Source code in src/albert/collections/inventory.py
@validate_call
def get_by_id(self, *, id: InventoryId) -> InventoryItem:
    """Get a single, fully populated inventory item by its ID.

    For retrieving many items at once, use [`get_by_ids`][albert.collections.inventory.InventoryCollection.get_by_ids]. To find items
    without knowing their IDs, use [`search`][albert.collections.inventory.InventoryCollection.search] or [`get_all`][albert.collections.inventory.InventoryCollection.get_all].

    !!! example
        ```python
        item = client.inventory.get_by_id(id="INVA9999999")
        item.name
        # 'Titanium Dioxide'
        ```

    Parameters
    ----------
    id : InventoryId
        The Inventory ID (format ``INV...``, e.g. ``"INVA9999999"``).

    Returns
    -------
    InventoryItem
        The fully populated item.
    """
    url = f"{self.base_path}/{id}"
    response = self.session.get(url)
    return InventoryItem(**response.json())

get_by_ids

get_by_ids(
    *, ids: list[InventoryId]
) -> list[InventoryItem]

Get multiple fully populated inventory items by their IDs.

Requests are automatically split into batches, so arbitrarily long ID lists are supported. Items not found are omitted from the result.

Example

items = client.inventory.get_by_ids(ids=["INVA9999999", "INVA9999998"])
[i.name for i in items]
# ['Titanium Dioxide', 'Acetone']

Parameters:

Name Type Description Default
ids list[InventoryId]

The Inventory IDs to retrieve (format INV...).

required

Returns:

Type Description
list[InventoryItem]

The matching items. Order is not guaranteed to match the input.

Source code in src/albert/collections/inventory.py
@validate_call
def get_by_ids(self, *, ids: list[InventoryId]) -> list[InventoryItem]:
    """Get multiple fully populated inventory items by their IDs.

    Requests are automatically split into batches, so arbitrarily long ID
    lists are supported. Items not found are omitted from the result.

    !!! example
        ```python
        items = client.inventory.get_by_ids(ids=["INVA9999999", "INVA9999998"])
        [i.name for i in items]
        # ['Titanium Dioxide', 'Acetone']
        ```

    Parameters
    ----------
    ids : list[InventoryId]
        The Inventory IDs to retrieve (format ``INV...``).

    Returns
    -------
    list[InventoryItem]
        The matching items. Order is not guaranteed to match the input.
    """
    batch_size = 250
    batches = [ids[i : i + batch_size] for i in range(0, len(ids), batch_size)]
    inventory = []
    for batch in batches:
        response = self.session.get(f"{self.base_path}/ids", params={"id": batch})
        inventory.extend([InventoryItem(**item) for item in response.json()["Items"]])
    return inventory

get_specs

get_specs(
    *, ids: list[InventoryId]
) -> list[InventorySpecList]

Get the legacy inventory reference specs attached to inventory items.

Each InventorySpecList holds that item's declared reference properties (definition and value together). This is not Property Data: for measured task results or custom property-data values, use PropertyDataCollection. Requests are automatically batched.

Deprecated

Prefer get_by_parent_ids (client.attributes). Specs are removed in SDK 2.0. See the Specs → Attributes migration guide.

Example

spec_lists = client.inventory.get_specs(ids=["INVA9999999"])
for spec in spec_lists[0].specs:
    print(spec.name, spec.value.reference if spec.value else None)

Parameters:

Name Type Description Default
ids list[InventoryId]

The Inventory IDs to fetch specs for (format INV...).

required

Returns:

Type Description
list[InventorySpecList]

One entry per item, each holding that item's specs.

Source code in src/albert/collections/inventory.py
@deprecated(
    "get_specs() is deprecated and will be removed in 2.0. "
    "Use client.attributes.get_by_parent_ids() instead."
)
@validate_call
def get_specs(self, *, ids: list[InventoryId]) -> list[InventorySpecList]:
    """Get the legacy inventory reference specs attached to inventory items.

    Each [`InventorySpecList`][albert.resources.inventory.InventorySpecList]
    holds that item's declared reference properties (definition and value
    together). This is **not** Property Data: for measured task results or
    custom property-data values, use
    [`PropertyDataCollection`][albert.collections.property_data.PropertyDataCollection].
    Requests are automatically batched.

    !!! warning "Deprecated"
        Prefer
        [`get_by_parent_ids`][albert.collections.attributes.AttributeCollection.get_by_parent_ids]
        (``client.attributes``). Specs are removed in SDK 2.0. See the Specs →
        Attributes migration guide.

    !!! example
        ```python
        spec_lists = client.inventory.get_specs(ids=["INVA9999999"])
        for spec in spec_lists[0].specs:
            print(spec.name, spec.value.reference if spec.value else None)
        ```

    Parameters
    ----------
    ids : list[InventoryId]
        The Inventory IDs to fetch specs for (format ``INV...``).

    Returns
    -------
    list[InventorySpecList]
        One entry per item, each holding that item's specs.
    """
    url = f"{self.base_path}/specs"
    batches = [ids[i : i + 250] for i in range(0, len(ids), 250)]
    ta = TypeAdapter(InventorySpecList)
    return [
        ta.validate_python(item)
        for batch in batches
        for item in self.session.get(url, params={"id": batch}).json()
    ]

add_specs

add_specs(
    *,
    inventory_id: InventoryId,
    specs: InventorySpec | list[InventorySpec],
) -> InventorySpecList

Attach legacy inventory reference specs to an inventory item.

Each InventorySpec both defines a property (name, data_column_id) and assigns its expected InventorySpecValue on this item. Use Specs for inventory reference properties (e.g. a supplier-stated density that worksheets look up). For experimentally measured results, use Tasks and PropertyDataCollection instead. A spec may optionally name conditions via a workflow.

Deprecated

Prefer add_values (client.attributes) after creating shared attribute definitions. Specs are removed in SDK 2.0. See the Specs → Attributes migration guide.

Warning

This call replaces the item's complete spec set; it is not an append. Always pass every spec the item should carry in one call: a follow-up call with a subset can drop previously attached specs. A 500 Duplicate reference name error means spec rows with those names already exist on the item (even when get_specs shows none); do not blindly retry, call get_specs first and reconcile. There is no Specs API to remove individual values.

Example

from albert.resources.inventory import InventorySpec, InventorySpecValue
spec = InventorySpec(
    name="Density",
    data_column_id="DAC9999999",
    value=InventorySpecValue(min="1.1", max="1.3"),
)
client.inventory.add_specs(inventory_id="INVA9999999", specs=spec)

Parameters:

Name Type Description Default
inventory_id InventoryId

The item to attach the specs to (format INV...).

required
specs InventorySpec or list[InventorySpec]

The full set of reference specs the item should carry. Each embeds the property definition and value (and optionally workflow conditions).

required

Returns:

Type Description
InventorySpecList

The full set of specs now attached to the item.

Source code in src/albert/collections/inventory.py
@deprecated(
    "add_specs() is deprecated and will be removed in 2.0. "
    "Use client.attributes.add_values() instead."
)
@validate_call
def add_specs(
    self,
    *,
    inventory_id: InventoryId,
    specs: InventorySpec | list[InventorySpec],
) -> InventorySpecList:
    """Attach legacy inventory reference specs to an inventory item.

    Each [`InventorySpec`][albert.resources.inventory.InventorySpec] both
    defines a property (``name``, ``data_column_id``) and assigns its expected
    [`InventorySpecValue`][albert.resources.inventory.InventorySpecValue] on
    this item. Use Specs for inventory **reference** properties (e.g. a
    supplier-stated density that worksheets look up). For experimentally
    measured results, use Tasks and
    [`PropertyDataCollection`][albert.collections.property_data.PropertyDataCollection]
    instead. A spec may optionally name conditions via a workflow.

    !!! warning "Deprecated"
        Prefer
        [`add_values`][albert.collections.attributes.AttributeCollection.add_values]
        (``client.attributes``) after creating shared attribute definitions.
        Specs are removed in SDK 2.0. See the Specs → Attributes migration guide.

    !!! warning
        This call replaces the item's complete spec set; it is not an
        append. Always pass every spec the item should carry in one call: a
        follow-up call with a subset can drop previously attached specs. A
        ``500 Duplicate reference name`` error means spec rows with those
        names already exist on the item (even when ``get_specs`` shows
        none); do not blindly retry, call ``get_specs`` first and
        reconcile. There is no Specs API to remove individual values.

    !!! example
        ```python
        from albert.resources.inventory import InventorySpec, InventorySpecValue
        spec = InventorySpec(
            name="Density",
            data_column_id="DAC9999999",
            value=InventorySpecValue(min="1.1", max="1.3"),
        )
        client.inventory.add_specs(inventory_id="INVA9999999", specs=spec)
        ```

    Parameters
    ----------
    inventory_id : InventoryId
        The item to attach the specs to (format ``INV...``).
    specs : InventorySpec or list[InventorySpec]
        The full set of reference specs the item should carry. Each embeds
        the property definition and value (and optionally workflow conditions).

    Returns
    -------
    InventorySpecList
        The full set of specs now attached to the item.
    """
    if isinstance(specs, InventorySpec):
        specs = [specs]
    response = self.session.put(
        url=f"{self.base_path}/{inventory_id}/specs",
        json=[x.model_dump(exclude_unset=True, by_alias=True, mode="json") for x in specs],
    )
    return InventorySpecList(**response.json())

delete

delete(*, id: InventoryId) -> None

Delete an inventory item by its ID.

This permanently removes the item. To consolidate duplicates while preserving data, use merge instead.

Example

client.inventory.delete(id="INVA9999999")

Parameters:

Name Type Description Default
id InventoryId

The Inventory ID to delete (format INV...).

required

Returns:

Type Description
None
Source code in src/albert/collections/inventory.py
@validate_call
def delete(self, *, id: InventoryId) -> None:
    """Delete an inventory item by its ID.

    This permanently removes the item. To consolidate duplicates while
    preserving data, use [`merge`][albert.collections.inventory.InventoryCollection.merge] instead.

    !!! example
        ```python
        client.inventory.delete(id="INVA9999999")
        ```

    Parameters
    ----------
    id : InventoryId
        The Inventory ID to delete (format ``INV...``).

    Returns
    -------
    None
    """

    url = f"{self.base_path}/{id}"
    self.session.delete(url)

get_all_facets

get_all_facets(
    *,
    text: str | None = None,
    cas: list[Cas] | Cas | None = None,
    category: list[InventoryCategory]
    | InventoryCategory
    | None = None,
    company: list[Company] | Company | None = None,
    location: list[Location] | Location | None = None,
    storage_location: list[
        StorageLocation | StorageLocationFilter
    ]
    | StorageLocation
    | StorageLocationFilter
    | None = None,
    project_id: ProjectId | None = None,
    sheet_id: WorksheetId | None = None,
    created_by: list[User]
    | User
    | str
    | list[str]
    | None = None,
    lot_owner: list[User] | User | None = None,
    tags: list[str] | None = None,
    match_all_conditions: bool = False,
) -> list[FacetItem]

Get the facets available for an inventory search.

Facets are the grouped, counted filter options for a query, like the refinement sidebar of a search UI (e.g. how many matching items fall under each category, company, or tag). Use them to build progressive filtering or to summarize a result set without fetching every item. To pull a single named facet, use get_facet_by_name.

Example

facets = client.inventory.get_all_facets(text="titanium dioxide")
[f.name for f in facets]
# ['Category', 'Company', 'Tags', ...]

Parameters:

Name Type Description Default
text str

Free-text query matched against item name and related fields.

None
cas Cas or list[Cas]

Filter by CAS number(s).

None
category InventoryCategory or list[InventoryCategory]

Filter by category: RawMaterials, Consumables, Equipment, or Formulas.

None
company Company or list[Company]

Filter by manufacturing Company.

None
location Location or list[Location]

Filter by location.

None
storage_location StorageLocation or StorageLocationFilter or list[StorageLocation | StorageLocationFilter]

Filter by storage location.

None
project_id ProjectId

Filter by project.

None
sheet_id WorksheetId

Filter by worksheet.

None
created_by User, list[User], str, or list[str]

Filter by creator. Accepts user display name(s) or UserId(s) (e.g. "USR4227" or "Jane Doe"), or User object(s).

None
lot_owner User or list[User]

Filter by lot owner.

None
tags list[str]

Filter by tag name(s).

None
match_all_conditions bool

If True, only count items that satisfy every applied filter (AND logic). Default False.

False

Returns:

Type Description
list[FacetItem]

The facet groups available for the query.

Source code in src/albert/collections/inventory.py
@validate_call
def get_all_facets(
    self,
    *,
    text: str | None = None,
    cas: list[Cas] | Cas | None = None,
    category: list[InventoryCategory] | InventoryCategory | None = None,
    company: list[Company] | Company | None = None,
    location: list[Location] | Location | None = None,
    storage_location: list[StorageLocation | StorageLocationFilter]
    | StorageLocation
    | StorageLocationFilter
    | None = None,
    project_id: ProjectId | None = None,
    sheet_id: WorksheetId | None = None,
    created_by: list[User] | User | str | list[str] | None = None,
    lot_owner: list[User] | User | None = None,
    tags: list[str] | None = None,
    match_all_conditions: bool = False,
) -> list[FacetItem]:
    """Get the facets available for an inventory search.

    Facets are the grouped, counted filter options for a query, like the
    refinement sidebar of a search UI (e.g. how many matching items fall under
    each category, company, or tag). Use them to build progressive filtering
    or to summarize a result set without fetching every item. To pull a single
    named facet, use [`get_facet_by_name`][albert.collections.inventory.InventoryCollection.get_facet_by_name].

    !!! example
        ```python
        facets = client.inventory.get_all_facets(text="titanium dioxide")
        [f.name for f in facets]
        # ['Category', 'Company', 'Tags', ...]
        ```

    Parameters
    ----------
    text : str, optional
        Free-text query matched against item name and related fields.
    cas : Cas or list[Cas], optional
        Filter by CAS number(s).
    category : InventoryCategory or list[InventoryCategory], optional
        Filter by category: ``RawMaterials``, ``Consumables``, ``Equipment``,
        or ``Formulas``.
    company : Company or list[Company], optional
        Filter by manufacturing Company.
    location : Location or list[Location], optional
        Filter by location.
    storage_location : StorageLocation or StorageLocationFilter or list[StorageLocation | StorageLocationFilter], optional
        Filter by storage location.
    project_id : ProjectId, optional
        Filter by project.
    sheet_id : WorksheetId, optional
        Filter by worksheet.
    created_by : User, list[User], str, or list[str], optional
        Filter by creator. Accepts user display name(s) or UserId(s) (e.g.
        ``"USR4227"`` or ``"Jane Doe"``), or [`User`][albert.resources.users.User]
        object(s).
    lot_owner : User or list[User], optional
        Filter by lot owner.
    tags : list[str], optional
        Filter by tag name(s).
    match_all_conditions : bool, optional
        If True, only count items that satisfy every applied filter (AND logic).
        Default False.

    Returns
    -------
    list[FacetItem]
        The facet groups available for the query.
    """

    params = self._prepare_parameters(
        text=text,
        cas=cas,
        category=category,
        company=company,
        location=location,
        storage_location=storage_location,
        project_id=project_id,
        sheet_id=sheet_id,
        created_by=created_by,
        lot_owner=lot_owner,
        tags=tags,
    )
    params["limit"] = 1
    params = {k: v for k, v in params.items() if v is not None}
    response = self.session.get(
        url=f"{self.base_path}/llmsearch"
        if match_all_conditions
        else f"{self.base_path}/search",
        params=params,
    )
    return [FacetItem.model_validate(x) for x in response.json()["Facets"]]

get_facet_by_name

get_facet_by_name(
    name: str | list[str],
    *,
    text: str | None = None,
    cas: list[Cas] | Cas | None = None,
    category: list[InventoryCategory]
    | InventoryCategory
    | None = None,
    company: list[Company] | Company | None = None,
    location: list[Location] | Location | None = None,
    storage_location: list[
        StorageLocation | StorageLocationFilter
    ]
    | StorageLocation
    | StorageLocationFilter
    | None = None,
    project_id: ProjectId | None = None,
    sheet_id: WorksheetId | None = None,
    created_by: list[User]
    | User
    | str
    | list[str]
    | None = None,
    lot_owner: list[User] | User | None = None,
    tags: list[str] | None = None,
    match_all_conditions: bool = False,
) -> list[FacetItem]

Return one or more named facets for an inventory search.

A convenience wrapper over get_all_facets that keeps only the facet group(s) you name. Useful for iterative search refinement, e.g. fetching the remaining Tags facet after other filters are applied.

Example

tags = client.inventory.get_facet_by_name("Tags", text="acetone")
tags[0].name
# 'Tags'

Parameters:

Name Type Description Default
name str or list[str]

The facet group name(s) to return (e.g. "Tags", "Company"). Matching is case-insensitive.

required
text str

Search text for full-text matching.

None
cas list[Cas] | Cas | None

Filter by CAS values.

None
category list[InventoryCategory] | InventoryCategory | None

Filter by inventory category.

None
company list[Company] | Company | None

Filter by company.

None
location list[Location] | Location | None

Filter by location.

None
storage_location list[StorageLocation | StorageLocationFilter] | StorageLocation | StorageLocationFilter | None

Filter by storage location.

None
project_id ProjectId | None

Filter by project.

None
sheet_id WorksheetId | None

Filter by worksheet.

None
created_by User, list[User], str, or list[str]

Filter by creator. Accepts user display name(s) or UserId(s) (e.g. "USR4227" or "Jane Doe"), or User object(s).

None
lot_owner list[User] | User | None

Filter by lot owner.

None
tags list[str] | None

Filter by tags.

None
match_all_conditions bool

If True, only count items that satisfy every applied filter (AND logic). Default False.

False

Returns:

Type Description
list[FacetItem]

The facet group(s) matching name.

Source code in src/albert/collections/inventory.py
@validate_call
def get_facet_by_name(
    self,
    name: str | list[str],
    *,
    text: str | None = None,
    cas: list[Cas] | Cas | None = None,
    category: list[InventoryCategory] | InventoryCategory | None = None,
    company: list[Company] | Company | None = None,
    location: list[Location] | Location | None = None,
    storage_location: list[StorageLocation | StorageLocationFilter]
    | StorageLocation
    | StorageLocationFilter
    | None = None,
    project_id: ProjectId | None = None,
    sheet_id: WorksheetId | None = None,
    created_by: list[User] | User | str | list[str] | None = None,
    lot_owner: list[User] | User | None = None,
    tags: list[str] | None = None,
    match_all_conditions: bool = False,
) -> list[FacetItem]:
    """Return one or more named facets for an inventory search.

    A convenience wrapper over [`get_all_facets`][albert.collections.inventory.InventoryCollection.get_all_facets] that keeps only the
    facet group(s) you name. Useful for iterative search refinement, e.g.
    fetching the remaining ``Tags`` facet after other filters are applied.

    !!! example
        ```python
        tags = client.inventory.get_facet_by_name("Tags", text="acetone")
        tags[0].name
        # 'Tags'
        ```

    Parameters
    ----------
    name : str or list[str]
        The facet group name(s) to return (e.g. ``"Tags"``, ``"Company"``).
        Matching is case-insensitive.
    text : str, optional
        Search text for full-text matching.
    cas : list[Cas] | Cas | None, optional
        Filter by CAS values.
    category : list[InventoryCategory] | InventoryCategory | None, optional
        Filter by inventory category.
    company : list[Company] | Company | None, optional
        Filter by company.
    location : list[Location] | Location | None, optional
        Filter by location.
    storage_location : list[StorageLocation | StorageLocationFilter] | StorageLocation | StorageLocationFilter | None, optional
        Filter by storage location.
    project_id : ProjectId | None, optional
        Filter by project.
    sheet_id : WorksheetId | None, optional
        Filter by worksheet.
    created_by : User, list[User], str, or list[str], optional
        Filter by creator. Accepts user display name(s) or UserId(s) (e.g.
        ``"USR4227"`` or ``"Jane Doe"``), or [`User`][albert.resources.users.User]
        object(s).
    lot_owner : list[User] | User | None, optional
        Filter by lot owner.
    tags : list[str] | None, optional
        Filter by tags.
    match_all_conditions : bool, optional
        If True, only count items that satisfy every applied filter (AND logic).
        Default False.

    Returns
    -------
    list[FacetItem]
        The facet group(s) matching ``name``.
    """
    name = ensure_list(name) or []

    facets = self.get_all_facets(
        text=text,
        cas=cas,
        category=category,
        company=company,
        location=location,
        storage_location=storage_location,
        project_id=project_id,
        sheet_id=sheet_id,
        created_by=created_by,
        lot_owner=lot_owner,
        tags=tags,
        match_all_conditions=match_all_conditions,
    )
    filtered_facets = []
    for facet in facets:
        if facet.name in name or facet.name.lower() in name:
            filtered_facets.append(facet)

    return filtered_facets

search

search(
    *,
    text: str | None = None,
    cas: list[Cas] | Cas | None = None,
    category: list[InventoryCategory]
    | InventoryCategory
    | None = None,
    company: list[Company] | Company | None = None,
    location: list[Location] | Location | None = None,
    storage_location: list[
        StorageLocation | StorageLocationFilter
    ]
    | StorageLocation
    | StorageLocationFilter
    | None = None,
    project_id: ProjectId | None = None,
    sheet_id: WorksheetId | None = None,
    created_by: list[User]
    | User
    | str
    | list[str]
    | None = None,
    lot_owner: list[User] | User | None = None,
    tags: list[str] | None = None,
    match_all_conditions: bool = False,
    order: OrderBy = DESCENDING,
    sort_by: str | None = None,
    max_items: int | None = None,
    offset: int | None = 0,
    from_created_at: str | None = None,
    to_created_at: str | None = None,
    updated_by: str | list[str] | None = None,
    from_updated_at: str | None = None,
    to_updated_at: str | None = None,
    albert_id: str | list[str] | None = None,
    attribute_id: str | list[str] | None = None,
    cas_smile: str | list[str] | None = None,
    collaborator_pop_up: bool | None = None,
    contains_field: str | list[str] | None = None,
    contains_text: str | list[str] | None = None,
    created_by_id: str | list[str] | None = None,
    details: bool | None = None,
    drop_down_text: str | None = None,
    drop_down_text_prop: str | None = None,
    dup_detection: bool | None = None,
    facet_field: str | None = None,
    facet_text: str | None = None,
    from_expiration_date: str | None = None,
    from_lot_created_at: str | None = None,
    from_on_hand: str | None = None,
    gslo_group: str | list[str] | None = None,
    idh: str | list[str] | None = None,
    is_pop_up: bool | None = None,
    lot_created_by: list[User]
    | User
    | str
    | list[str]
    | None = None,
    material_category: str | list[str] | None = None,
    pack_size: str | list[str] | None = None,
    pictogram_name: str | list[str] | None = None,
    result: str | list[str] | None = None,
    rsn: str | list[str] | None = None,
    source_field: str | list[str] | None = None,
    status: str | list[str] | None = None,
    sub_category: str | list[str] | None = None,
    synthesis_product_created: str
    | list[str]
    | None = None,
    to_expiration_date: str | None = None,
    to_lot_created_at: str | None = None,
    to_on_hand: str | None = None,
    metadata_filters: dict[str, Any] | None = None,
    custom_fields: dict[str, Any] | None = None,
    additional_field: str | list[str] | None = None,
    project_facets: dict[str, Any] | None = None,
    composite_search: dict[str, Any] | None = None,
) -> Iterator[InventorySearchItem]

Search for inventory items matching the given filters.

Returns lightweight, partially populated results and is the fastest way to look items up (best for name lookups, counts, or feeding IDs into another call). Fields such as full CAS breakdowns and metadata are omitted; when you need complete items, use get_all with the same filters, or pass the resulting IDs to get_by_ids.

Filters are combined with OR logic by default (an item matches if it satisfies any filter); set match_all_conditions=True to require every filter to match. Results are returned as a lazily paginated iterator, so iterating fetches additional pages on demand.

Example

from albert.resources.inventory import InventoryCategory
hits = client.inventory.search(
    text="acetone",
    category=InventoryCategory.RAW_MATERIALS,
    max_items=10,
)
first = next(iter(hits))
first.name
# 'Acetone'

Parameters:

Name Type Description Default
text str

Free-text query matched against item name, alias, and related fields. Only the first 50 characters are used.

None
cas Cas or list[Cas]

Filter by CAS number(s).

None
category InventoryCategory or list[InventoryCategory]

Filter by category: RawMaterials, Consumables, Equipment, or Formulas.

None
company Company or list[Company]

Filter by manufacturing Company.

None
location Location or list[Location]

Filter by location.

None
storage_location StorageLocation or StorageLocationFilter or list[StorageLocation | StorageLocationFilter]

Filter by storage location.

None
project_id str

Filter by the project a formula belongs to (Formula items only).

None
sheet_id str

Filter by worksheet ID.

None
created_by User, list[User], str, or list[str]

Filter by creator. Accepts user display name(s) or UserId(s) (e.g. "USR4227" or "Jane Doe"), or User object(s).

None
lot_owner User or list[User]

Filter by lot owner(s).

None
tags list[str]

Filter by tag name(s).

None
match_all_conditions bool

Require every filter to match (AND logic). Default False (OR logic).

False
order OrderBy

Sort direction. Default OrderBy.DESCENDING.

DESCENDING
sort_by str

Field to sort by. Default None (server default order).

None
max_items int

Maximum number of items to return in total. If None, iterates over all matches.

None
from_created_at str

Only include items created on or after this date, formatted as YYYY-MM-DD.

None
to_created_at str

Only include items created on or before this date, formatted as YYYY-MM-DD.

None
updated_by str or list[str]

Filter by user(s) who last updated the item. Accepts UserId(s) only (e.g. "USR4227"), not display names.

None
from_updated_at str

Only include items updated on or after this date (ISO 8601).

None
to_updated_at str

Only include items updated on or before this date (ISO 8601).

None
metadata_filters dict[str, Any]

Filter by custom field (metadata) values.

None
albert_id str or list[str]

Filter by Albert ID(s).

None
attribute_id str or list[str]

Filter by attribute ID(s). Cannot be combined with metadata_filters, custom_fields, additional_field, project_facets, or composite_search.

None
cas_smile str or list[str]

Filter by CAS SMILES string(s).

None
collaborator_pop_up bool

Apply collaborator popup search behavior.

None
contains_field str or list[str]

Field(s) for contains-style filtering.

None
contains_text str or list[str]

Text value(s) paired with contains_field.

None
created_by_id str or list[str]

Filter by creator UserId(s).

None
details bool

Invoke custom logic for the worksheet details view.

None
drop_down_text str

Dropdown search text.

None
drop_down_text_prop str

Dropdown search property name.

None
dup_detection bool

Enable duplicate-detection text sanitization.

None
facet_field str

Facet field to filter on.

None
facet_text str

Facet text to match.

None
from_expiration_date str

Only include lots expiring on or after this date (YYYY-MM-DD).

None
from_lot_created_at str

Only include lots created on or after this date (YYYY-MM-DD).

None
from_on_hand str

Minimum on-hand quantity filter.

None
gslo_group str or list[str]

Filter by GSLO group(s).

None
idh str or list[str]

Filter by IDH value(s).

None
is_pop_up bool

Apply popup search behavior.

None
lot_created_by User, list[User], str, or list[str]

Filter by lot creator. Accepts display name(s), UserId(s), or User object(s).

None
material_category str or list[str]

Filter by material category.

None
pack_size str or list[str]

Filter by pack size.

None
pictogram_name str or list[str]

Filter by pictogram name(s).

None
result str or list[str]

Filter by result value(s).

None
rsn str or list[str]

Filter by RSN value(s).

None
source_field str or list[str]

Restrict which fields are returned in search results.

None
status str or list[str]

Filter by status value(s).

None
sub_category str or list[str]

Filter by sub-category.

None
synthesis_product_created str or list[str]

Filter by synthesis product creation value(s).

None
to_expiration_date str

Only include lots expiring on or before this date (YYYY-MM-DD).

None
to_lot_created_at str

Only include lots created on or before this date (YYYY-MM-DD).

None
to_on_hand str

Maximum on-hand quantity filter.

None
custom_fields dict[str, Any]

Filter by custom field values.

None
additional_field str or list[str]

Request additional columns from the search index.

None
project_facets dict[str, Any]

Project facet filters.

None
composite_search dict[str, Any]

Composite search specification.

None

Returns:

Type Description
Iterator[InventorySearchItem]

A lazily paginated iterator of partially populated search results.

Source code in src/albert/collections/inventory.py
@validate_call
def search(
    self,
    *,
    text: str | None = None,
    cas: list[Cas] | Cas | None = None,
    category: list[InventoryCategory] | InventoryCategory | None = None,
    company: list[Company] | Company | None = None,
    location: list[Location] | Location | None = None,
    storage_location: list[StorageLocation | StorageLocationFilter]
    | StorageLocation
    | StorageLocationFilter
    | None = None,
    project_id: ProjectId | None = None,
    sheet_id: WorksheetId | None = None,
    created_by: list[User] | User | str | list[str] | None = None,
    lot_owner: list[User] | User | None = None,
    tags: list[str] | None = None,
    match_all_conditions: bool = False,
    order: OrderBy = OrderBy.DESCENDING,
    sort_by: str | None = None,
    max_items: int | None = None,
    offset: int | None = 0,
    from_created_at: str | None = None,
    to_created_at: str | None = None,
    updated_by: str | list[str] | None = None,
    from_updated_at: str | None = None,
    to_updated_at: str | None = None,
    albert_id: str | list[str] | None = None,
    attribute_id: str | list[str] | None = None,
    cas_smile: str | list[str] | None = None,
    collaborator_pop_up: bool | None = None,
    contains_field: str | list[str] | None = None,
    contains_text: str | list[str] | None = None,
    created_by_id: str | list[str] | None = None,
    details: bool | None = None,
    drop_down_text: str | None = None,
    drop_down_text_prop: str | None = None,
    dup_detection: bool | None = None,
    facet_field: str | None = None,
    facet_text: str | None = None,
    from_expiration_date: str | None = None,
    from_lot_created_at: str | None = None,
    from_on_hand: str | None = None,
    gslo_group: str | list[str] | None = None,
    idh: str | list[str] | None = None,
    is_pop_up: bool | None = None,
    lot_created_by: list[User] | User | str | list[str] | None = None,
    material_category: str | list[str] | None = None,
    pack_size: str | list[str] | None = None,
    pictogram_name: str | list[str] | None = None,
    result: str | list[str] | None = None,
    rsn: str | list[str] | None = None,
    source_field: str | list[str] | None = None,
    status: str | list[str] | None = None,
    sub_category: str | list[str] | None = None,
    synthesis_product_created: str | list[str] | None = None,
    to_expiration_date: str | None = None,
    to_lot_created_at: str | None = None,
    to_on_hand: str | None = None,
    metadata_filters: dict[str, Any] | None = None,
    custom_fields: dict[str, Any] | None = None,
    additional_field: str | list[str] | None = None,
    project_facets: dict[str, Any] | None = None,
    composite_search: dict[str, Any] | None = None,
) -> Iterator[InventorySearchItem]:
    """Search for inventory items matching the given filters.

    Returns lightweight, partially populated results and is the fastest way to
    look items up (best for name lookups, counts, or feeding IDs into another
    call). Fields such as full CAS breakdowns and metadata are omitted; when
    you need complete items, use [`get_all`][albert.collections.inventory.InventoryCollection.get_all] with the same filters, or pass
    the resulting IDs to [`get_by_ids`][albert.collections.inventory.InventoryCollection.get_by_ids].

    Filters are combined with OR logic by default (an item matches if it
    satisfies any filter); set ``match_all_conditions=True`` to require every
    filter to match. Results are returned as a lazily paginated iterator, so
    iterating fetches additional pages on demand.

    !!! example
        ```python
        from albert.resources.inventory import InventoryCategory
        hits = client.inventory.search(
            text="acetone",
            category=InventoryCategory.RAW_MATERIALS,
            max_items=10,
        )
        first = next(iter(hits))
        first.name
        # 'Acetone'
        ```

    Parameters
    ----------
    text : str, optional
        Free-text query matched against item name, alias, and related fields.
        Only the first 50 characters are used.
    cas : Cas or list[Cas], optional
        Filter by CAS number(s).
    category : InventoryCategory or list[InventoryCategory], optional
        Filter by category: ``RawMaterials``, ``Consumables``, ``Equipment``,
        or ``Formulas``.
    company : Company or list[Company], optional
        Filter by manufacturing Company.
    location : Location or list[Location], optional
        Filter by location.
    storage_location : StorageLocation or StorageLocationFilter or list[StorageLocation | StorageLocationFilter], optional
        Filter by storage location.
    project_id : str, optional
        Filter by the project a formula belongs to (Formula items only).
    sheet_id : str, optional
        Filter by worksheet ID.
    created_by : User, list[User], str, or list[str], optional
        Filter by creator. Accepts user display name(s) or UserId(s) (e.g.
        ``"USR4227"`` or ``"Jane Doe"``), or [`User`][albert.resources.users.User]
        object(s).
    lot_owner : User or list[User], optional
        Filter by lot owner(s).
    tags : list[str], optional
        Filter by tag name(s).
    match_all_conditions : bool, optional
        Require every filter to match (AND logic). Default False (OR logic).
    order : OrderBy, optional
        Sort direction. Default ``OrderBy.DESCENDING``.
    sort_by : str, optional
        Field to sort by. Default None (server default order).
    max_items : int, optional
        Maximum number of items to return in total. If None, iterates over all
        matches.
    from_created_at : str, optional
        Only include items created on or after this date, formatted as
        ``YYYY-MM-DD``.
    to_created_at : str, optional
        Only include items created on or before this date, formatted as
        ``YYYY-MM-DD``.
    updated_by : str or list[str], optional
        Filter by user(s) who last updated the item. Accepts UserId(s) only
        (e.g. ``"USR4227"``), not display names.
    from_updated_at : str, optional
        Only include items updated on or after this date (ISO 8601).
    to_updated_at : str, optional
        Only include items updated on or before this date (ISO 8601).
    metadata_filters : dict[str, Any], optional
        Filter by custom field (metadata) values.
    albert_id : str or list[str], optional
        Filter by Albert ID(s).
    attribute_id : str or list[str], optional
        Filter by attribute ID(s). Cannot be combined with ``metadata_filters``, ``custom_fields``, ``additional_field``, ``project_facets``, or ``composite_search``.
    cas_smile : str or list[str], optional
        Filter by CAS SMILES string(s).
    collaborator_pop_up : bool, optional
        Apply collaborator popup search behavior.
    contains_field : str or list[str], optional
        Field(s) for contains-style filtering.
    contains_text : str or list[str], optional
        Text value(s) paired with ``contains_field``.
    created_by_id : str or list[str], optional
        Filter by creator UserId(s).
    details : bool, optional
        Invoke custom logic for the worksheet details view.
    drop_down_text : str, optional
        Dropdown search text.
    drop_down_text_prop : str, optional
        Dropdown search property name.
    dup_detection : bool, optional
        Enable duplicate-detection text sanitization.
    facet_field : str, optional
        Facet field to filter on.
    facet_text : str, optional
        Facet text to match.
    from_expiration_date : str, optional
        Only include lots expiring on or after this date (``YYYY-MM-DD``).
    from_lot_created_at : str, optional
        Only include lots created on or after this date (``YYYY-MM-DD``).
    from_on_hand : str, optional
        Minimum on-hand quantity filter.
    gslo_group : str or list[str], optional
        Filter by GSLO group(s).
    idh : str or list[str], optional
        Filter by IDH value(s).
    is_pop_up : bool, optional
        Apply popup search behavior.
    lot_created_by : User, list[User], str, or list[str], optional
        Filter by lot creator. Accepts display name(s), UserId(s), or
        [`User`][albert.resources.users.User] object(s).
    material_category : str or list[str], optional
        Filter by material category.
    pack_size : str or list[str], optional
        Filter by pack size.
    pictogram_name : str or list[str], optional
        Filter by pictogram name(s).
    result : str or list[str], optional
        Filter by result value(s).
    rsn : str or list[str], optional
        Filter by RSN value(s).
    source_field : str or list[str], optional
        Restrict which fields are returned in search results.
    status : str or list[str], optional
        Filter by status value(s).
    sub_category : str or list[str], optional
        Filter by sub-category.
    synthesis_product_created : str or list[str], optional
        Filter by synthesis product creation value(s).
    to_expiration_date : str, optional
        Only include lots expiring on or before this date (``YYYY-MM-DD``).
    to_lot_created_at : str, optional
        Only include lots created on or before this date (``YYYY-MM-DD``).
    to_on_hand : str, optional
        Maximum on-hand quantity filter.
    custom_fields : dict[str, Any], optional
        Filter by custom field values.
    additional_field : str or list[str], optional
        Request additional columns from the search index.
    project_facets : dict[str, Any], optional
        Project facet filters.
    composite_search : dict[str, Any], optional
        Composite search specification.

    Returns
    -------
    Iterator[InventorySearchItem]
        A lazily paginated iterator of partially populated search results.
    """

    def deserialize(items: list[dict]):
        return [InventorySearchItem.model_validate(x)._bind_collection(self) for x in items]

    search_text = text if (text is None or len(text) < 50) else text[:50]

    query_params = self._prepare_parameters(
        text=search_text,
        cas=cas,
        category=category,
        company=company,
        order=order,
        sort_by=sort_by,
        location=location,
        storage_location=storage_location,
        project_id=project_id,
        sheet_id=sheet_id,
        created_by=created_by,
        lot_owner=lot_owner,
        tags=tags,
        offset=offset,
        from_created_at=from_created_at,
        to_created_at=to_created_at,
        updated_by=updated_by,
        from_updated_at=from_updated_at,
        to_updated_at=to_updated_at,
        albert_id=albert_id,
        attribute_id=attribute_id,
        cas_smile=cas_smile,
        collaborator_pop_up=collaborator_pop_up,
        contains_field=contains_field,
        contains_text=contains_text,
        created_by_id=created_by_id,
        details=details,
        drop_down_text=drop_down_text,
        drop_down_text_prop=drop_down_text_prop,
        dup_detection=dup_detection,
        facet_field=facet_field,
        facet_text=facet_text,
        from_expiration_date=from_expiration_date,
        from_lot_created_at=from_lot_created_at,
        from_on_hand=from_on_hand,
        gslo_group=gslo_group,
        idh=idh,
        is_pop_up=is_pop_up,
        lot_created_by=lot_created_by,
        material_category=material_category,
        pack_size=pack_size,
        pictogram_name=pictogram_name,
        result=result,
        rsn=rsn,
        source_field=source_field,
        status=status,
        sub_category=sub_category,
        synthesis_product_created=synthesis_product_created,
        to_expiration_date=to_expiration_date,
        to_lot_created_at=to_lot_created_at,
        to_on_hand=to_on_hand,
    )

    return self._paginate_inventory_search(
        deserialize=deserialize,
        query_params=query_params,
        match_all_conditions=match_all_conditions,
        max_items=max_items,
        metadata_filters=metadata_filters,
        custom_fields=custom_fields,
        additional_field=additional_field,
        project_facets=project_facets,
        composite_search=composite_search,
    )

get_all

get_all(
    *,
    text: str | None = None,
    cas: list[Cas] | Cas | None = None,
    category: list[InventoryCategory]
    | InventoryCategory
    | None = None,
    company: list[Company] | Company | None = None,
    location: list[Location] | Location | None = None,
    storage_location: list[
        StorageLocation | StorageLocationFilter
    ]
    | StorageLocation
    | StorageLocationFilter
    | None = None,
    project_id: ProjectId | None = None,
    sheet_id: WorksheetId | None = None,
    created_by: list[User]
    | User
    | str
    | list[str]
    | None = None,
    lot_owner: list[User] | User | None = None,
    tags: list[str] | None = None,
    match_all_conditions: bool = False,
    order: OrderBy = DESCENDING,
    sort_by: str | None = None,
    max_items: int | None = None,
    offset: int | None = 0,
    from_created_at: str | None = None,
    to_created_at: str | None = None,
    updated_by: str | list[str] | None = None,
    from_updated_at: str | None = None,
    to_updated_at: str | None = None,
    albert_id: str | list[str] | None = None,
    attribute_id: str | list[str] | None = None,
    cas_smile: str | list[str] | None = None,
    collaborator_pop_up: bool | None = None,
    contains_field: str | list[str] | None = None,
    contains_text: str | list[str] | None = None,
    created_by_id: str | list[str] | None = None,
    details: bool | None = None,
    drop_down_text: str | None = None,
    drop_down_text_prop: str | None = None,
    dup_detection: bool | None = None,
    facet_field: str | None = None,
    facet_text: str | None = None,
    from_expiration_date: str | None = None,
    from_lot_created_at: str | None = None,
    from_on_hand: str | None = None,
    gslo_group: str | list[str] | None = None,
    idh: str | list[str] | None = None,
    is_pop_up: bool | None = None,
    lot_created_by: list[User]
    | User
    | str
    | list[str]
    | None = None,
    material_category: str | list[str] | None = None,
    pack_size: str | list[str] | None = None,
    pictogram_name: str | list[str] | None = None,
    result: str | list[str] | None = None,
    rsn: str | list[str] | None = None,
    source_field: str | list[str] | None = None,
    status: str | list[str] | None = None,
    sub_category: str | list[str] | None = None,
    synthesis_product_created: str
    | list[str]
    | None = None,
    to_expiration_date: str | None = None,
    to_lot_created_at: str | None = None,
    to_on_hand: str | None = None,
    metadata_filters: dict[str, Any] | None = None,
    custom_fields: dict[str, Any] | None = None,
    additional_field: str | list[str] | None = None,
    project_facets: dict[str, Any] | None = None,
    composite_search: dict[str, Any] | None = None,
) -> Iterator[InventoryItem]

Get fully populated inventory items matching the given filters.

Accepts the same filters as search but returns complete InventoryItem entities rather than lightweight search results. This is slower because it fetches full detail for every match, so prefer search when you only need names, IDs, or counts.

Filters are combined with OR logic by default; set match_all_conditions=True to require every filter to match. Results are returned as a lazily paginated iterator.

Example

from albert.resources.inventory import InventoryCategory
for item in client.inventory.get_all(
    category=InventoryCategory.RAW_MATERIALS,
    max_items=50,
):
    print(item.id, item.name)

Parameters:

Name Type Description Default
text str

Free-text query matched against item name, alias, and related fields. Only the first 50 characters are used.

None
cas Cas or list[Cas]

Filter by CAS number(s).

None
category InventoryCategory or list[InventoryCategory]

Filter by category: RawMaterials, Consumables, Equipment, or Formulas.

None
company Company or list[Company]

Filter by manufacturing Company.

None
location Location or list[Location]

Filter by location.

None
storage_location StorageLocation or StorageLocationFilter or list[StorageLocation | StorageLocationFilter]

Filter by storage location.

None
project_id str

Filter by the project a formula belongs to (Formula items only).

None
sheet_id str

Filter by worksheet ID.

None
created_by User, list[User], str, or list[str]

Filter by creator. Accepts user display name(s) or UserId(s) (e.g. "USR4227" or "Jane Doe"), or User object(s).

None
lot_owner User or list[User]

Filter by lot owner(s).

None
tags list[str]

Filter by tag name(s).

None
match_all_conditions bool

Require every filter to match (AND logic). Default False (OR logic).

False
order OrderBy

Sort direction. Default OrderBy.DESCENDING.

DESCENDING
sort_by str

Field to sort by. Default None (server default order).

None
max_items int

Maximum number of items to return in total. If None, iterates over all matches.

None
from_created_at str

Only include items created on or after this date, formatted as YYYY-MM-DD.

None
to_created_at str

Only include items created on or before this date, formatted as YYYY-MM-DD.

None
updated_by str or list[str]

Filter by user(s) who last updated the item. Accepts UserId(s) only (e.g. "USR4227"), not display names.

None
from_updated_at str

Only include items updated on or after this date (ISO 8601).

None
to_updated_at str

Only include items updated on or before this date (ISO 8601).

None
metadata_filters dict[str, Any]

Filter by custom field (metadata) values.

None
albert_id str or list[str]

Filter by Albert ID(s).

None
attribute_id str or list[str]

Filter by attribute ID(s). Cannot be combined with metadata_filters, custom_fields, additional_field, project_facets, or composite_search.

None
cas_smile str or list[str]

Filter by CAS SMILES string(s).

None
collaborator_pop_up bool

Apply collaborator popup search behavior.

None
contains_field str or list[str]

Field(s) for contains-style filtering.

None
contains_text str or list[str]

Text value(s) paired with contains_field.

None
created_by_id str or list[str]

Filter by creator UserId(s).

None
details bool

Invoke custom logic for the worksheet details view.

None
drop_down_text str

Dropdown search text.

None
drop_down_text_prop str

Dropdown search property name.

None
dup_detection bool

Enable duplicate-detection text sanitization.

None
facet_field str

Facet field to filter on.

None
facet_text str

Facet text to match.

None
from_expiration_date str

Only include lots expiring on or after this date (YYYY-MM-DD).

None
from_lot_created_at str

Only include lots created on or after this date (YYYY-MM-DD).

None
from_on_hand str

Minimum on-hand quantity filter.

None
gslo_group str or list[str]

Filter by GSLO group(s).

None
idh str or list[str]

Filter by IDH value(s).

None
is_pop_up bool

Apply popup search behavior.

None
lot_created_by User, list[User], str, or list[str]

Filter by lot creator. Accepts display name(s), UserId(s), or User object(s).

None
material_category str or list[str]

Filter by material category.

None
pack_size str or list[str]

Filter by pack size.

None
pictogram_name str or list[str]

Filter by pictogram name(s).

None
result str or list[str]

Filter by result value(s).

None
rsn str or list[str]

Filter by RSN value(s).

None
source_field str or list[str]

Restrict which fields are returned in search results.

None
status str or list[str]

Filter by status value(s).

None
sub_category str or list[str]

Filter by sub-category.

None
synthesis_product_created str or list[str]

Filter by synthesis product creation value(s).

None
to_expiration_date str

Only include lots expiring on or before this date (YYYY-MM-DD).

None
to_lot_created_at str

Only include lots created on or before this date (YYYY-MM-DD).

None
to_on_hand str

Maximum on-hand quantity filter.

None
custom_fields dict[str, Any]

Filter by custom field values.

None
additional_field str or list[str]

Request additional columns from the search index.

None
project_facets dict[str, Any]

Project facet filters.

None
composite_search dict[str, Any]

Composite search specification.

None

Returns:

Type Description
Iterator[InventoryItem]

A lazily paginated iterator of fully populated items.

Source code in src/albert/collections/inventory.py
@validate_call
def get_all(
    self,
    *,
    text: str | None = None,
    cas: list[Cas] | Cas | None = None,
    category: list[InventoryCategory] | InventoryCategory | None = None,
    company: list[Company] | Company | None = None,
    location: list[Location] | Location | None = None,
    storage_location: list[StorageLocation | StorageLocationFilter]
    | StorageLocation
    | StorageLocationFilter
    | None = None,
    project_id: ProjectId | None = None,
    sheet_id: WorksheetId | None = None,
    created_by: list[User] | User | str | list[str] | None = None,
    lot_owner: list[User] | User | None = None,
    tags: list[str] | None = None,
    match_all_conditions: bool = False,
    order: OrderBy = OrderBy.DESCENDING,
    sort_by: str | None = None,
    max_items: int | None = None,
    offset: int | None = 0,
    from_created_at: str | None = None,
    to_created_at: str | None = None,
    updated_by: str | list[str] | None = None,
    from_updated_at: str | None = None,
    to_updated_at: str | None = None,
    albert_id: str | list[str] | None = None,
    attribute_id: str | list[str] | None = None,
    cas_smile: str | list[str] | None = None,
    collaborator_pop_up: bool | None = None,
    contains_field: str | list[str] | None = None,
    contains_text: str | list[str] | None = None,
    created_by_id: str | list[str] | None = None,
    details: bool | None = None,
    drop_down_text: str | None = None,
    drop_down_text_prop: str | None = None,
    dup_detection: bool | None = None,
    facet_field: str | None = None,
    facet_text: str | None = None,
    from_expiration_date: str | None = None,
    from_lot_created_at: str | None = None,
    from_on_hand: str | None = None,
    gslo_group: str | list[str] | None = None,
    idh: str | list[str] | None = None,
    is_pop_up: bool | None = None,
    lot_created_by: list[User] | User | str | list[str] | None = None,
    material_category: str | list[str] | None = None,
    pack_size: str | list[str] | None = None,
    pictogram_name: str | list[str] | None = None,
    result: str | list[str] | None = None,
    rsn: str | list[str] | None = None,
    source_field: str | list[str] | None = None,
    status: str | list[str] | None = None,
    sub_category: str | list[str] | None = None,
    synthesis_product_created: str | list[str] | None = None,
    to_expiration_date: str | None = None,
    to_lot_created_at: str | None = None,
    to_on_hand: str | None = None,
    metadata_filters: dict[str, Any] | None = None,
    custom_fields: dict[str, Any] | None = None,
    additional_field: str | list[str] | None = None,
    project_facets: dict[str, Any] | None = None,
    composite_search: dict[str, Any] | None = None,
) -> Iterator[InventoryItem]:
    """Get fully populated inventory items matching the given filters.

    Accepts the same filters as [`search`][albert.collections.inventory.InventoryCollection.search] but returns complete
    ``InventoryItem`` entities rather than lightweight search results. This is
    slower because it fetches full detail for every match, so prefer
    [`search`][albert.collections.inventory.InventoryCollection.search] when you only need names, IDs, or counts.

    Filters are combined with OR logic by default; set
    ``match_all_conditions=True`` to require every filter to match. Results are
    returned as a lazily paginated iterator.

    !!! example
        ```python
        from albert.resources.inventory import InventoryCategory
        for item in client.inventory.get_all(
            category=InventoryCategory.RAW_MATERIALS,
            max_items=50,
        ):
            print(item.id, item.name)
        ```

    Parameters
    ----------
    text : str, optional
        Free-text query matched against item name, alias, and related fields.
        Only the first 50 characters are used.
    cas : Cas or list[Cas], optional
        Filter by CAS number(s).
    category : InventoryCategory or list[InventoryCategory], optional
        Filter by category: ``RawMaterials``, ``Consumables``, ``Equipment``,
        or ``Formulas``.
    company : Company or list[Company], optional
        Filter by manufacturing Company.
    location : Location or list[Location], optional
        Filter by location.
    storage_location : StorageLocation or StorageLocationFilter or list[StorageLocation | StorageLocationFilter], optional
        Filter by storage location.
    project_id : str, optional
        Filter by the project a formula belongs to (Formula items only).
    sheet_id : str, optional
        Filter by worksheet ID.
    created_by : User, list[User], str, or list[str], optional
        Filter by creator. Accepts user display name(s) or UserId(s) (e.g.
        ``"USR4227"`` or ``"Jane Doe"``), or [`User`][albert.resources.users.User]
        object(s).
    lot_owner : User or list[User], optional
        Filter by lot owner(s).
    tags : list[str], optional
        Filter by tag name(s).
    match_all_conditions : bool, optional
        Require every filter to match (AND logic). Default False (OR logic).
    order : OrderBy, optional
        Sort direction. Default ``OrderBy.DESCENDING``.
    sort_by : str, optional
        Field to sort by. Default None (server default order).
    max_items : int, optional
        Maximum number of items to return in total. If None, iterates over all
        matches.
    from_created_at : str, optional
        Only include items created on or after this date, formatted as
        ``YYYY-MM-DD``.
    to_created_at : str, optional
        Only include items created on or before this date, formatted as
        ``YYYY-MM-DD``.
    updated_by : str or list[str], optional
        Filter by user(s) who last updated the item. Accepts UserId(s) only
        (e.g. ``"USR4227"``), not display names.
    from_updated_at : str, optional
        Only include items updated on or after this date (ISO 8601).
    to_updated_at : str, optional
        Only include items updated on or before this date (ISO 8601).
    metadata_filters : dict[str, Any], optional
        Filter by custom field (metadata) values.
    albert_id : str or list[str], optional
        Filter by Albert ID(s).
    attribute_id : str or list[str], optional
        Filter by attribute ID(s). Cannot be combined with ``metadata_filters``, ``custom_fields``, ``additional_field``, ``project_facets``, or ``composite_search``.
    cas_smile : str or list[str], optional
        Filter by CAS SMILES string(s).
    collaborator_pop_up : bool, optional
        Apply collaborator popup search behavior.
    contains_field : str or list[str], optional
        Field(s) for contains-style filtering.
    contains_text : str or list[str], optional
        Text value(s) paired with ``contains_field``.
    created_by_id : str or list[str], optional
        Filter by creator UserId(s).
    details : bool, optional
        Invoke custom logic for the worksheet details view.
    drop_down_text : str, optional
        Dropdown search text.
    drop_down_text_prop : str, optional
        Dropdown search property name.
    dup_detection : bool, optional
        Enable duplicate-detection text sanitization.
    facet_field : str, optional
        Facet field to filter on.
    facet_text : str, optional
        Facet text to match.
    from_expiration_date : str, optional
        Only include lots expiring on or after this date (``YYYY-MM-DD``).
    from_lot_created_at : str, optional
        Only include lots created on or after this date (``YYYY-MM-DD``).
    from_on_hand : str, optional
        Minimum on-hand quantity filter.
    gslo_group : str or list[str], optional
        Filter by GSLO group(s).
    idh : str or list[str], optional
        Filter by IDH value(s).
    is_pop_up : bool, optional
        Apply popup search behavior.
    lot_created_by : User, list[User], str, or list[str], optional
        Filter by lot creator. Accepts display name(s), UserId(s), or
        [`User`][albert.resources.users.User] object(s).
    material_category : str or list[str], optional
        Filter by material category.
    pack_size : str or list[str], optional
        Filter by pack size.
    pictogram_name : str or list[str], optional
        Filter by pictogram name(s).
    result : str or list[str], optional
        Filter by result value(s).
    rsn : str or list[str], optional
        Filter by RSN value(s).
    source_field : str or list[str], optional
        Restrict which fields are returned in search results.
    status : str or list[str], optional
        Filter by status value(s).
    sub_category : str or list[str], optional
        Filter by sub-category.
    synthesis_product_created : str or list[str], optional
        Filter by synthesis product creation value(s).
    to_expiration_date : str, optional
        Only include lots expiring on or before this date (``YYYY-MM-DD``).
    to_lot_created_at : str, optional
        Only include lots created on or before this date (``YYYY-MM-DD``).
    to_on_hand : str, optional
        Maximum on-hand quantity filter.
    custom_fields : dict[str, Any], optional
        Filter by custom field values.
    additional_field : str or list[str], optional
        Request additional columns from the search index.
    project_facets : dict[str, Any], optional
        Project facet filters.
    composite_search : dict[str, Any], optional
        Composite search specification.

    Returns
    -------
    Iterator[InventoryItem]
        A lazily paginated iterator of fully populated items.
    """

    def deserialize(items: list[dict]) -> list[InventoryItem]:
        return self.get_by_ids(ids=[x["albertId"] for x in items])

    search_text = text if (text is None or len(text) < 50) else text[:50]

    query_params = self._prepare_parameters(
        text=search_text,
        cas=cas,
        category=category,
        company=company,
        order=order,
        sort_by=sort_by,
        location=location,
        storage_location=storage_location,
        project_id=project_id,
        sheet_id=sheet_id,
        created_by=created_by,
        lot_owner=lot_owner,
        tags=tags,
        offset=offset,
        from_created_at=from_created_at,
        to_created_at=to_created_at,
        updated_by=updated_by,
        from_updated_at=from_updated_at,
        to_updated_at=to_updated_at,
        albert_id=albert_id,
        attribute_id=attribute_id,
        cas_smile=cas_smile,
        collaborator_pop_up=collaborator_pop_up,
        contains_field=contains_field,
        contains_text=contains_text,
        created_by_id=created_by_id,
        details=details,
        drop_down_text=drop_down_text,
        drop_down_text_prop=drop_down_text_prop,
        dup_detection=dup_detection,
        facet_field=facet_field,
        facet_text=facet_text,
        from_expiration_date=from_expiration_date,
        from_lot_created_at=from_lot_created_at,
        from_on_hand=from_on_hand,
        gslo_group=gslo_group,
        idh=idh,
        is_pop_up=is_pop_up,
        lot_created_by=lot_created_by,
        material_category=material_category,
        pack_size=pack_size,
        pictogram_name=pictogram_name,
        result=result,
        rsn=rsn,
        source_field=source_field,
        status=status,
        sub_category=sub_category,
        synthesis_product_created=synthesis_product_created,
        to_expiration_date=to_expiration_date,
        to_lot_created_at=to_lot_created_at,
        to_on_hand=to_on_hand,
    )

    return self._paginate_inventory_search(
        deserialize=deserialize,
        query_params=query_params,
        match_all_conditions=match_all_conditions,
        max_items=max_items,
        metadata_filters=metadata_filters,
        custom_fields=custom_fields,
        additional_field=additional_field,
        project_facets=project_facets,
        composite_search=composite_search,
    )

update

update(*, inventory_item: InventoryItem) -> InventoryItem

Update an existing inventory item.

Fetch the item (e.g. with get_by_id), modify the updatable fields on the returned object, then pass it here. Only the fields listed in Notes are applied; changes to other fields are ignored.

Example

item = client.inventory.get_by_id(id="INVA9999999")
item.description = "Updated description"
updated = client.inventory.update(inventory_item=item)
updated.description
# 'Updated description'

Parameters:

Name Type Description Default
inventory_item InventoryItem

The item to update. Must have a valid id.

required

Returns:

Type Description
InventoryItem

The updated item.

Notes

The following fields can be updated: alias, description, is_formula_override, metadata, name, security_class, unit_category. On individual CAS entries (via cas): min, max, target, cas_category, inventory_function. substance_id can be set when adding a new CAS entry; it is not patchable on existing entries.

Source code in src/albert/collections/inventory.py
def update(self, *, inventory_item: InventoryItem) -> InventoryItem:
    """Update an existing inventory item.

    Fetch the item (e.g. with [`get_by_id`][albert.collections.inventory.InventoryCollection.get_by_id]), modify the updatable fields
    on the returned object, then pass it here. Only the fields listed in Notes
    are applied; changes to other fields are ignored.

    !!! example
        ```python
        item = client.inventory.get_by_id(id="INVA9999999")
        item.description = "Updated description"
        updated = client.inventory.update(inventory_item=item)
        updated.description
        # 'Updated description'
        ```

    Parameters
    ----------
    inventory_item : InventoryItem
        The item to update. Must have a valid ``id``.

    Returns
    -------
    InventoryItem
        The updated item.

    Notes
    -----
    The following fields can be updated: ``alias``, ``description``,
    ``is_formula_override``, ``metadata``, ``name``, ``security_class``,
    ``unit_category``.
    On individual CAS entries (via ``cas``): ``min``, ``max``, ``target``,
    ``cas_category``, ``inventory_function``.
    ``substance_id`` can be set when adding a new CAS entry; it is not
    patchable on existing entries.
    """
    # Fetch the current object state from the server or database
    current_object = self.get_by_id(id=inventory_item.id)
    # Generate the PATCH payload
    patch_payload = self._generate_inventory_patch_payload(
        existing=current_object, updated=inventory_item
    )

    # Complex patching does not work for some fields, so I'm going to do this in a loop :(
    # https://teams.microsoft.com/l/message/19:de4a48c366664ce1bafcdbea02298810@thread.tacv2/1724856117312?tenantId=98aab90e-764b-48f1-afaa-02e3c7300653&groupId=35a36a3d-fc25-4899-a1dd-ad9c7d77b5b3&parentMessageId=1724856117312&teamName=Product%20%2B%20Engineering&channelName=General%20-%20API&createdTime=1724856117312
    url = f"{self.base_path}/{inventory_item.id}"
    batch_patch_changes = list()
    for change in patch_payload["data"]:
        if change["attribute"].startswith("Metadata."):  # Metadata can be batch patched
            batch_patch_changes.append(change)
        else:
            change_payload = {"data": [change]}
            self.session.patch(url, json=change_payload)

    # Use batch update for fields that allow it
    if batch_patch_changes:
        batch_patch_payload = {"data": batch_patch_changes}
        self.session.patch(url, json=batch_patch_payload)

    updated_inv = self.get_by_id(id=inventory_item.id)
    return updated_inv