> ## Documentation Index
> Fetch the complete documentation index at: https://notte-experiment-visibility-md-links.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Creating Functions

> Write and deploy browser automation Functions

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:

{/* @sniptest testers/functions/creating/basic_function.py */}

```python hello_function.py theme={null}
def run():
    """A simple function that returns a greeting."""
    return "Hello from Notte Functions!"
```

### Function with Parameters

Accept input parameters:

{/* @sniptest testers/functions/creating/function_with_params.py */}

```python greet_function.py theme={null}
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:

{/* @sniptest testers/functions/creating/browser_automation_function.py */}

```python scraper_function.py theme={null}
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:

{/* @sniptest testers/functions/creating/deploy_sdk.py */}

```python deploy_sdk.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Deploy function
function = client.Function(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

{/* @sniptest testers/functions/creating/deployment_options.py */}

```python deployment_options.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

function = client.Function(
    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:

{/* @sniptest testers/functions/creating/handler_examples.py */}

```python handler_examples.py theme={null}
# 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:

{/* @sniptest testers/functions/creating/dependencies.py */}

```python dependencies.py theme={null}
# 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:

{/* @sniptest testers/functions/creating/return_values.py */}

```python return_values.py theme={null}
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

{/* @sniptest testers/functions/creating/parameter_types.py */}

```python parameter_types.py theme={null}
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:

{/* @sniptest testers/functions/creating/type_validation.py */}

```python type_validation.py theme={null}
def run(count: int):
    # count is automatically validated as int
    for i in range(count):
        print(i)
```

### Complex Types

Use Pydantic for structured parameters:

{/* @sniptest testers/functions/creating/complex_types.py */}

```python complex_types.py theme={null}
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:

{/* @sniptest testers/functions/creating/environment_variables.py */}

```python environment_variables.py theme={null}
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:

{/* @sniptest testers/functions/creating/graceful_errors.py */}

```python graceful_errors.py theme={null}
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:

{/* @sniptest testers/functions/creating/raising_exceptions.py */}

```python raising_exceptions.py theme={null}
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:

{/* @sniptest testers/functions/creating/test_locally.py */}

```python test_locally.py theme={null}
# 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:

{/* @sniptest testers/functions/creating/mock_api_calls.py */}

```python mock_api_calls.py theme={null}
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:

{/* @sniptest testers/functions/creating/document_params.py */}

```python document_params.py theme={null}
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:

{/* @sniptest testers/functions/creating/return_structured.py */}

```python return_structured.py theme={null}

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:

{/* @sniptest testers/functions/creating/add_logging.py */}

```python add_logging.py theme={null}
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:

{/* @sniptest testers/functions/creating/set_timeouts.py */}

```python set_timeouts.py theme={null}
from notte_sdk import NotteClient


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

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

    return data
```

### 5. Validate Inputs

Check parameters before processing:

{/* @sniptest testers/functions/creating/validate_inputs.py */}

```python validate_inputs.py theme={null}
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

{/* @sniptest testers/functions/creating/simple_scraper.py */}

```python simple_scraper.py theme={null}
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

{/* @sniptest testers/functions/form_submission.py */}

```python form_submission.py theme={null}
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

{/* @sniptest testers/functions/structured_extraction.py */}

```python structured_extraction.py theme={null}
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

<CardGroup cols={2}>
  <Card title="Invocations" icon="play" href="/features/functions/invocations.md">
    Learn how to call your Functions
  </Card>

  <Card title="Schedules" icon="clock" href="/features/functions/schedules.md">
    Schedule Functions to run automatically
  </Card>

  <Card title="Management" icon="sliders" href="/features/functions/management.md">
    Update and manage Functions
  </Card>

  <Card title="Functions Concept" icon="lightbulb" href="/concepts/functions.md">
    Back to Functions overview
  </Card>
</CardGroup>
