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:
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

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:
# ✅ 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:
# Built-in imports
import os
import json
from datetime import datetime

# Notte SDK
from notte_sdk import NotteClient
from pydantic import BaseModel

# Third-party (common libraries available)
import requests

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:
# ✅ Valid returns
return "string"
return 123
return {"key": "value"}
return ["item1", "item2"]
return None

# ❌ Invalid returns
return datetime.now()  # Not JSON serializable
return lambda x: x  # Functions not serializable

Parameter Types

Supported Types

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:
def run(count: int):
    # count is automatically validated as int
    for i in range(count):
        print(i)

Complex Types

Use Pydantic for structured parameters:
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:
import os

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:
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:
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_function.py
from scraper_function import run

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

print(result)

Mock API Calls

Test without deploying:
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:
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:
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:
from loguru import logger

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:
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:
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

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