# Python UDFs

A Python User-Defined Function (UDF) is an external function that allows you to execute Python code outside of the SingleStore engine's process. It enables you to extend SingleStore with custom Python logic in a SingleStore Notebook. Python UDFs are especially useful when you need to integrate with AI applications, machine learning (ML) models and perform vector operations with libraries like NumPy, Pandas, Polars, or call external APIs.

## Prerequisites

To enable Python UDFs in the SingleStore cluster, ensure the following:

* **SingleStore Version**: SingleStore version 8.9 or later.
* **Environment**: The SingleStore cluster must run in an AWS EKS IRSA-supported environment.
* **Cluster**: Python UDFs require a cluster group that supports Python UDFs. The Cloud Portal validates support for the selected cluster group while publishing Python UDF.

After ensuring, contact [SingleStore Support](http://support.singlestore.com) to enable this feature for your organization.

## Publish a Python UDF

## Create a Python UDF

Python UDFs can be created in Shared notebook only.

To create a new Python UDF, perform the following steps:

1. In the left navigation, select **Editor > Shared**.

2. Create or open a shared notebook.

3. Select **Publish** (on the top right).

## New Python UDF

After selecting **Publish**, a new dialog box appears.

**Publish Settings**

| **Publish as**   | Select**Python UDF**.                                                                                                                                                              |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Name**         | Enter a name of the Python UDF.                                                                                                                                                    |
| **Description**  | Enter the Python UDF description.                                                                                                                                                  |
| **Notebook**     | Select a shared notebook to publish as a Python UDF. The shared notebook is pre-selected when the Python UDF is published through notebooks.                                       |
| **Deployment**   | Select theSingleStoreclusterthe notebook connects to.Selecting aclusterallows connecting to theSingleStoredatabases referenced in the notebook natively.                           |
| **Runtime**      | Select an available runtime. The available options depend on yourclusterconfiguration.<blockquote> <p><strong>📝 Note</strong>: <p>This field is in preview.</p></p> </blockquote> |
| **Region**       | Select a region.                                                                                                                                                                   |
| **Idle Timeout** | Select an idle timeout.                                                                                                                                                            |

Select **Next**.

Select **Publish** to publish the notebook as Python UDF. Once the Python UDF is published, call the function using SQL Editor.

> **📝 Note**: A notebook can have one active Python UDF publication at a time. Function names registered in the selected database must not conflict with functions from another Python UDF. To modify an existing Python UDF, use **Update**.

## Example Notebook

The following notebook shows how to publish your first Python UDF:

## Manage an Existing Python UDF

> **📝 Note**: You can manage a Python UDF only if you have the Owner role on it. Users with the User role can view and invoke the Python UDF but cannot update, delete, or share it. Refer to [Access Control for Python UDFs](https://docs.singlestore.com/#section-id235777399423477.md) for more information.

To view an existing Python UDF, select **Python UDFs** in the left navigation. Existing Python UDFs can be managed by performing the following actions:

* View
* Update
* Delete

## View an Existing Python UDF

To view an existing Python UDF, select the Python UDF from the **Name** column. The following actions can be performed for a Python UDF from this page:

* View Live Logs
* Update
* Delete

## View Live Logs

To view live logs of the selected Python UDF, select **View Live Logs** from the ellipsis on the right side. A new window appears, where the **Timestamp** and the message in the **Body** column can be viewed. View the **Log JSON** by selecting the eye icon.

## Update an Existing Python UDF

To update an existing Python UDF, select the ellipsis in the Actions column of the Python UDF, and select **Update**.

## Delete an Existing Python UDF

To delete an existing Python UDF, select the ellipsis in the **Actions** column of the Python UDF, and select **Delete**.

## Access Control for Python UDFs

Python UDFs are governed by the [Container Services RBAC](https://docs.singlestore.com/cloud/container-services/container-services-rbac.md) model. Each Python UDF has a backing Aura App resource, and access is controlled through Aura App roles.

## Who Can Create a Python UDF

Creating a Python UDF requires an organization-level role that grants the Aura Apps create permission (for example, the **Aura Creator** role). Organization members without this permission cannot create Python UDFs.

When a user creates a Python UDF, the system automatically grants the user the Owner role on the new Python UDF.

## Roles and Permissions

| **Role**  | **Permissions**                                                                                                                           |
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| **Owner** | <ul> <li>Use</li> <li>Update</li> <li>Delete</li> <li>Share (Control Access)</li> <li>Create API Keys</li> <li>Revoke API Keys</li> </ul> |
| **User**  | <ul> <li>Use (view + invoke)</li> <li>Create API Keys</li> </ul>                                                                          |

## Visibility

Users can view the Python UDFs for which they have **Owner** or **User** access. Python UDFs cannot be viewed without an explicit role grant in the Cloud Portal list or in Management API responses.

## Organization Role Inheritance

The following organization roles automatically inherit full Owner permissions on all Python UDFs in the organization:

* Organization Owner
* AI Administrator

Other organization roles (Reader, Writer, Operator, Observer, Billing Administrator, and User Administrator) do not inherit Python UDF access. Users with these roles need an explicit Owner or User grant on a specific Python UDF to view or use it.

Refer to [Container Services RBAC](https://docs.singlestore.com/cloud/container-services/container-services-rbac.md) for more information.

## Share Access to a Python UDF

Owners can share a Python UDF with other users or teams from the resource's action menu.

1. Navigate to **Python UDFs** in the left navigation.

2. Select the ellipsis (three dots) in the **Actions** column of the target Python UDF.

3. Select **Share**.

4. Select the user or team, assign the **Owner** or **User** role, and select **Save**.

To remove access, select **Remove Access** for the user or team.

## Guarded Actions

| Action                | Required Role |
| --------------------- | ------------- |
| View / List           | Owner or User |
| Invoke (Cloud Portal) | Owner or User |
| Update                | Owner         |
| Delete                | Owner         |
| Share / Manage Access | Owner         |
| Create API Key        | Owner or User |
| Revoke API Key        | Owner         |

> **📝 Note**: SQL invocation is separate from Aura App RBAC. Invoking a Python UDF from SQL is not governed by the Aura App roles above. Standard database-level grants control SQL invocation:- `EXECUTE` on the function object
> - `OUTBOUND` global permission for UDFs that make external service callsRefer to [User Permissions](https://docs.singlestore.com/cloud/container-services/python-udfs/working-with-python-udfs/#section-id235163222933409.md) for more information on SQL-level permission.

## Status of Python UDF

| Status       | Description                                                                                                                             |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| Initializing | The Python UDF is being created, updated, or restarted.                                                                                 |
| Active       | The Python UDF is available and accepting requests.                                                                                     |
| Idle         | The Python UDF is not currently running and may cold start on the next request.                                                         |
| Failed       | The notebook code or UDF definition failed validation or execution. Use the snapshot and logs to debug.                                 |
| Error        | A service or infrastructure issue prevented the Python UDF from becoming available. Try again or contact Support if the issue persists. |
| Deleting     | The Python UDF is being deleted.                                                                                                        |
| Deleted      | The Python UDF has been deleted.                                                                                                        |

## Troubleshoot Python UDFs

SingleStore automatically saves a snapshot of the notebook for each failed execution. Open **Python UDFs**, select the Python UDF, then use the detail page to view logs or open the notebook snapshot for debugging.

| Error                        | Solution                                                                                                |
| ---------------------------- | ------------------------------------------------------------------------------------------------------- |
| No functions found           | Ensure the notebook defines at least one valid Python UDF function decorated with`@udf`.                |
| Too many functions found     | Reduce the number of Python UDF functions in the notebook to 10 or fewer and try again.                 |
| UDF server unreachable       | Ensure the notebook starts the UDF app with`connection_info = await apps.run_udf_app()`.                |
| Duplicate UDF Function Name  | Rename conflicting functions or remove the existing conflicting Python UDF.                             |
| Database Dropped             | Update the Python UDF and select an existing database.                                                  |
| Old Database Detached        | Reattach the previous database if stale functions need to be removed, or contact Support.               |
| ClusterDeleted               | Update the Python UDF and select a differentcluster.                                                    |
| ClusterSuspended             | Resume theclusteror create a new Python UDF with a differentcluster.                                    |
| Notebook Deleted/Not Present | Create a new Python UDF with a different notebook.                                                      |
| Internal Errors/Misc         | Contact[SingleStoreSupport](http://support.singlestore.com/)or use the chat feature in theCloud Portal. |

## Defining Python UDFs

Each Python UDF must meet the following requirements:

1. The function's parameters and return types must be annotated.

2. The function must be wrapped with the `@udf` decorator, which is located in `singlestoredb.functions`.

The `@udf` decorator is a critical component, as it automatically analyzes the type annotations to map Python data types to SingleStore data types. The mapping is subsequently used to generate the necessary `CREATE EXTERNAL FUNCTION` statement in the SingleStore database, ensuring a reliable connection between the Python code and the SQL queries. Refer to [Equivalent Data Types](https://docs.singlestore.com/cloud/container-services/python-udfs/working-with-python-udfs/#section-id235163203492027.md) for related information.

There are two main types of Python UDFs, defined by the type annotations:

* Scalar
* Vectorized

## Scalar Python UDFs

Scalar Python UDFs are defined with standard Python type annotations, such as `int`, `float`, or `str`. When called from the database, the Python UDF server receives a batch of rows, but the Python UDF itself is invoked once for each individual row of data. This is useful in complex logic with individual records.

The following example demonstrates a scalar Python UDF:

```python
from singlestoredb.functions import udf
import singlestoredb.apps as apps

@udf
async def multiply(x: float, y: float) -> float:
    return x * y

# Start Python UDF server
connection_info = await apps.run_udf_app()
print("UDF server running. Connection info:", connection_info)
```

This creates the following external function:

```sql
CREATE OR REPLACE EXTERNAL FUNCTION `multiply`(
    `x` DOUBLE NOT NULL,
    `y` DOUBLE NOT NULL
)
RETURNS DOUBLE NOT NULL
DEFINER = '<function_owner>'@'%'
AS MANAGED SERVICE '<managed_service_url>'
FORMAT ROWDAT_1;
```

> **📝 Note**: The `DEFINER` value and `AS MANAGED SERVICE URL` are generated automatically when you publish the function. The `DEFINER` identifies the internal service identity that owns the published function. The URL points to the region-local Python UDF endpoint for the function. These values vary by function, publisher, and region.

Use `async def` for improved cancellation handling.

Invoke the scalar Python UDF using the following command:

```sql
SELECT multiply(5.0, 10.0) AS result;

```

```output

+-----------------------+
| result                |
+-----------------------+
| 50.0                  |
+-----------------------+
```

## Vectorized Python UDFs

Vectorized Python UDFs are defined with vector type annotations, such as `numpy.ndarray`, `pandas.Series`, `polars.Series`, or `pyarrow.Array`. The Python UDF is called only once for each batch of rows received from the SingleStore database. The entire batch is converted into vectorized inputs, where each column of data corresponds to a single vector object passed as a function parameter. This is useful in high-performance numerical processing.

The following example demonstrates a vectorized Python UDF.

```python
import numpy as np
import numpy.typing as npt
from singlestoredb.functions import udf

@udf
async def vec_multiply(
      x: npt.NDArray[np.float64],
      y: npt.NDArray[np.float64]
    ) -> npt.NDArray[np.float64]:
    return x * y

# Start Python UDF server

import singlestoredb.apps as apps
connection_info = await apps.run_udf_app()
```

This creates the following external function:

```sql
CREATE OR REPLACE EXTERNAL FUNCTION `vec_multiply`(
    `x` DOUBLE NOT NULL,
    `y` DOUBLE NOT NULL
)
RETURNS DOUBLE NOT NULL
DEFINER = '<function_owner>'@'%'
AS MANAGED SERVICE '<managed_service_url>'
FORMAT ROWDAT_1;

```

Invoke the vectorized Python UDF using the following command:

```sql
SELECT vec_multiply(vec_col1, vec_col2)AS result;

```

```output

+-------------------+
| result            |
+-------------------+
| [4.0, 10.0, 18.0] |
+-------------------+
```

The function processes all rows in a batch simultaneously. Each row receives its own scalar result, for example, if `col1 = 2.0` and `col2 = 5.0` for a given row, the result for that row is `10.0`.

## Scalar Python TVFs

Scalar Python TVFs are defined in the same way as scalar Python UDFs, except that a scalar TVF uses a `Table` annotation to indicate that the function returns a table. The function must also return the final result wrapped in a `Table` object.

The following example demonstrates a scalar Python TVF:

```python
from singlestoredb.functions import udf, Table

@udf
async def number_stats(n: int) -> Table[list[int], list[int], list[float]]:
    numbers = list(range(1, n + 1))
    squares = [x ** 2 for x in numbers]
    roots = [round(x ** 0.5, 2) for x in numbers]
    return Table(numbers, squares, roots)

# Start Python UDF server
import singlestoredb.apps as apps
connection_info = await apps.run_udf_app()
```

This creates the following external function:

```sql
CREATE OR REPLACE EXTERNAL FUNCTION `number_stats`(
    `n` BIGINT NOT NULL
)
RETURNS TABLE(
    `numbers` BIGINT NOT NULL,
    `squares` BIGINT NOT NULL,
    `roots` DOUBLE NOT NULL
)
DEFINER = '<function_owner>'@'%'
AS MANAGED SERVICE '<managed_service_url>'
FORMAT ROWDAT_1;
```

Invoke the scalar Python TVF using the following command:

```sql
SELECT * FROM number_stats([5]);

```

```output

+---------+---------+-------+
| numbers | squares | roots |
+---------+---------+-------+
| 1       | 1       | 1.00  |
| 2       | 4       | 1.41  |
| 3       | 9       | 1.73  |
| 4       | 16      | 2.00  |
| 5       | 25      | 2.24  |
+---------+---------+-------+

```

SingleStore automatically generates generic column names if no names are associated with the return fields (`numbers`, `squares`, and `roots` in this example). Explicitly name result columns using overrides or schema classes. Refer to [Overriding Parameters and Return Value Types](https://docs.singlestore.com/cloud/container-services/python-udfs/working-with-python-udfs/#section-id235163211595581.md) for related information.

## Vectorized Python TVFs

Vectorized Python TVFs return a table of results. Unlike Python UDFs, which are invoked for each row in a query, a TVF is invoked once with a set of parameters and returns multiple rows and columns. In a vectorized Python TVF, each returned vector represents a column in the output table. The length of each vector determines the number of rows returned.

The following example demonstrates a vectorized Python TVF:

```python
import numpy as np
import numpy.typing as npt
from singlestoredb.functions import udf, Table

@udf
async def vec_table_function(
    n: npt.NDArray[np.int_],
) -> Table[npt.NDArray[np.int_], npt.NDArray[np.float64], npt.NDArray[np.str_]]:
    x = np.array([10] * n[0], dtype=np.int_)
    y = np.array([10.0] * n[0], dtype=np.float64)
    z = np.array(['ten'] * n[0], dtype=np.str_)

    # Returns a tuple of vectors (each column of the output)
    return Table(x, y, z)

# Start Python UDF server

import singlestoredb.apps as apps
connection_info = await apps.run_udf_app()

```

This creates the following external function:

```sql
CREATE OR REPLACE EXTERNAL FUNCTION `vec_table_function`(
    `n` BIGINT NOT NULL
)
RETURNS TABLE(
    `x` BIGINT NOT NULL,
    `y` DOUBLE NOT NULL,
    `z` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL
)
DEFINER = '<function_owner>'@'%'
AS MANAGED SERVICE '<managed_service_url>'
FORMAT ROWDAT_1;
```

Invoke the vectorized Python TVF using the following command:

```sql
SELECT * FROM vec_table_function([1]);

```

```output

+----+------+-----+
| x  | y    | z   |
+----+------+-----+
| 10 | 10.0 | ten |
+----+------+-----+

```

SingleStore automatically generates generic column names if no names are associated with the return fields (`x`, `y`, `z`). Explicitly name result columns using overrides or schema classes. Refer to [Overriding Parameters and Return Value Types](https://docs.singlestore.com/cloud/container-services/python-udfs/working-with-python-udfs/#section-id235163211595581.md) for related information.

## In this section

* [Working with Python UDFs](https://docs.singlestore.com/cloud/container-services/python-udfs/working-with-python-udfs.md)

***

Modified at: September 8, 2026

Source: [/cloud/container-services/python-udfs/](https://docs.singlestore.com/cloud/container-services/python-udfs/)

(An index of the documentation is available at /llms.txt)
