> ## Documentation Index
> Fetch the complete documentation index at: https://help.cryptolens.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Python 3: Product

> List products and search their license keys.

Python 3 package `licensing 0.54`. [Reviewed source revision](https://github.com/Cryptolens/cryptolens-python/tree/8f38b5a37580f2aba1c9374f581cdc85e251950f).

```python theme={null}
from licensing.methods import Product
```

<a id="licensing.methods.Product.get_products" />

## get\_products

Retrieve the products accessible to the access token.

```python theme={null}
Product.get_products(token)
```

### Parameters

| Parameter | Default | Meaning |
| - | - | - |
| `token` | Required | Your access token. It needs the permission required by the linked Web API method. |

### Result

Returns `(products, '')`, where `products` is a list of dictionaries.

Returns `(None, message)` for API rejection or a handled request failure. Malformed response data can raise an exception.

### Checks

The API checks the access token's permissions. This method does not verify a signed license response.

[Web API reference](/api-reference/generated/GetProducts).

<a id="licensing.methods.Product.get_keys" />

## get\_keys

Search a product's license keys and retrieve one page of results.

```python theme={null}
Product.get_keys(token, product_id, page=1, order_by='ID ascending', search_query='')
```

### Parameters

| Parameter | Default | Meaning |
| - | - | - |
| `token` | Required | Your access token. It needs the permission required by the linked Web API method. |
| `product_id` | Required | The numeric product ID. |
| `page` | `1` | The page number, starting at 1. |
| `order_by` | `'ID ascending'` | The API's ordering expression. |
| `search_query` | `''` | The dashboard search expression to apply to license keys. |

### Result

Returns `(license_keys, '', pagination)`. `license_keys` is a list of dictionaries, not `LicenseKey` objects. `pagination` is a dictionary with `returned`, `total` and `pageCount`.

Returns a two-item `(None, message)` tuple on API rejection or a handled request failure. Check the first item before reading pagination. Malformed response data can raise an exception.

### Checks

The API checks access permissions. These results are not signed license objects for offline verification.

[Web API reference](/api-reference/generated/GetKeys).

### Example

This counts licenses created in the 2026 UTC calendar year for example product 3349. Replace the access token and product ID; the access token needs the **GetKeys permission**.

```python theme={null}
result = Product.get_keys(
    token="YOUR_ACCESS_TOKEN", product_id=3349,
    search_query="created >= DateTime(2026,1,1) and created < DateTime(2027,1,1)")
if result[0] is None:
    print("Search failed: {0}".format(result[1]))
else:
    print("Matching licenses: {0}".format(result[2]["total"]))
```

## FAQ

<AccordionGroup>
  <Accordion title="Why Should I Use the Total Instead of Counting the List?" id="why-should-i-use-the-total-instead-of-counting-the-list">
    The list contains only the requested page. `result[2]["total"]` is the number matching the filter across all pages of that product. `returned` is the number on the current page. For several products, sum their filtered totals.
  </Accordion>
</AccordionGroup>

[Python SDK Reference](/libraries/python-api)
