Skip to main content
Learn how to write Function scripts and deploy them to Notte’s serverless infrastructure.

Writing a Function

Functions are Python scripts with a run() function that serves as the entry point.

Basic Function

The minimal Function structure:
hello_function.py
def run():
    """A simple function that returns a greeting."""
    return "Hello from Notte Functions!"

Function with Parameters

Accept input parameters:
greet_function.py
def run(name: str, greeting: str = "Hello"):
    """
    Greet someone by name.

    Args:
        name: The person's name
        greeting: The greeting word (default: "Hello")

    Returns:
        A personalized greeting
    """
    return f"{greeting}, {name}!"

Browser Automation Function

Use Notte SDK for browser automation:
scraper_function.py
from notte_sdk import NotteClient


def run(url: str, selector: str):
    """
    Scrape data from a website.

    Args:
        url: The website URL
        selector: CSS selector for target element

    Returns:
        Extracted data
    """
    client = NotteClient()

    with client.Session() as session:
        session.execute(type="goto", url=url)
        data = session.scrape(instructions=f"Extract content from {selector}")

    return {"url": url, "data": data}

Deploying Functions

Via SDK

Deploy from Python:
deploy_sdk.py
from notte_sdk import NotteClient

client = NotteClient()

# Deploy function
function = client.Function(
    workflow_path="scraper_function.py", name="Website Scraper", description="Scrapes data from websites"
)

print(f"Function deployed: {function.function_id}")
print(f"Version: {function.response.latest_version}")

Deployment Options

deployment_options.py
from notte_sdk import NotteClient

client = NotteClient()

function = client.Function(
    workflow_path="my_function.py",
    name="My Function",  # Display name
    description="What this function does",  # Description
    shared=False,  # Private by default
)
Parameters:
  • workflow_path (str, required): Path to your Python file
  • name (str, optional): Function display name
  • description (str, optional): What the function does
  • shared (bool, default=False): Whether function is publicly accessible

Function Requirements

The Handler

Must have a run() function:
handler_examples.py
# Correct
def run(param1, param2):
    return "result"


# Wrong - different name
def execute(param1, param2):
    return "result"


# Wrong - no function
result = perform_task()

Dependencies

Import Notte SDK and standard libraries:
dependencies.py
# Built-in imports

# Notte SDK

# Third-party (common libraries available)


def run():
    # Your code
    pass
Available packages:
  • notte-sdk - Notte SDK
  • requests - HTTP client
  • pydantic - Data validation
  • Standard Python library
  • Most common packages

Return Values

Return JSON-serializable data:
return_values.py
def valid_returns():
    # Valid return types (JSON serializable)
    return "string"
    return 123
    return {"key": "value"}
    return ["item1", "item2"]
    return None


def invalid_returns():
    # Invalid returns (not JSON serializable)
    # return datetime.now()  # datetime not serializable
    # return lambda x: x     # functions not serializable
    pass

Parameter Types

Supported Types

parameter_types.py
def run(
    text: str,  # String
    number: int,  # Integer
    decimal: float,  # Float
    flag: bool,  # Boolean
    items: list,  # List
    data: dict,  # Dictionary
    optional: str | None = None,  # Optional
    with_default: int = 10,  # Default value
):
    pass

Type Validation

Use type hints for automatic validation:
type_validation.py
def run(count: int):
    # count is automatically validated as int
    for i in range(count):
        print(i)

Complex Types

Use Pydantic for structured parameters:
complex_types.py
from notte_sdk import NotteClient
from pydantic import BaseModel


class SearchParams(BaseModel):
    url: str
    query: str
    max_results: int = 10


def run(params: SearchParams):
    # params is validated against SearchParams model
    client = NotteClient()
    # Use params.url, params.query, etc.

Environment Variables

Access secrets securely:
environment_variables.py
import os

from notte_sdk import NotteClient


def run():
    # Access environment variables
    api_key = os.getenv("MY_API_KEY")
    webhook_url = os.getenv("WEBHOOK_URL")

    if not api_key:
        return {"error": "API key not configured"}

    # Use in automation
    client = NotteClient(api_key=api_key)
Set environment variables in Console or locally for testing.

Error Handling

Graceful Errors

Return error information in results:
graceful_errors.py
from notte_sdk import NotteClient


def run(url: str):
    try:
        client = NotteClient()
        with client.Session() as session:
            session.execute(type="goto", url=url)
            data = session.scrape()

        return {"success": True, "data": data}

    except Exception as e:
        return {"success": False, "error": str(e), "error_type": type(e).__name__}

Raising Exceptions

Let Functions fail explicitly:
raising_exceptions.py
def run(url: str):
    if not url.startswith("https://"):
        raise ValueError("URL must use HTTPS")

    # Continue with automation

Testing Locally

Test Before Deploying

Run your function locally:
test_locally.py
# Define your function (from your scraper_function.py)
def run(url: str, selector: str) -> dict:
    # Your scraping logic here
    return {"url": url, "selector": selector}


# Test with sample parameters
result = run(url="https://example.com", selector=".content")

print(result)

Mock API Calls

Test without deploying:
mock_api_calls.py
from notte_sdk import NotteClient

client = NotteClient()

# Load existing function with decryption key for local execution
function = client.Function(
    function_id="func_abc123",
    decryption_key="your-decryption-key",  # Required for local execution
)

# Run locally (not on cloud)
result = function.run(local=True, url="https://example.com")

print(result.result)

Best Practices

1. Document Parameters

Use clear docstrings:
document_params.py
def run(url: str, max_retries: int = 3):
    """
    Fetch data from a website with retries.

    Args:
        url: The website URL to scrape
        max_retries: Number of retry attempts on failure (default: 3)

    Returns:
        Dict with 'success' (bool) and 'data' (any) keys
    """
    pass

2. Return Structured Data

Use consistent return formats:
return_structured.py
from datetime import datetime


def run(url: str):
    try:
        # Perform automation
        data = scrape_url(url)

        return {"success": True, "data": data, "url": url, "timestamp": datetime.now().isoformat()}

    except Exception as e:
        return {"success": False, "error": str(e), "url": url}

3. Add Logging

Log key steps for debugging:
add_logging.py
from loguru import logger
from notte_sdk import NotteClient


def run(url: str):
    logger.info(f"Starting scrape of {url}")

    client = NotteClient()
    with client.Session() as session:
        logger.info("Session created")
        session.execute(type="goto", url=url)
        logger.info("Page loaded")

        data = session.scrape()
        logger.info(f"Extracted {len(data)} items")

    return data

4. Set Timeouts

Prevent functions from hanging:
set_timeouts.py
from notte_sdk import NotteClient


def run(url: str):
    client = NotteClient()

    # Set session timeout
    with client.Session(timeout_minutes=5) as session:
        session.execute(type="goto", url=url)
        data = session.scrape()

    return data

5. Validate Inputs

Check parameters before processing:
validate_inputs.py
def run(url: str, count: int):
    # Validate inputs
    if not url.startswith("http"):
        return {"error": "Invalid URL format"}

    if count < 1 or count > 100:
        return {"error": "Count must be between 1 and 100"}

    # Proceed with automation
    pass

Examples

Simple Scraper

simple_scraper.py
from notte_sdk import NotteClient


def run(url: str):
    """Scrape page content."""
    client = NotteClient()

    with client.Session() as session:
        session.execute(type="goto", url=url)
        content = session.scrape()

    return content

Form Submission

form_submission.py
from notte_sdk import NotteClient


def run(form_url: str, name: str, email: str, message: str):
    """Submit a contact form."""
    client = NotteClient()

    with client.Session() as session:
        session.execute(type="goto", url=form_url)
        session.execute(type="fill", id="name", value=name)
        session.execute(type="fill", id="email", value=email)
        session.execute(type="fill", id="message", value=message)
        session.execute(type="click", selector="button[type='submit']")

    return {"status": "submitted"}

Data Extraction

structured_extraction.py
from notte_sdk import NotteClient
from pydantic import BaseModel


class Product(BaseModel):
    name: str
    price: float
    in_stock: bool


def run(product_url: str):
    """Extract structured product data."""
    client = NotteClient()

    with client.Session() as session:
        session.execute(type="goto", url=product_url)

        product = session.scrape(response_format=Product, instructions="Extract product details")

    return product.model_dump()

Next Steps