---
title: Automate Features | Tabstack
description: Execute complex browser automation tasks using natural language with the Tabstack Python SDK.
---

The Automate operator executes complex browser automation tasks using natural language instructions. It provides real-time streaming updates as the automation progresses.

The `agent.automate()` method returns a `Stream` that yields `AutomateEvent` objects.

## Overview

```
import os
from tabstack import Tabstack


with Tabstack(api_key=os.getenv('TABSTACK_API_KEY')) as client:
    # Execute automation (returns a Stream)
    stream = client.agent.automate(
        task='Your task description',
        url='https://example.com',      # Optional starting URL
        data=None,                       # Optional context data
        geo_target=None,                 # Optional geotargeting (e.g., {'country': 'US'})
        guardrails=None,                 # Optional safety constraints
        max_iterations=50,               # Optional (default: 50, range: 1-100)
        max_validation_attempts=3,       # Optional (default: 3, range: 1-10)
        interactive=False                # Optional human-in-the-loop form filling
    )


    for event in stream:
        print(event.event, event.data)
```

## Execute Automation

The `automate` method returns a `Stream` that yields `AutomateEvent` objects. Each event has:

- `event`: The event type (e.g., `'start'`, `'agent:processing'`, `'complete'`)
- `data`: The event payload (varies by event type)

### Basic Usage

```
import os
from tabstack import Tabstack


with Tabstack(api_key=os.getenv('TABSTACK_API_KEY')) as client:
    stream = client.agent.automate(
        task='Find the top 3 trending repositories on GitHub and extract their names and star counts',
        url='https://github.com/trending',
        guardrails='browse and extract only'
    )


    for event in stream:
        print(f"Event: {event.event}")


        if event.event == 'complete':
            print(f'Automation completed: {event.data.final_answer}')
```

### Async Usage

```
import asyncio
import os
from tabstack import AsyncTabstack


async def run_automation():
    async with AsyncTabstack(api_key=os.getenv('TABSTACK_API_KEY')) as client:
        stream = await client.agent.automate(
            task='Find the top 3 trending repositories',
            url='https://github.com/trending',
            guardrails='browse and extract only'
        )


        async for event in stream:
            print(f"Event: {event.event}")


            if event.event == 'complete':
                print(f'Completed: {event.data.final_answer}')


asyncio.run(run_automation())
```

## Event Types

| Event Type                      | Description                                 | Data Fields                                                        |
| ------------------------------- | ------------------------------------------- | ------------------------------------------------------------------ |
| `task:started`                  | Task accepted, agent begins planning        | `task`, `plan`, `success_criteria`, `action_items`                 |
| `agent:status`                  | Human-readable status update                | `message`                                                          |
| `agent:processing`              | Agent is planning or thinking               | `operation`, `has_screenshot`                                      |
| `agent:action`                  | Performing a browser action                 | `action`, `ref`, `value`                                           |
| `agent:extracted`               | Data was extracted from the page            | `extracted_data`                                                   |
| `browser:navigated`             | Page navigation occurred                    | `url`, `title`                                                     |
| `task:validated`                | Validation pass succeeded                   | `completion_quality`, `observation`                                |
| `task:completed`                | Agent finished the task                     | `final_answer`, `success`                                          |
| `task:aborted`                  | Agent terminated the task early             | `reason`                                                           |
| `complete`                      | Final result event with stats               | `final_answer`, `stats`, `success`, `error?`                       |
| `done`                          | Stream terminator (empty payload, reserved) | None                                                               |
| `error`                         | Top-level runner crash                      | `error.code`, `error.message`, `error.timestamp`, `success: false` |
| `interactive:form_data:request` | Agent needs form data                       | `request_id`, `fields`, `form_description`                         |
| `interactive:form_data:error`   | Form validation failed                      | `request_id`, `fields`, `field_errors`                             |

For the complete event schema and full payload shapes, see the [Automate Events reference](/reference/automate-events/index.md) and the [API reference](/api/resources/agent/methods/automate/index.md).

## Real-World Examples

### Example 1: Data Extraction with Navigation

```
import os
from tabstack import Tabstack


def collect_trending():
    with Tabstack(api_key=os.getenv('TABSTACK_API_KEY')) as client:
        print('Collecting GitHub trending repositories...\n')


        stream = client.agent.automate(
            task='Navigate to GitHub trending, find the top 5 repositories, and extract: name, description, primary language, and star count',
            url='https://github.com/trending',
            guardrails='browse and extract only',
            max_iterations=50
        )


        for event in stream:
            if event.event == 'agent:status':
                print(f"Status: {event.data.message}")


            elif event.event == 'agent:action':
                print(f"Action: {event.data.action}")


            elif event.event == 'browser:navigated':
                print(f"Navigated to: {event.data.url}")


            elif event.event == 'agent:extracted':
                print(f'Extracted data: {event.data.extracted_data}')


            elif event.event == 'complete':
                print('\nAutomation completed!')
                print(f'Final result: {event.data.final_answer}')


            elif event.event == 'error':
                print(f"Error: {event.data.error.message}")


collect_trending()
```

### Example 2: Form Filling

```
import os
from tabstack import Tabstack


def fill_contact_form():
    form_data = {
        'name': 'Alex Johnson',
        'email': 'alex@example.com',
        'company': 'Example Corp',
        'message': 'I am interested in learning more about your products.'
    }


    with Tabstack(api_key=os.getenv('TABSTACK_API_KEY')) as client:
        print('Filling contact form...\n')


        stream = client.agent.automate(
            task='Fill out the contact form with the provided data and submit it',
            url='https://company.example.com/contact',
            data=form_data,
            guardrails='do not navigate away from the domain',
            max_iterations=30
        )


        for event in stream:
            if event.event == 'agent:action':
                print(f"Action: {event.data.action}")


            elif event.event == 'complete':
                print('\nForm submitted successfully!')
                print(f'Result: {event.data.final_answer}')


fill_contact_form()
```

### Example 3: Progress Tracking

```
import os
from tabstack import Tabstack


def track_progress():
    progress = {
        'status': 'Starting...',
        'current_step': 0,
        'last_action': '',
        'is_complete': False,
        'error': None
    }


    with Tabstack(api_key=os.getenv('TABSTACK_API_KEY')) as client:
        try:
            stream = client.agent.automate(
                task='Find and extract the top 5 blog posts',
                url='https://blog.example.com'
            )


            for event in stream:
                if event.event == 'agent:status':
                    progress['status'] = event.data.message


                elif event.event == 'agent:action':
                    progress['last_action'] = event.data.action


                elif event.event == 'complete':
                    progress['is_complete'] = True
                    progress['status'] = 'Completed'


                elif event.event == 'error':
                    progress['error'] = event.data.error.message


                # Display progress
                print(f"Status: {progress['status']}")
                print(f"Last Action: {progress['last_action']}\n")


        except Exception as error:
            print(f"Automation failed: {error}")


track_progress()
```

## Working with AutomateEvent

Each event in the stream is an `AutomateEvent` object with two attributes:

```
for event in stream:
    # Event type as a string
    event_type = event.event  # e.g., 'agent:status', 'complete'


    # event.data is a typed payload whose shape depends on event.event.
    # Match on the event name, then read the fields for that variant by attribute.
    if event.event == 'agent:status':
        print(f'Status: {event.data.message}')


    elif event.event == 'agent:extracted':
        print(f'Extracted: {event.data.extracted_data}')


    elif event.event == 'complete':
        if event.data.success:
            print(f'Done: {event.data.final_answer}')
        elif event.data.error:
            print(f'Failed ({event.data.error.code}): {event.data.error.message}')
```

## Options Reference

### agent.automate()

| Parameter                 | Type     | Default  | Description                                                                      |
| ------------------------- | -------- | -------- | -------------------------------------------------------------------------------- |
| `task`                    | `str`    | required | Natural language description of the task                                         |
| `url`                     | `str`    | `None`   | Starting URL for the automation                                                  |
| `data`                    | `object` | `None`   | Context data (e.g., form fields to fill)                                         |
| `geo_target`              | `dict`   | `None`   | Geotargeting parameters (e.g., `{'country': 'US'}`) for region-specific browsing |
| `guardrails`              | `str`    | `None`   | Safety constraints for automation behavior                                       |
| `max_iterations`          | `int`    | `50`     | Maximum iterations (range: 1-100)                                                |
| `max_validation_attempts` | `int`    | `3`      | Maximum validation retry attempts (range: 1-10)                                  |
| `interactive`             | `bool`   | `False`  | Enable interactive mode for human-in-the-loop form filling                       |

## Interactive Mode

Interactive mode allows the automation agent to pause and request user input when it encounters forms requiring personal data. Enable it by passing `interactive=True`:

```
import os
from tabstack import Tabstack


with Tabstack(api_key=os.getenv('TABSTACK_API_KEY')) as client:
    stream = client.agent.automate(
        task='Sign up for the newsletter',
        url='https://example.com',
        interactive=True,
    )


    for event in stream:
        # The agent requests form data when it encounters a form
        if event.event == 'interactive:form_data:request':
            request_id = event.data.request_id


            # Collect values for each field. `event.data.fields` is a list of
            # typed Field models -- attribute access, not dict access.
            field_values = []
            for field in event.data.fields:
                value = input(f"  {field.label}: ")
                field_values.append({'ref': field.ref, 'value': value})


            # Submit values back to resume the task
            client.agent.automate_input(request_id, fields=field_values)


        # Handle validation errors with corrected values
        elif event.event == 'interactive:form_data:error':
            request_id = event.data.request_id
            field_errors = event.data.field_errors


            field_values = []
            for field in event.data.fields:
                error = field_errors.get(field.ref, '')
                prompt = f"  {field.label} ({error}): " if error else f"  {field.label}: "
                value = input(prompt)
                field_values.append({'ref': field.ref, 'value': value})


            client.agent.automate_input(request_id, fields=field_values)


        elif event.event == 'complete':
            if event.data.success:
                print(f'Done: {event.data.final_answer}')
            elif event.data.error:
                print(f'Failed ({event.data.error.code}): {event.data.error.message}')
```

To cancel an interactive request instead of providing data:

```
client.agent.automate_input(request_id, cancelled=True)
```

For a complete guide, see [Interactive Mode](/guides/interactive-mode/index.md).

## Guardrails

Use guardrails to constrain what the automation agent can do:

```
# Browse only - no form submissions
guardrails = 'browse and extract only'


# Stay on domain
guardrails = 'do not navigate away from the domain'


# No purchases
guardrails = 'do not add items to cart or make purchases'


# Read-only
guardrails = 'read-only operations, do not submit forms'


# Multiple constraints
guardrails = 'browse and extract only, do not submit forms, stay on the main domain'
```

## Best Practices

### 1. Be Specific with Instructions

```
# Vague
task = 'Get some products'


# Specific
task = 'Find the top 5 best-selling products in Electronics and extract names, prices, and ratings'
```

### 2. Always Use Guardrails

```
# Good: Clear safety constraints
stream = client.agent.automate(
    task='Extract data',
    guardrails='browse and extract only, do not submit forms'
)
```

### 3. Handle All Event Types

```
for event in stream:
    if event.event == 'agent:status':
        # Show progress
        pass
    elif event.event == 'complete':
        # Handle success
        pass
    elif event.event == 'error':
        # Handle errors
        break
    elif event.event == 'task:aborted':
        # Handle abortion
        break
```

### 4. Set Appropriate Iteration Limits

```
# Simple single-page tasks
max_iterations = 20


# Multi-page workflows
max_iterations = 50  # default


# Complex deep pagination
max_iterations = 100
```

### 5. Handle Errors Gracefully

```
import tabstack
from tabstack import Tabstack


with Tabstack() as client:
    try:
        stream = client.agent.automate(task=task, url=url)


        for event in stream:
            if event.event == 'error':
                print(f"Automation error: {event.data.error.message}")
                break
            elif event.event == 'complete':
                print(f"Success: {event.data.final_answer}")


    except tabstack.APIStatusError as error:
        print(f"API error: {error.status_code} - {error}")
    except tabstack.APIConnectionError as error:
        print(f"Connection error: {error}")
```

## Next Steps

- **[Error Handling](./error-handling)**: Build robust applications
- **[Generate Features](./generate)**: Discover AI transformations
- **[REST API Reference](/api/index.md)**: See the REST API endpoint
