Skip to content

Excel Add-in

The Excel add-in APIs integrate Datatailr workflows with spreadsheet-based workflows.

Related API pages

Quick start: deploy your first Excel add-in

Step 1: write your Excel add-in

In this example, we will create a simple Excel Add-in that implements some basic functionality.

# my_addin.py

import datatailr.excel

addin = datatailr.excel.Addin(
    "MyAddin",
    "This is a very cool simple Excel addin!",
)

@addin.expose(description="Adds 2 numbers", help="To add 2 numbers give them to the function")
def add(a: int, b: int) -> float:
    return a + b

@addin.expose(description="Subtracts 2 numbers", help="To subtract 2 numbers give them to the function")
def sub(a: int, b: int) -> float:
    return a - b

def __excel_main__(port, debug=False):
    addin.run(port=port)

Step 2: deploy the Excel add-in

Your Excel Add-in deployment can be defined as a datatailr.scheduler.Job object:

# my_addin_deployment.py

from datatailr import ExcelAddin
from my_addin import __excel_main__

addin = ExcelAddin(
    name="my_addin",
    entrypoint=__excel_main__,
)
addin.save()
addin.start()

Running the code above will deploy your Excel Add-in.

Developing and deploying Excel add-ins

Excel add-ins on Datatailr are created automatically from Python modules. Any fully type-annotated functions you expose will be callable from Excel once the add-in is sideloaded.

Develop an Excel add-in

from datatailr.excel import Addin, Queue

Create the add-in

my_addin = Addin("MyAddin", "This is a very cool simple Excel addin!")
Addin(...) parameters
  • name: str - Display name in Excel.
  • description: str - Shown in the taskpane.
  • version: str (optional), default value "1.0.0" - Semantic version.
  • min_update_interval: float (optional), default value 0.5 - exists for compatibility with addins defined for v1. Doesn't do anything.

Expose functions

Decorate fully annotated functions with addin.expose(...).

Allowed type hints are plain int, float, list, bool, and str only. Do not use generics (list[int]), unions (int | None), or other types (dict, datetime, etc.) — the Excel bridge cannot map them and Excel will report "Invalid argument" errors. For streaming functions, the first parameter may be annotated queue: Queue; it is omitted from the Excel signature.

When you construct ExcelAddin(name=..., entrypoint=main, ...), the SDK parses the entrypoint source file and raises ValueError if any @addin.expose(...) function violates these rules.

import time
from datatailr.excel import Queue

@addin.expose(description="Add two numbers", help="Returns a + b")
def add(a: float, b: float) -> float:
    return a + b
addin.expose(...) parameters
  • description: str - Shown in the taskpane next to the function name.
  • help: str - Tooltip shown in the formula bar.
  • volatile: bool = False - Mark as volatile (Excel may recalc more often).
  • streaming: bool = False - If True, the function's first argument must be queue: datatailr.excel.Queue. The return value is ignored; values are sent via queue.push(...).
  • progressbar: bool = False - When True, the function becomes a streaming function that auto-streams a progress bar until the final result is ready.

Streaming functions

import time
from datatailr.excel import Queue

@addin.expose(
    description="Simple numeric ticker",
    help="Streams an increasing number",
    streaming=True,
)
def int_ticker_N(queue: Queue, start: int, N: int) -> int:
    value = start
    for _ in range(N):
        queue.push(value)   # streamed to Excel immediately
        value += 1
        time.sleep(1)
    # return value is ignored for streaming functions

Queue API

  • Queue.push(value: Any) -> None - Streams a value to Excel. Type should match the function's annotated return type.
  • Queue.error(message: str) -> None - Sends an error for the current invocation.

Working with matrices

Excel custom functions may accept and return 2-D lists ("matrices"). Elements may be mixed-type (strings, numbers, etc). Even 1-D vectors must still be represented as a 2-D nested list.

def print_row() -> list:
    return [[1, 2, 3]]

def print_column() -> list:
    return [[1], [2], [3]]

def print_matrix() -> list:
    return [[1, 2], [3, 4]]

Optional parameters

If you define a default for a parameter, Excel treats it as optional. For list-like parameters, use a plain list annotation and handle None inside the function body (do not use list | None or Optional[list] in the hint):

def foo(x: list = None) -> list:
    if x is None:
        ...

Deploy an Excel add-in

Assume the add-in lives in my_module/my_addin.py:

import datatailr.excel

addin = datatailr.excel.Addin(
    "MyAddin",
    "This is a very cool simple Excel addin as always!",
    version="1.0.0",
)

@addin.expose(description="Adds 2 numbers", help="Provide two numbers")
def add(a: int, b: int) -> int:
    return a + b

Deploying in the IDE (for development)

Run via the IDE proxy for quick testing:

if __name__ == "__main__":
    addin.run(12345, 12346, ide=True)
Addin.run(...) parameters
  • port: int - HTTP port for the download page and non-streaming calls.
  • ws_port: int - WebSocket port for streaming functions.
  • ide: bool = True - Must be True when running inside the IDE. Using False here in the IDE will produce a broken add-in.

Deploying to Datatailr

Create an ExcelAddin job (or a generic Job with JobType.EXCEL). The ExcelAddin constructor validates type annotations in the entrypoint source file before building the job definition.

from my_module.my_addin import main
from datatailr import ExcelAddin, Resources

addin = ExcelAddin(
    name="my_addin",
    entrypoint=main,
    python_version="3.13",
    resources=Resources(memory="512m", cpu=1.0),
)
addin.save()

Resources also accepts vram for GPU memory, using the same size strings as memory (for example "2g"). If you omit vram, the job definition omits the vram field.

Alternatively, using the lower-level scheduler API:

from my_module.my_addin import __excel_main__

from datatailr.scheduler import Job, JobType, EntryPoint, Resources

job = Job(
    "my_addin",
    python_version="3.13",
    resources=Resources(memory="512m", cpu=1.0),
    entrypoint=EntryPoint(type=JobType.EXCEL, func=__excel_main__),
)
job.save()

This will deploy the add-in and make it available with the rest of the deployed applications and add-ins in the dev apps section.

Note: In EntryPoint, you pass a function name (e.g., __excel_main__) to carry the module reference; the function itself is not executed for Excel jobs.