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

# Functions

> Deploy browser automations as serverless API endpoints

<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.md">
    Learn how to write and deploy Functions
  </Card>

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

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

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