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 viaqueue.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.