# Python UDFs

> **📝 Note**: This is a Private Preview feature.

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 deployment, ensure the following:

* **SingleStore Version**: SingleStore version 8.9 or later.
* **Environment**: The SingleStore deployment must run in an AWS EKS IRSA-supported environment.

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. Python UDFs are not supported for Shared Edition workspaces. Python UDFs can only be installed per individual database, which requires a Standard or Enterprise workspace.

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 theSingleStoredeployment (workspace) the notebook connects to.Selecting aworkspaceallows connecting to theSingleStoredatabases referenced in the notebook natively.                |
| **Runtime**      | Select a runtime from the following:<ul> <li>Small</li> <li>Medium</li> <li>GPU-T4</li> </ul><blockquote> <p><strong>📝 Note</strong>: <p>This field is in preview.</p></p> </blockquote> |
| **Region**       | Select a region.                                                                                                                                                                          |
| **Idle Timeout** | Select an idle timeout.<blockquote> <p><strong>📝 Note</strong>: <p>This field is in preview.</p></p> </blockquote>                                                                       |

Select **Next**.

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

> **📝 Note**: Python UDF names must be unique. If you attempt to create a Python UDF with a name that already exists, the system returns a "Duplicate UDF Function Name" error. To update an existing Python UDF, delete the existing UDF from Container Services and then create the new Python UDF with the same name.

## Example Notebook

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

## Manage an Existing Python UDF

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. Following actions can be performed for a dashboard app 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**.

## Status of Python UDF

| Status       | Description                                                                                                                                                                        |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Initializing | The notebook is creating or updating the Python UDF.                                                                                                                               |
| Active       | The notebook is successfully published as a Python UDF.                                                                                                                            |
| Failed       | The Python UDF is not configured correctly. View the notebook snapshot to debug the failure.                                                                                       |
| Error        | An error unrelated to the notebook code prevented the Python UDF from initializing. Refer to[Troubleshoot Python UDFs](https://docs.singlestore.com/#section-id23547459240214.md). |

## Troubleshoot Python UDFs

SingleStore automatically saves a snapshot of the notebook for each failed execution. Navigate to **Container Services** in the left navigation, select the Python UDF, and download the snapshot associated with the failed Python UDF to diagnose the error.

| Error                        | Solution                                                                                                  |
| ---------------------------- | --------------------------------------------------------------------------------------------------------- |
| WorkspaceDeleted             | Update the Python UDF and select a different deployment.                                                  |
| WorkspaceSuspended           | Resume theworkspaceor create a new Python UDF with a different deployment.                                |
| Database Detached            | Reattach the database with the right permissions or create a new Python UDF with a different database.    |
| Notebook Deleted/Not Present | Create a new Python UDF with a different notebook.                                                        |
| Internal Errors/Misc         | Contact[SingleStore Support](http://support.singlestore.com/)or use the chat feature in the Cloud 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 EXTERNAL FUNCTION multiply(x DOUBLE NOT NULL, y DOUBLE NOT NULL)
RETURNS DOUBLE NOT NULL
AS REMOTE SERVICE "http://<svchost>/<endpoint>/invoke" FORMAT ROWDAT_1;
```

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 EXTERNAL FUNCTION vec_multiply(x DOUBLE NOT NULL, y DOUBLE NOT NULL)
RETURNS DOUBLE NOT NULL
AS REMOTE SERVICE "http://<svchost>/<endpoint>/invoke" 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] |
+-------------------+
```

Where `vec_col1 = [1,2,3]` and `vec_col2 = [4,5,6]`.

## 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
import numpy as np
import numpy.typing as npt
from singlestoredb.functions import udf, Table

@udf
async def number_stats(
    n: npt.NDArray[np.int_],
) -> Table[npt.NDArray[np.int_], npt.NDArray[np.int_], npt.NDArray[np.float64]]:
    numbers = np.arange(1, n[0] + 1, dtype=np.int_)
    squares = numbers ** 2
    roots = np.sqrt(numbers).round(2)   
    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 EXTERNAL FUNCTION 
    `number_stats`(`n` BIGINT NOT NULL) 
    RETURNS TABLE(
`numbers` BIGINT NOT NULL,
`squares` BIGINT NOT NULL,
`roots` DOUBLE) 
    AS REMOTE SERVICE "http://<svchost>/<endpoint>/invoke" 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 EXTERNAL FUNCTION 
    `vec_table_function`(`n` BIGINT NOT NULL)
    RETURNS TABLE(
        `x` BIGINT NOT NULL, 
        `y` DOUBLE NOT NULL, 
        `z` TEXT NOT NULL
    ) 
    AS REMOTE SERVICE "http://<svchost>/<endpoint>/invoke" 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: May 22, 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)
