> ## Documentation Index
> Fetch the complete documentation index at: https://docs.notte.cc/llms.txt
> Use this file to discover all available pages before exploring further.

# Functions

> Deploy browser automations as serverless API endpoints

<Tip>
  STOP. Do not read past this section until you have read and followed [/quickstart.md](/quickstart.md).

  Goal: produce a reliable SDK automation script. The intended authoring flow is generate then edit, not write browser automation from scratch.

  Do not hand-write SDK code with element IDs, selectors, or form fields from memory. Element IDs, selectors, and field mappings must come from a live `observe()` call, CLI `notte page observe` output, or generated workflow code. Guessed browser targets fail on real pages.

  Use the Notte skill and CLI to inspect the live site, validate actions/selectors, handle auth/session state, and export workflow code with `notte sessions workflow-code` before using SDK docs or SDK code.

  SDK reference pages are for understanding, running, or editing generated workflow code. They are not the starting point for manually authoring the initial browser automation. SDK-first code is guesswork on real-world pages with dynamic selectors, auth state, CAPTCHAs, and anti-bot behavior.
</Tip>

<iframe className="w-full aspect-video rounded-xl" src="https://www.youtube.com/embed/jzbsfX-2qzY" title="Notte Functions" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

Functions are serverless deployments of your browser automations that can be invoked via API, scheduled to run automatically, or triggered by events.

## What are Functions?

Functions turn your automation scripts into:

* **API endpoints** you can call with HTTP requests
* **Scheduled jobs** that run on a cron schedule
* **Reusable workflows** accessible from anywhere
* **Shareable automations** for your team

Unlike running scripts locally, Functions:

* ✅ Run on Notte's infrastructure (no servers to manage)
* ✅ Scale automatically based on demand
* ✅ Provide built-in logging and monitoring
* ✅ Can be invoked from any platform (Python, JavaScript, cURL, etc.)
* ✅ Support scheduling and automation

## How Functions Work

### 1. Write Your Script

Create a Python file with a `run()` function:

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

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

client = NotteClient()


def run(url: str, search_query: str):
    """Search a website url and extract results."""

    with client.Session() as session:
        # Navigate to site
        session.execute(type="goto", url=url)

        # Search
        session.execute(type="fill", selector="input[name='search']", value=search_query)
        session.execute(type="press_key", key="Enter")

        # Extract results
        results = session.scrape(instructions="Extract search results")

        return results
```

### 2. Deploy to Notte

Upload your script to create a Function:

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

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

client = NotteClient()

# Deploy function
function = client.Function(
    path="my_automation.py", name="Search Automation", description="Searches a website and extracts results"
)

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

### 3. Invoke the Function

Call your Function as an API:

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

```python invoke_sdk.py theme={null}
# Via SDK
result = function.run(url="https://example.com", search_query="laptop")

print(result.result)
```

```bash theme={null}
# Via cURL
curl -X POST https://api.notte.cc/functions/{function_id}/runs/start \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "function_id": "workflow_123",
    "variables": {
      "url": "https://example.com",
      "search_query": "laptop"
    }
  }'
```

## Function Structure

### The Handler Function

Functions must have a `run()` function that serves as the entry point:

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

```python handler_function.py theme={null}
def run(param1: str, param2: int = 10):
    """
    Function docstring explains what it does.

    Args:
        param1: Description of param1
        param2: Description of param2 (optional)

    Returns:
        Description of return value
    """
    # Your automation code
    result = perform_automation(param1, param2)
    return result
```

**Key points:**

* Named `run()` - this is the entry point
* Can accept parameters (passed as `variables` when invoked)
* Should have type hints for clarity
* Should include docstring documentation
* Returns a value (any JSON-serializable type)

### Parameters

Define parameters as arguments to the `run` function:

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

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


def run(email: str, password: str, product_id: str):
    client = NotteClient()

    with client.Session() as session:
        session.execute(type="fill", id="email", value=email)
        session.execute(type="fill", id="password", value=password)
        session.execute(type="goto", url=f"https://example.com/product/{product_id}")
```

Pass values when invoking the function:

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

```python pass_values.py theme={null}
function.run(email="user@example.com", password="secret", product_id="123")
```

### Return Values

Functions can return any JSON-serializable data:

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

```python return_values.py theme={null}
from pydantic import BaseModel


# Return string
def run_string():
    return "Task completed successfully"


# Return dict
def run_dict():
    extracted_data = ["item1", "item2"]
    return {"status": "success", "data": extracted_data, "count": len(extracted_data)}


# Return list
def run_list():
    return ["item1", "item2", "item3"]


# Return Pydantic model (serialized)
class Result(BaseModel):
    success: bool
    data: list[str]


def run():
    return Result(success=True, data=["a", "b"]).model_dump()
```

## Use Cases

### 1. Scheduled Scraping

Extract data on a schedule:

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

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

# price_monitor.py
def run(product_urls: list[str]):
    run_client = NotteClient()
    prices = []

    for url in product_urls:
        with run_client.Session() as session:
            session.execute(type="goto", url=url)
            price = session.scrape(instructions="Extract product price")
            prices.append({"url": url, "price": price})

    return prices


# Deploy and schedule to run daily
```

**Schedule:** `0 9 * * *` (Every day at 9 AM)

***

### 2. API Endpoints

Expose automation as an API:

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

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


# contact_extractor.py
def run(company_url: str):
    client = NotteClient()

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

        contact_info = session.scrape(instructions="Extract contact email and phone")

        return contact_info


# Now callable from any service
# GET /functions/{id}/runs/start?variables={"company_url": "..."}
```

***

### 3. Webhooks

Trigger automations from external events:

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

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


# order_processor.py
def run(order_id: str, action: str):
    client = NotteClient()

    with client.Session() as session:
        # Login to admin panel
        session.execute(type="goto", url="https://admin.example.com")

        # Process order
        if action == "fulfill":
            session.execute(type="click", selector=f"button[data-order='{order_id}'].fulfill")
        elif action == "refund":
            session.execute(type="click", selector=f"button[data-order='{order_id}'].refund")

    return {"order_id": order_id, "action": action, "status": "completed"}


# Called via webhook from your e-commerce platform
```

***

### 4. Batch Processing

Process multiple items in parallel:

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

```python bulk_data_extract.py theme={null}
from concurrent.futures import ThreadPoolExecutor

from notte_sdk import NotteClient


# bulk_data_extract.py
def run(urls: list[str], max_workers: int = 5):
    client = NotteClient()

    def extract_from_url(url):
        with client.Session() as session:
            session.execute(type="goto", url=url)
            data = session.scrape()
            return {"url": url, "data": data}

    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        results = list(executor.map(extract_from_url, urls))

    return results
```

## How Functions Fit In

Functions are a **deployment layer** - they turn any automation into a reusable API.

```
┌─────────────────────────────────────────────────────┐
│                     Function                        │
│            (Deployment & Scheduling)                │
├─────────────────────────────────────────────────────┤
│                                                     │
│   ┌─────────────────┐    ┌─────────────────┐       │
│   │ Scripted        │ or │ Agent           │       │
│   │ Automation      │    │ (AI-driven)     │       │
│   └────────┬────────┘    └────────┬────────┘       │
│            │                      │                 │
│            └──────────┬───────────┘                 │
│                       │                             │
│              ┌────────▼────────┐                    │
│              │    Session      │                    │
│              │ (Cloud Browser) │                    │
│              └─────────────────┘                    │
│                                                     │
└─────────────────────────────────────────────────────┘
```

* **Session** - The cloud browser that runs everything
* **Scripted Automation vs Agent** - How you control the session
* **Function** - Deploys your automation as an API with scheduling, versioning, and sharing

**Use Functions when:**

* You need to run automation repeatedly
* You want to expose automation as an API
* You need scheduling capabilities
* You want to share automation with team or customers

Functions can contain either scripted automation or agents - they're not mutually exclusive.

## Function Lifecycle

```mermaid theme={null}
graph LR
    A[Write Script] --> B[Deploy Function]
    B --> C[Get Function ID]
    C --> D{Invoke}
    D -->|API Call| E[Execute]
    D -->|Schedule| E
    D -->|Webhook| E
    E --> F[Return Result]
    F --> G[Access Logs/Replay]
```

1. **Write** - Create Python script with `run()` function
2. **Deploy** - Upload to Notte (creates function ID)
3. **Version** - Notte tracks versions automatically
4. **Invoke** - Call via API, schedule, or webhook
5. **Execute** - Runs on Notte infrastructure
6. **Monitor** - View logs, replays, and results

## Best Practices

### 1. Use Clear Parameters

Define parameters with type hints and descriptions:

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

```python bp_clear_parameters.py theme={null}
def run(target_url: str, max_results: int = 10, timeout_seconds: int = 30):
    """
    Extract data from a website.

    Args:
        target_url: The URL to scrape
        max_results: Maximum number of results to extract
        timeout_seconds: Session timeout
    """
    pass
```

### 2. Return Structured Data

Always return JSON-serializable data:

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

```python bp_return_structured.py theme={null}
from datetime import datetime


def my_function():
    results = []  # your data

    # Good - return JSON-serializable dict
    return {"success": True, "data": results, "count": len(results), "timestamp": datetime.now().isoformat()}

    # Bad - not JSON serializable
    # return datetime.now()  # Can't serialize datetime directly
```

### 3. Handle Errors Gracefully

Catch exceptions and return meaningful errors:

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

```python bp_handle_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__}
```

### 4. Add Logging

Log important steps for debugging:

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

```python bp_add_logging.py theme={null}
from loguru import logger
from notte_sdk import NotteClient


def run(url: str):
    logger.info(f"Starting automation for {url}")

    client = NotteClient()
    with client.Session() as session:
        logger.info("Session started")
        session.execute(type="goto", url=url)
        logger.info("Navigation complete")

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

    return data
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Creating Functions" icon="file-code" href="/features/functions/creating">
    Learn how to write and deploy Functions
  </Card>

  <Card title="Invocations" icon="play" href="/features/functions/invocations">
    Call Functions via API, SDK, or cURL
  </Card>

  <Card title="Schedules" icon="clock" href="/features/functions/schedules">
    Schedule Functions with cron
  </Card>

  <Card title="Management" icon="sliders" href="/features/functions/management">
    Update, version, and monitor Functions
  </Card>
</CardGroup>
