# Custom Tools
Source: https://docs.smooth.sh/api-reference/custom-tools
Register custom tools that the AI agent can call during task execution
Custom tools allow you to extend the agent's capabilities by registering functions that it can call during execution. When the agent needs to perform an action that your tool provides, it will pause and wait for you to execute the tool and return the result.
## How Custom Tools Work
1. **Register tools** when creating a task via the `custom_tools` parameter
2. **Poll for tool calls** - the agent sends `tool_call` events when it needs to use a tool
3. **Execute the tool** locally and return the result via the Event endpoint
4. **Agent continues** with the tool's output
```mermaid theme={null}
sequenceDiagram
participant Client
participant API
participant Agent
Client->>API: POST /task (with custom_tools)
API-->>Client: {id, status: "running"}
Agent->>API: Tool call needed
Client->>API: GET /task/{id}?event_t=0
API-->>Client: {events: [{name: "tool_call", payload: {name: "my_tool", input: {...}}}]}
Note over Client: Execute tool locally
Client->>API: POST /task/{id}/event (tool response)
API-->>Client: {id: "evt_response"}
Agent->>API: Continues with tool result
```
## Step 1: Define Your Tools
When creating a task, define your custom tools using the `custom_tools` parameter:
```bash theme={null}
curl -X POST "https://api.smooth.sh/api/v1/task" \
-H "Content-Type: application/json" \
-H "apikey: YOUR_API_KEY" \
-d '{
"task": "Look up the weather in New York and send me a summary via Slack",
"custom_tools": [
{
"name": "get_weather",
"description": "Get the current weather for a city. Use this when you need weather information.",
"inputs": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "The city name"
}
},
"required": ["city"]
},
"output": "Weather information as a JSON object with temperature, conditions, and humidity"
},
{
"name": "send_slack_message",
"description": "Send a message to a Slack channel. Use this to notify the user.",
"inputs": {
"type": "object",
"properties": {
"channel": {
"type": "string",
"description": "The Slack channel name"
},
"message": {
"type": "string",
"description": "The message to send"
}
},
"required": ["channel", "message"]
},
"output": "Boolean indicating success"
}
]
}'
```
### Tool Definition Schema
| Field | Type | Required | Description |
| ------------- | ------ | -------- | ---------------------------------------------- |
| `name` | string | Yes | Unique identifier for the tool |
| `description` | string | Yes | Explains what the tool does and when to use it |
| `inputs` | object | Yes | JSON Schema describing the input parameters |
| `output` | string | Yes | Description of what the tool returns |
## Step 2: Poll for Tool Calls
When the agent needs to call one of your tools, it emits a `tool_call` event. Poll the task endpoint to receive these events:
```bash theme={null}
curl -X GET "https://api.smooth.sh/api/v1/task/task_abc123?event_t=0" \
-H "apikey: YOUR_API_KEY"
```
**Response with tool call:**
```json theme={null}
{
"r": {
"id": "task_abc123",
"status": "running",
"events": [
{
"id": "tc_weather_001",
"name": "tool_call",
"payload": {
"name": "get_weather",
"input": {
"city": "New York"
}
},
"timestamp": 1699999999999
}
]
}
}
```
**Important:** The `id` field in the event is crucial - you'll use it to send the response.
## Step 3: Execute and Respond
Execute the tool locally, then send the result back via the Event endpoint:
```bash theme={null}
curl -X POST "https://api.smooth.sh/api/v1/task/task_abc123/event" \
-H "Content-Type: application/json" \
-H "apikey: YOUR_API_KEY" \
-d '{
"name": "tool_call",
"payload": {
"code": 200,
"output": {
"temperature": 72,
"conditions": "Partly cloudy",
"humidity": 45
}
},
"id": "tc_weather_001"
}'
```
### Response Format
| Field | Type | Description |
| ---------------- | ------ | ---------------------------------------------- |
| `name` | string | Must be `"tool_call"` |
| `id` | string | **Must match** the event ID from the tool call |
| `payload.code` | number | `200` for success, `400` or `500` for errors |
| `payload.output` | any | The tool's result (or error message) |
### Handling Errors
If your tool fails, return an error:
```bash theme={null}
curl -X POST "https://api.smooth.sh/api/v1/task/task_abc123/event" \
-H "Content-Type: application/json" \
-H "apikey: YOUR_API_KEY" \
-d '{
"name": "tool_call",
"payload": {
"code": 400,
"output": "City not found: New Yrok"
},
"id": "tc_weather_001"
}'
```
The agent will receive the error and may try again with corrected input or take an alternative approach.
## Complete Example: Weather Bot
Here's a complete implementation that handles custom tool calls:
```javascript theme={null}
const API_KEY = 'YOUR_API_KEY';
const BASE_URL = 'https://api.smooth.sh/api/v1';
// Define your tool implementations
const toolImplementations = {
async get_weather({ city }) {
// In reality, you'd call a weather API
const weatherData = {
'New York': { temperature: 72, conditions: 'Partly cloudy', humidity: 45 },
'London': { temperature: 58, conditions: 'Rainy', humidity: 80 },
'Tokyo': { temperature: 68, conditions: 'Clear', humidity: 55 }
};
const weather = weatherData[city];
if (!weather) {
throw new Error(`Weather data not available for: ${city}`);
}
return weather;
},
async send_slack_message({ channel, message }) {
// In reality, you'd use the Slack API
console.log(`[Slack #${channel}] ${message}`);
return true;
}
};
async function request(method, path, body = null) {
const response = await fetch(`${BASE_URL}${path}`, {
method,
headers: {
'apikey': API_KEY,
'Content-Type': 'application/json'
},
body: body ? JSON.stringify(body) : null
});
return response.json();
}
async function handleToolCall(taskId, event) {
const { name, input } = event.payload;
console.log(`Tool called: ${name}`, input);
let response;
try {
const implementation = toolImplementations[name];
if (!implementation) {
throw new Error(`Unknown tool: ${name}`);
}
const output = await implementation(input);
response = { code: 200, output };
} catch (error) {
response = { code: 400, output: error.message };
}
// Send the response
await request('POST', `/task/${taskId}/event`, {
name: 'tool_call',
payload: response,
id: event.id
});
console.log(`Tool response sent for ${name}:`, response);
}
async function runTaskWithTools() {
// Create task with custom tools
const { r: task } = await request('POST', '/task', {
task: 'Look up the weather in New York and send a summary to the #general Slack channel',
custom_tools: [
{
name: 'get_weather',
description: 'Get current weather for a city',
inputs: {
type: 'object',
properties: {
city: { type: 'string', description: 'The city name' }
},
required: ['city']
},
output: 'Weather data with temperature, conditions, humidity'
},
{
name: 'send_slack_message',
description: 'Send a message to a Slack channel',
inputs: {
type: 'object',
properties: {
channel: { type: 'string', description: 'Channel name' },
message: { type: 'string', description: 'Message to send' }
},
required: ['channel', 'message']
},
output: 'Boolean indicating success'
}
]
});
console.log(`Task created: ${task.id}`);
console.log(`Live URL: ${task.live_url}`);
// Poll and handle tool calls
let lastEventT = 0;
const processedEvents = new Set();
while (true) {
const { r: taskStatus } = await request('GET', `/task/${task.id}?event_t=${lastEventT}`);
// Check if task is done
if (!['running', 'waiting'].includes(taskStatus.status)) {
console.log(`Task finished with status: ${taskStatus.status}`);
console.log('Output:', taskStatus.output);
break;
}
// Process tool calls
if (taskStatus.events) {
for (const event of taskStatus.events) {
if (event.name === 'tool_call' && !processedEvents.has(event.id)) {
processedEvents.add(event.id);
await handleToolCall(task.id, event);
}
}
lastEventT = taskStatus.events[taskStatus.events.length - 1].timestamp;
}
await new Promise(r => setTimeout(r, 1000));
}
}
runTaskWithTools().catch(console.error);
```
## Using with Sessions
Custom tools work the same way with session workflows. Define them when creating the session:
```bash theme={null}
curl -X POST "https://api.smooth.sh/api/v1/task" \
-H "Content-Type: application/json" \
-H "apikey: YOUR_API_KEY" \
-d '{
"task": null,
"url": "https://example.com",
"custom_tools": [
{
"name": "save_to_database",
"description": "Save extracted data to the database",
"inputs": {
"type": "object",
"properties": {
"data": { "type": "object" },
"table": { "type": "string" }
},
"required": ["data", "table"]
},
"output": "The saved record ID"
}
]
}'
```
Then when running tasks within the session, the agent can use your custom tools:
```bash theme={null}
curl -X POST "https://api.smooth.sh/api/v1/task/task_abc123/event" \
-H "Content-Type: application/json" \
-H "apikey: YOUR_API_KEY" \
-d '{
"name": "session_action",
"payload": {
"name": "run_task",
"input": {
"task": "Extract all products from this page and save them to the products database table"
}
},
"id": "evt_task_001"
}'
```
## Python SDK Example
The SDK provides a decorator-based approach for custom tools:
```python theme={null}
from smooth import SmoothClient, tool
client = SmoothClient(api_key="YOUR_API_KEY")
@tool
def get_weather(city: str) -> dict:
"""Get current weather for a city.
Args:
city: The city name to get weather for
Returns:
Weather data with temperature, conditions, humidity
"""
# Your weather API call here
return {
"temperature": 72,
"conditions": "Partly cloudy",
"humidity": 45
}
@tool
def send_slack_message(channel: str, message: str) -> bool:
"""Send a message to a Slack channel.
Args:
channel: The Slack channel name
message: The message to send
Returns:
True if message was sent successfully
"""
# Your Slack API call here
print(f"[Slack #{channel}] {message}")
return True
# The SDK handles polling and tool execution automatically
task = client.run(
task="Look up the weather in New York and send a summary to #general",
tools=[get_weather, send_slack_message]
)
result = task.result()
print(f"Task output: {result.output}")
```
## Best Practices
1. **Write clear descriptions** - The agent uses descriptions to decide when to call your tool
2. **Define complete schemas** - Include all required fields and descriptions for each input
3. **Handle errors gracefully** - Return meaningful error messages so the agent can recover
4. **Set appropriate timeouts** - Long-running tools should be async with proper timeout handling
5. **Validate inputs** - Check that required fields are present before executing
# Send Event
Source: https://docs.smooth.sh/api-reference/event/send-event
/api-reference/openapi.json post /task/{task_id}/event
Send an event to a running task. This is used for:
- **Session actions**: `run_task`, `goto`, `extract`, `evaluate_js`, `close`
- **Custom tool responses**: Responding to tool calls from the agent
The SDK handles this automatically when using `session.run_task()`, `session.goto()`, etc.
# Delete Extension
Source: https://docs.smooth.sh/api-reference/extension/delete-extension
/api-reference/openapi.json delete /browser/extension/{extension_id}
Delete an uploaded browser extension by its ID.
# List Extensions
Source: https://docs.smooth.sh/api-reference/extension/list-extensions
/api-reference/openapi.json get /extension
List all uploaded browser extensions.
# Upload Extension
Source: https://docs.smooth.sh/api-reference/extension/upload-extension
/api-reference/openapi.json post /extension
Upload a browser extension (.zip file) to be used in tasks.
# Delete File
Source: https://docs.smooth.sh/api-reference/file/delete-file
/api-reference/openapi.json delete /file/{file_id}
Delete an uploaded file by its ID.
# Upload File
Source: https://docs.smooth.sh/api-reference/file/upload-file
/api-reference/openapi.json post /file
Upload a file to be used by tasks. Files can be passed to tasks via the `files` parameter.
# API Overview
Source: https://docs.smooth.sh/api-reference/introduction
Complete guide to the Smooth REST API
**We recommend using one of our SDKs instead of calling the API directly.** The SDKs handle authentication, polling, and error handling automatically, making integration much simpler.
If we don't support your language yet, reach out at **[support@circlemind.co](mailto:support@circlemind.co)** and we'll be happy to support your implementation.
The Smooth API enables programmatic browser automation through REST endpoints. You can run AI-powered tasks, create interactive browser sessions, and extend agent capabilities with custom tools.
## Quick Start
Get your API key and start running tasks in minutes.
Unlock your free welcome credits. No credit card required.
## Authentication
All API endpoints require authentication via the `apikey` header:
```bash theme={null}
curl -X POST "https://api.smooth.sh/api/v1/task" \
-H "apikey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"task": "Go to hacker news and get the top story"}'
```
## Two Ways to Use the API
### 1. Simple Tasks
Run one-shot tasks that execute and return results:
```bash theme={null}
# Submit a task
curl -X POST "https://api.smooth.sh/api/v1/task" \
-H "apikey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"task": "Go to hacker news and get the top 5 stories"}'
# Response: {"r": {"id": "task_abc123", "status": "running", ...}}
# Poll for results
curl -X GET "https://api.smooth.sh/api/v1/task/task_abc123" \
-H "apikey: YOUR_API_KEY"
# Response: {"r": {"id": "task_abc123", "status": "done", "output": "..."}}
```
### 2. Session Workflows
For multi-step workflows, create a session and send actions:
```bash theme={null}
# Create a session (task=null)
curl -X POST "https://api.smooth.sh/api/v1/task" \
-H "apikey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"task": null, "url": "https://example.com"}'
# Response: {"r": {"id": "task_abc123", "status": "running", "live_url": "..."}}
# Send actions via the Event endpoint
curl -X POST "https://api.smooth.sh/api/v1/task/task_abc123/event" \
-H "apikey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "session_action",
"payload": {"name": "run_task", "input": {"task": "Click the login button"}},
"id": "evt_001"
}'
# Poll for the result
curl -X GET "https://api.smooth.sh/api/v1/task/task_abc123?event_t=0" \
-H "apikey: YOUR_API_KEY"
```
Learn how to create sessions, send actions, and poll for results
Understand how to efficiently poll for task results and events
## Core Endpoints
| Endpoint | Method | Description |
| ------------------ | ------ | ----------------------------------------------- |
| `/task` | POST | Submit a task or create a session (`task=null`) |
| `/task` | GET | List all tasks |
| `/task/{id}` | GET | Get task status and results |
| `/task/{id}` | DELETE | Cancel a running task |
| `/task/{id}/event` | POST | Send an event to a running task/session |
## Session Actions
When using sessions, send actions via `POST /task/{id}/event`:
| Action | Event Type | Description |
| ---------- | ---------------- | ----------------------------------------- |
| Navigate | `browser_action` | Go to a URL |
| Extract | `browser_action` | Extract structured data from the page |
| JavaScript | `browser_action` | Execute JavaScript in the browser |
| Run Task | `session_action` | Run an AI-powered task within the session |
| Close | `session_action` | Close the session |
See complete examples of all session actions
## Custom Tools
Extend agent capabilities by registering custom tools:
```bash theme={null}
curl -X POST "https://api.smooth.sh/api/v1/task" \
-H "apikey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"task": "Look up the weather and send it to Slack",
"custom_tools": [
{
"name": "get_weather",
"description": "Get weather for a city",
"inputs": {"type": "object", "properties": {"city": {"type": "string"}}},
"output": "Weather data"
},
{
"name": "send_slack",
"description": "Send a Slack message",
"inputs": {"type": "object", "properties": {"message": {"type": "string"}}},
"output": "Success boolean"
}
]
}'
```
When the agent calls a tool, you receive a `tool_call` event. Execute the tool locally and respond via the Event endpoint.
Complete guide to implementing custom tools with the API
## Additional Features
### Browser Profiles
Persist cookies and authentication across tasks:
```bash theme={null}
# Create a profile
curl -X POST "https://api.smooth.sh/api/v1/profile" \
-H "apikey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"id": "my-profile"}'
# Use the profile in a task
curl -X POST "https://api.smooth.sh/api/v1/task" \
-H "apikey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"task": "Check my account balance", "profile_id": "my-profile"}'
```
### Structured Output
Get structured data matching a JSON schema:
```bash theme={null}
curl -X POST "https://api.smooth.sh/api/v1/task" \
-H "apikey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"task": "Get the top 3 stories from Hacker News",
"response_model": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {"type": "string"},
"url": {"type": "string"},
"points": {"type": "number"}
}
}
}
}'
```
### File Uploads
Pass files to tasks:
```bash theme={null}
# Upload a file
curl -X POST "https://api.smooth.sh/api/v1/file" \
-H "apikey: YOUR_API_KEY" \
-F "file=@invoice.pdf"
# Response: {"r": {"id": "file_xyz789"}}
# Use in a task
curl -X POST "https://api.smooth.sh/api/v1/task" \
-H "apikey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"task": "Extract the total from this invoice", "files": ["file_xyz789"]}'
```
## Response Format
All responses are wrapped in an `r` field:
```json theme={null}
{
"r": {
"id": "task_abc123",
"status": "done",
"output": "...",
"credits_used": 15
}
}
```
### Task Status Values
| Status | Description |
| ----------- | --------------------------- |
| `waiting` | Task is queued |
| `running` | Task is executing |
| `done` | Task completed successfully |
| `failed` | Task failed |
| `cancelled` | Task was cancelled |
## Error Handling
| HTTP Code | Meaning |
| --------- | ------------------------------------------ |
| 400 | Invalid request parameters |
| 403 | Premium feature not available on your plan |
| 404 | Resource not found |
| 422 | Validation error |
| 429 | Rate limit exceeded or credits exhausted |
Error responses include a detail message:
```json theme={null}
{
"detail": "API credits exhausted. Please upgrade your plan."
}
```
## Next Steps
Create interactive sessions for multi-step workflows
Extend agent capabilities with your own tools
Best practices for polling task results
Use the SDK for automatic polling and easier integration
# Polling Mechanism
Source: https://docs.smooth.sh/api-reference/polling
How to poll for task results and events
The Smooth API uses polling to deliver task results and events. This page explains how the polling mechanism works and best practices for implementing it.
## Why Polling?
The API uses polling rather than webhooks or WebSockets for several reasons:
* **Simplicity** - No need to set up webhook endpoints or maintain WebSocket connections
* **Reliability** - No lost events due to connection issues
* **Firewall-friendly** - Works behind firewalls that block incoming connections
* **Stateless** - Each request is independent; easy to implement in any language
## Basic Polling
For simple tasks, poll the task endpoint until the status changes to `done`, `failed`, or `cancelled`:
```bash theme={null}
# Initial request
curl -X GET "https://api.smooth.sh/api/v1/task/task_abc123" \
-H "apikey: YOUR_API_KEY"
```
**Response (running):**
```json theme={null}
{
"r": {
"id": "task_abc123",
"status": "running",
"output": null,
"live_url": "https://live.smooth.sh/v/..."
}
}
```
**Response (completed):**
```json theme={null}
{
"r": {
"id": "task_abc123",
"status": "done",
"output": "The top 5 stories from Hacker News are...",
"credits_used": 15,
"recording_url": "https://..."
}
}
```
### HTTP Status Codes
| Status Code | Meaning |
| ----------- | ----------------------------------------------------- |
| `200` | Task has completed (`done`, `failed`, or `cancelled`) |
| `202` | Task is still running (`waiting` or `running`) |
| `404` | Task not found |
## Event-Based Polling
For sessions and custom tools, use the `event_t` parameter to receive events incrementally. This is more efficient than fetching the full response each time.
### The event\_t Parameter
The `event_t` (event timestamp) parameter filters events to only return those that occurred **after** the specified timestamp:
```bash theme={null}
# First poll - get all events
curl -X GET "https://api.smooth.sh/api/v1/task/task_abc123?event_t=0" \
-H "apikey: YOUR_API_KEY"
```
```json theme={null}
{
"r": {
"id": "task_abc123",
"status": "running",
"events": [
{
"id": "evt_001",
"name": "browser_action",
"payload": {"code": 200, "output": null},
"timestamp": 1699999990000
},
{
"id": "evt_002",
"name": "tool_call",
"payload": {"name": "my_tool", "input": {"x": 1}},
"timestamp": 1699999999000
}
]
}
}
```
```bash theme={null}
# Subsequent polls - only new events
curl -X GET "https://api.smooth.sh/api/v1/task/task_abc123?event_t=1699999999000" \
-H "apikey: YOUR_API_KEY"
```
```json theme={null}
{
"r": {
"id": "task_abc123",
"status": "running",
"events": [
{
"id": "evt_003",
"name": "browser_action",
"payload": {"code": 200, "output": {"title": "Example"}},
"timestamp": 1700000005000
}
]
}
}
```
### Event Structure
Each event contains:
| Field | Type | Description |
| ----------- | ------- | -------------------------------------------------------------- |
| `id` | string | Unique event identifier (use for matching responses) |
| `name` | string | Event type: `browser_action`, `session_action`, or `tool_call` |
| `payload` | object | Event-specific data |
| `timestamp` | integer | Unix timestamp in milliseconds |
## Polling Loop Implementation
Here's a robust polling implementation:
```javascript theme={null}
async function pollTask(taskId, options = {}) {
const {
pollInterval = 1000,
timeout = 300000, // 5 minutes
onEvent = null
} = options;
const startTime = Date.now();
let lastEventT = 0;
while (true) {
// Check timeout
if (Date.now() - startTime > timeout) {
throw new Error('Polling timeout exceeded');
}
// Poll for updates
const response = await fetch(
`https://api.smooth.sh/api/v1/task/${taskId}?event_t=${lastEventT}`,
{ headers: { 'apikey': API_KEY } }
);
const { r: task } = await response.json();
// Process events
if (task.events && task.events.length > 0) {
for (const event of task.events) {
if (onEvent) {
await onEvent(event);
}
}
// Update timestamp for next poll
lastEventT = task.events[task.events.length - 1].timestamp;
}
// Check if task is complete
if (!['running', 'waiting'].includes(task.status)) {
return task;
}
// Wait before next poll
await new Promise(r => setTimeout(r, pollInterval));
}
}
// Usage
const result = await pollTask('task_abc123', {
pollInterval: 1000,
timeout: 300000,
onEvent: async (event) => {
console.log('Event:', event.name, event.id);
// Handle tool calls
if (event.name === 'tool_call') {
const output = await executeMyTool(event.payload);
await sendToolResponse(taskId, event.id, output);
}
}
});
```
## Best Practices
### 1. Use Appropriate Poll Intervals
| Scenario | Recommended Interval |
| ----------------------------- | -------------------- |
| Simple tasks | 1-2 seconds |
| Sessions with actions | 500ms - 1 second |
| Custom tools (time-sensitive) | 500ms |
| Background monitoring | 5-10 seconds |
### 2. Implement Exponential Backoff
For long-running tasks, increase the interval over time:
```javascript theme={null}
async function pollWithBackoff(taskId) {
let interval = 1000;
const maxInterval = 10000;
while (true) {
const task = await getTask(taskId);
if (task.status !== 'running' && task.status !== 'waiting') {
return task;
}
await new Promise(r => setTimeout(r, interval));
// Increase interval up to max
interval = Math.min(interval * 1.2, maxInterval);
}
}
```
### 3. Handle Network Errors
```javascript theme={null}
async function resilientPoll(taskId) {
let retries = 0;
const maxRetries = 5;
while (true) {
try {
const task = await getTask(taskId);
retries = 0; // Reset on success
if (task.status !== 'running' && task.status !== 'waiting') {
return task;
}
} catch (error) {
retries++;
if (retries >= maxRetries) {
throw new Error(`Polling failed after ${maxRetries} retries: ${error.message}`);
}
// Exponential backoff on errors
await new Promise(r => setTimeout(r, Math.pow(2, retries) * 1000));
continue;
}
await new Promise(r => setTimeout(r, 1000));
}
}
```
### 4. Track Processed Events
Avoid processing the same event twice:
```javascript theme={null}
const processedEvents = new Set();
function handleEvents(events) {
for (const event of events) {
if (processedEvents.has(event.id)) {
continue; // Already processed
}
processedEvents.add(event.id);
// Process event...
}
}
```
### 5. Clean Up on Errors
If your polling loop fails, consider cancelling the task to avoid resource leaks:
```javascript theme={null}
async function runWithCleanup(taskId) {
try {
return await pollTask(taskId);
} catch (error) {
// Cancel task on error
try {
await fetch(`https://api.smooth.sh/api/v1/task/${taskId}`, {
method: 'DELETE',
headers: { 'apikey': API_KEY }
});
} catch (cancelError) {
// Ignore cancel errors
}
throw error;
}
}
```
## Python SDK
The Python SDK handles all polling automatically:
```python theme={null}
from smooth import SmoothClient
client = SmoothClient(api_key="YOUR_API_KEY")
# Automatic polling with result()
task = client.run(task="Go to google.com")
result = task.result() # Blocks until complete, handles polling internally
# With timeout
result = task.result(timeout=60) # Raises TimeoutError after 60 seconds
# Session polling is also automatic
with client.session() as session:
# Each action polls internally for the response
session.goto("https://example.com")
data = session.extract(schema={"type": "object"}, prompt="Extract data")
print(data.output)
```
## Debugging
### Checking Event Flow
Add logging to understand the event flow:
```javascript theme={null}
const { r: task } = await getTask(taskId, eventT);
console.log(`Status: ${task.status}`);
console.log(`Events since ${eventT}:`);
for (const event of task.events || []) {
console.log(` [${event.timestamp}] ${event.name} (${event.id})`);
console.log(` Payload:`, JSON.stringify(event.payload));
}
```
### Common Issues
| Issue | Cause | Solution |
| -------------------- | ----------------------------- | --------------------------------------------- |
| Missing events | Using wrong `event_t` | Always use the last event's timestamp |
| Duplicate processing | Not tracking processed events | Keep a Set of processed event IDs |
| Timeout errors | Poll interval too long | Reduce interval for time-sensitive operations |
| Rate limiting | Polling too fast | Increase poll interval or use backoff |
# Create Profile
Source: https://docs.smooth.sh/api-reference/profile/create-profile
/api-reference/openapi.json post /profile
Creates a new browser profile. Profiles persist cookies and authentication across tasks.
# Delete Profile
Source: https://docs.smooth.sh/api-reference/profile/delete-profile
/api-reference/openapi.json delete /profile/{profile_id}
Delete a browser profile by its ID.
# List Profiles
Source: https://docs.smooth.sh/api-reference/profile/list-profiles
/api-reference/openapi.json get /profile
List all browser profiles for the user.
# Open Browser Session (Deprecated)
Source: https://docs.smooth.sh/api-reference/profile/open-browser-session-deprecated
/api-reference/openapi.json post /browser/session
**Deprecated**: Use `POST /task` with `task=null` instead, and send actions via `POST /task/{task_id}/event`.
Opens an interactive browser instance for 5 minutes.
# Session Workflow
Source: https://docs.smooth.sh/api-reference/sessions
Create interactive browser sessions for multi-step workflows
Sessions allow you to maintain a persistent browser instance and perform multiple actions sequentially. This is useful for complex workflows like logging in, navigating, and extracting data across multiple pages.
## How Sessions Work
1. **Create a session** by calling `POST /task` with `task=null`
2. **Send actions** via `POST /task/{task_id}/event`
3. **Poll for results** via `GET /task/{task_id}?event_t={timestamp}`
4. **Close the session** by sending a `close` event
```mermaid theme={null}
sequenceDiagram
participant Client
participant API
participant Browser
Client->>API: POST /task (task=null)
API-->>Client: {id, status: "running", live_url}
Client->>API: POST /task/{id}/event (goto)
API-->>Client: {id: "evt_123"}
loop Poll for result
Client->>API: GET /task/{id}?event_t=0
API-->>Client: {events: [{id: "evt_123", payload: {code: 200}}]}
end
Client->>API: POST /task/{id}/event (extract)
API-->>Client: {id: "evt_456"}
loop Poll for result
Client->>API: GET /task/{id}?event_t=1699999999
API-->>Client: {events: [{id: "evt_456", payload: {code: 200, output: {...}}}]}
end
Client->>API: POST /task/{id}/event (close)
API-->>Client: {id: "evt_789"}
```
## Step 1: Create a Session
Create a session by submitting a task with `task=null`. The browser will open and wait for actions.
```bash theme={null}
curl -X POST "https://api.smooth.sh/api/v1/task" \
-H "Content-Type: application/json" \
-H "apikey: YOUR_API_KEY" \
-d '{
"task": null,
"url": "https://example.com",
"device": "desktop"
}'
```
**Response:**
```json theme={null}
{
"r": {
"id": "task_abc123",
"status": "running",
"live_url": "https://live.smooth.sh/v/...",
"output": null
}
}
```
Save the `id` - you'll use it for all subsequent actions.
## Step 2: Send Actions
Use the Event endpoint to send actions to your session. Each action requires:
* `name`: The event type (`browser_action` or `session_action`)
* `payload`: Contains the action name and input parameters
* `id`: A unique ID to match the response (use any unique string like UUID)
### Navigate to a URL
```bash theme={null}
curl -X POST "https://api.smooth.sh/api/v1/task/task_abc123/event" \
-H "Content-Type: application/json" \
-H "apikey: YOUR_API_KEY" \
-d '{
"name": "browser_action",
"payload": {
"name": "goto",
"input": {
"url": "https://example.com/login"
}
},
"id": "evt_goto_001"
}'
```
### Run a Task (Agent-powered)
Execute an AI-powered task within the session:
```bash theme={null}
curl -X POST "https://api.smooth.sh/api/v1/task/task_abc123/event" \
-H "Content-Type: application/json" \
-H "apikey: YOUR_API_KEY" \
-d '{
"name": "session_action",
"payload": {
"name": "run_task",
"input": {
"task": "Log in with username test@example.com and password secret123",
"max_steps": 32
}
},
"id": "evt_login_001"
}'
```
### Extract Data
Extract structured data from the current page:
```bash theme={null}
curl -X POST "https://api.smooth.sh/api/v1/task/task_abc123/event" \
-H "Content-Type: application/json" \
-H "apikey: YOUR_API_KEY" \
-d '{
"name": "browser_action",
"payload": {
"name": "extract",
"input": {
"schema": {
"type": "object",
"properties": {
"username": {"type": "string"},
"email": {"type": "string"},
"plan": {"type": "string"}
}
},
"prompt": "Extract the user profile information"
}
},
"id": "evt_extract_001"
}'
```
### Execute JavaScript
Run custom JavaScript in the browser:
```bash theme={null}
curl -X POST "https://api.smooth.sh/api/v1/task/task_abc123/event" \
-H "Content-Type: application/json" \
-H "apikey: YOUR_API_KEY" \
-d '{
"name": "browser_action",
"payload": {
"name": "evaluate_js",
"input": {
"js": "return document.title"
}
},
"id": "evt_js_001"
}'
```
## Step 3: Poll for Results
After sending an action, poll the task endpoint to receive the result. Use the `event_t` parameter to only receive new events.
```bash theme={null}
curl -X GET "https://api.smooth.sh/api/v1/task/task_abc123?event_t=0" \
-H "apikey: YOUR_API_KEY"
```
**Response with action result:**
```json theme={null}
{
"r": {
"id": "task_abc123",
"status": "running",
"events": [
{
"id": "evt_extract_001",
"name": "browser_action",
"payload": {
"code": 200,
"output": {
"username": "john_doe",
"email": "john@example.com",
"plan": "Pro"
}
},
"timestamp": 1699999999999
}
]
}
}
```
**Understanding the response:**
* `code: 200` - Action succeeded, `output` contains the result
* `code: 400` - Bad request, `output` contains error message
* `code: 500` - Internal error, `output` contains error message
For the next poll, use the last event's `timestamp`:
```bash theme={null}
curl -X GET "https://api.smooth.sh/api/v1/task/task_abc123?event_t=1699999999999" \
-H "apikey: YOUR_API_KEY"
```
## Step 4: Close the Session
When finished, close the session to release resources:
```bash theme={null}
curl -X POST "https://api.smooth.sh/api/v1/task/task_abc123/event" \
-H "Content-Type: application/json" \
-H "apikey: YOUR_API_KEY" \
-d '{
"name": "session_action",
"payload": {
"name": "close"
},
"id": "evt_close_001"
}'
```
## Complete Example: Login and Extract Data
Here's a complete workflow that logs into a website and extracts user data:
```javascript theme={null}
const API_KEY = 'YOUR_API_KEY';
const BASE_URL = 'https://api.smooth.sh/api/v1';
async function request(method, path, body = null) {
const response = await fetch(`${BASE_URL}${path}`, {
method,
headers: {
'apikey': API_KEY,
'Content-Type': 'application/json'
},
body: body ? JSON.stringify(body) : null
});
return response.json();
}
async function sendEvent(taskId, event) {
return request('POST', `/task/${taskId}/event`, event);
}
async function pollForResult(taskId, eventId, lastEventT = 0) {
while (true) {
const { r: task } = await request('GET', `/task/${taskId}?event_t=${lastEventT}`);
if (task.status !== 'running' && task.status !== 'waiting') {
throw new Error(`Task ended with status: ${task.status}`);
}
if (task.events) {
for (const event of task.events) {
if (event.id === eventId) {
if (event.payload.code === 200) {
return { result: event.payload.output, lastEventT: event.timestamp };
} else {
throw new Error(event.payload.output);
}
}
}
// Update timestamp for next poll
lastEventT = task.events[task.events.length - 1].timestamp;
}
await new Promise(r => setTimeout(r, 1000));
}
}
async function main() {
// 1. Create session
console.log('Creating session...');
const { r: session } = await request('POST', '/task', {
task: null,
url: 'https://example.com',
device: 'desktop'
});
const taskId = session.id;
console.log(`Session created: ${taskId}`);
console.log(`Live URL: ${session.live_url}`);
let lastEventT = 0;
// 2. Navigate to login page
console.log('Navigating to login...');
const gotoId = 'evt_goto_' + Date.now();
await sendEvent(taskId, {
name: 'browser_action',
payload: { name: 'goto', input: { url: 'https://example.com/login' } },
id: gotoId
});
const gotoResult = await pollForResult(taskId, gotoId, lastEventT);
lastEventT = gotoResult.lastEventT;
console.log('Navigation complete');
// 3. Run login task
console.log('Logging in...');
const loginId = 'evt_login_' + Date.now();
await sendEvent(taskId, {
name: 'session_action',
payload: {
name: 'run_task',
input: {
task: 'Fill in the login form with email "test@example.com" and password "secret123", then click the login button',
max_steps: 16
}
},
id: loginId
});
const loginResult = await pollForResult(taskId, loginId, lastEventT);
lastEventT = loginResult.lastEventT;
console.log('Login complete:', loginResult.result);
// 4. Extract user data
console.log('Extracting user data...');
const extractId = 'evt_extract_' + Date.now();
await sendEvent(taskId, {
name: 'browser_action',
payload: {
name: 'extract',
input: {
schema: {
type: 'object',
properties: {
username: { type: 'string' },
email: { type: 'string' },
accountType: { type: 'string' }
}
},
prompt: 'Extract the logged-in user profile information'
}
},
id: extractId
});
const extractResult = await pollForResult(taskId, extractId, lastEventT);
console.log('Extracted data:', extractResult.result);
// 5. Close session
console.log('Closing session...');
const closeId = 'evt_close_' + Date.now();
await sendEvent(taskId, {
name: 'session_action',
payload: { name: 'close' },
id: closeId
});
console.log('Session closed');
}
main().catch(console.error);
```
## Action Reference
| Action | Event Type | Payload | Description |
| ------------- | ---------------- | ---------------------------------------------------- | ----------------------- |
| `goto` | `browser_action` | `{name: "goto", input: {url}}` | Navigate to a URL |
| `extract` | `browser_action` | `{name: "extract", input: {schema, prompt?}}` | Extract structured data |
| `evaluate_js` | `browser_action` | `{name: "evaluate_js", input: {js, args?}}` | Execute JavaScript |
| `run_task` | `session_action` | `{name: "run_task", input: {task, max_steps?, ...}}` | Run an AI-powered task |
| `close` | `session_action` | `{name: "close"}` | Close the session |
## Using the Python SDK
The SDK handles all the polling complexity for you:
```python theme={null}
from smooth import SmoothClient
client = SmoothClient(api_key="YOUR_API_KEY")
with client.session(url="https://example.com") as session:
# Navigate
session.goto("https://example.com/login")
# Run a task
result = session.run_task(
task="Log in with test@example.com and password secret123",
max_steps=16
)
print(f"Login result: {result.output}")
# Extract data
data = session.extract(
schema={
"type": "object",
"properties": {
"username": {"type": "string"},
"email": {"type": "string"}
}
},
prompt="Extract user profile information"
)
print(f"Extracted: {data.output}")
# Execute JavaScript
title = session.evaluate_js("return document.title")
print(f"Page title: {title.output}")
```
# Cancel Task
Source: https://docs.smooth.sh/api-reference/task/cancel-task
/api-reference/openapi.json delete /task/{task_id}
Cancel a running task by its ID.
# Get Task
Source: https://docs.smooth.sh/api-reference/task/get-task
/api-reference/openapi.json get /task/{task_id}
Returns the status and result of a task by its ID. Use `event_t` parameter for polling to receive only new events since the given timestamp.
# List Tasks
Source: https://docs.smooth.sh/api-reference/task/list-tasks
/api-reference/openapi.json get /task
List all tasks for the authenticated user.
# Submit Task
Source: https://docs.smooth.sh/api-reference/task/submit-task
/api-reference/openapi.json post /task
Submits a task to be executed by the Smooth agent. If `task` is `null`, opens a browser session that waits for actions via the Event endpoint.
# Claude Code
Source: https://docs.smooth.sh/cli/claude-code
Setup Smooth CLI for Claude Code
## Installation
```bash theme={null}
pip install smooth-py
```
Get your API key from [app.smooth.sh](https://app.smooth.sh), then run:
```bash theme={null}
smooth config --api-key YOUR_API_KEY
```
```bash theme={null}
/plugin marketplace add circlemind-ai/smooth-sdk
/plugin install smooth-browser
```
Alternatively, you can copy the [SKILL.md file](https://raw.githubusercontent.com/circlemind-ai/smooth-sdk/refs/heads/master/skills/smooth-browser/SKILL.md) directly to your agent's skill folder at `~/.claude/skills/`.
Ask Claude to do something on the web. It will use Smooth automatically.
```
Find a one-way flight from London to NY leaving tomorrow
```
**Pro Tip:** You can also give Claude complex goals. Our skill will teach Claude how to break them into subtasks and distribute them across multiple concurrent browser sessions automatically.
# Stay Logged In
Source: https://docs.smooth.sh/cli/features/authentication
Let your agent access your accounts without re-authenticating
Browser profiles save your login state so your agent can access authenticated content. Log in once manually, then your agent can work with that account indefinitely.
## What Your Agent Can Do
* **Check your email** - Read and summarize messages from Gmail, Outlook, etc.
* **Manage social media** - Post updates, check notifications, respond to messages
* **Access work tools** - Use Jira, Notion, Salesforce, or any SaaS you're logged into
* **Monitor dashboards** - Check analytics, reports, and admin panels
## How It Works
1. Create a profile and log in manually through the live view
2. Your authentication (cookies, sessions) is saved to that profile
3. Future sessions with the same profile are already logged in
Your credentials stay on your machine and in the secure profile storage. The agent never sees your password.
## Example: Check LinkedIn Messages
First session - log in manually:
> "Start a session with my linkedin profile and go to linkedin.com. I'll log in."
You log in through the live view URL. Cookies are saved.
Next time - already authenticated:
> "Using my linkedin profile, check my messages and summarize any from recruiters this week"
Your agent is already logged in and can access your messages immediately.
## Example: Post to Social Media
> "Using my twitter profile, draft a tweet about our new product launch and show me a preview before posting"
Your agent uses your saved Twitter session to compose the tweet.
## Example: Check Work Dashboard
> "Using my salesforce profile, go to my deals dashboard and tell me which deals are closing this week"
Your agent accesses Salesforce with your saved authentication.
You control when to log in and what accounts to save. The agent only uses profiles you've explicitly set up.
# Extract Data
Source: https://docs.smooth.sh/cli/features/data-extraction
Pull structured information from any webpage
Your agent can extract structured data from any webpage and return it in a format you can work with. No parsing HTML, no writing selectors - just describe what you want.
## What Your Agent Can Do
* **Scrape product listings** - Get prices, names, and details from e-commerce sites
* **Extract contact info** - Pull emails, phone numbers, and addresses from directories
* **Gather research data** - Collect information from multiple sources into structured tables
* **Parse search results** - Turn search engine results into actionable data
## Example: Extract Search Results
Ask your agent:
> "Search Google for 'best restaurants in Austin' and extract the top 10 results with name, rating, and address"
Your agent returns structured data:
```json theme={null}
[
{"name": "Franklin Barbecue", "rating": 4.8, "address": "900 E 11th St"},
{"name": "Uchi", "rating": 4.7, "address": "801 S Lamar Blvd"},
...
]
```
## Example: Scrape a Product Page
> "Go to this Amazon product page and extract the title, price, rating, and number of reviews"
```json theme={null}
{
"title": "Sony WH-1000XM5 Wireless Headphones",
"price": 328.00,
"rating": 4.6,
"reviews": 12847
}
```
## Example: Gather Competitive Intelligence
> "Go to our three main competitors' pricing pages and extract all their plan names and prices"
Your agent visits multiple sites and returns consolidated data you can compare.
## How Extraction Works
1. Your agent navigates to the page (or you provide a URL)
2. You describe what data you want in plain English
3. The agent analyzes the page and extracts matching information
4. Data is returned in a structured format
Be specific about what fields you want. "Extract the products" is okay, but "Extract product name, price, and availability" gets better results.
# Upload & Download Files
Source: https://docs.smooth.sh/cli/features/files
Share files with your agent and receive files back
Your agent can work with files in both directions - you can give it files to upload to websites, and it can download files from the web for you.
## What Your Agent Can Do
* **Fill forms with attachments** - Upload resumes, documents, or images to web forms
* **Download reports** - Grab PDFs, spreadsheets, or exports from dashboards
* **Process documents** - Give the agent a file and have it upload it somewhere
* **Collect assets** - Download images, files, or exports from multiple sources
## Uploading Files
Give your agent a local file to use during its session:
> "Here's my resume (resume.pdf). Go to the company careers page and apply for the Software Engineer position"
Your agent uploads your resume to the job application form.
> "Upload this invoice (invoice.pdf) to the expense reporting system and categorize it as 'Travel'"
Your agent fills out the expense form with your document attached.
## Downloading Files
Your agent can download files and make them available to you:
> "Go to our analytics dashboard, generate a report for last month, and download it"
> "Find the latest SEC 10-K filing for Apple and download it"
> "Go to the Google Slides presentation at this URL and download it as a PDF"
After the session, you can retrieve downloaded files from the session.
## Example: Bulk Document Processing
> "For each of these 5 contracts in my folder, upload them to the DocuSign portal and send for signature to [legal@company.com](mailto:legal@company.com)"
Your agent processes each file, handling the uploads and form filling.
# Watch Your Agent Work
Source: https://docs.smooth.sh/cli/features/live-view
See exactly what your agent sees in real-time
Every session comes with a live view URL where you can watch your agent browse in real-time. You can also take over and interact with the browser yourself.
## What You Can Do
* **Watch progress** - See your agent navigate, click, and type in real-time
* **Debug issues** - See exactly where things go wrong when a task fails
* **Take over** - Manually handle a step the agent can't (like a CAPTCHA)
* **Verify actions** - Confirm the agent is doing what you expect before it continues
## How It Works
When a session starts, you get a live URL:
```
Session started successfully!
Session ID: abc123
Live URL: https://live.smooth.sh/v/...
```
Open that URL in your browser to watch and interact.
## Example: Supervise a Sensitive Task
> "Using my bank profile, go to my account and check my balance. I'll watch via live view."
You monitor the live view as the agent navigates your banking site, ready to intervene if needed.
## Example: Handle a CAPTCHA
The agent encounters a CAPTCHA it can't solve:
1. You see it in the live view
2. You solve the CAPTCHA manually
3. The agent continues where it left off
## Example: Verify Before Submitting
> "Fill out the insurance claim form with these details. Stop before submitting so I can review."
You check the live view to verify the form is correct, then tell the agent to submit.
The live view is interactive. You can click, type, and scroll - the agent will see your changes and can continue from there.
Live views are session-specific and expire when the session closes. Share them only with people you trust.
# Access Localhost
Source: https://docs.smooth.sh/cli/features/localhost
Let your agent browse your local development environment
Your agent can access localhost and your local network through a secure tunnel. This is perfect for testing local apps, accessing internal tools, or working with development servers.
## What Your Agent Can Do
* **Test your local app** - Have the agent click through your dev server and report bugs
* **Access internal dashboards** - Browse admin panels and internal tools on your network
* **Fill local forms** - Submit data to your local APIs through the browser
* **Screenshot local pages** - Get visual feedback from your running application
## How It Works
When you start a session, the browser traffic is routed through your machine by default. Your agent sees exactly what you'd see if you opened the browser yourself.
```
Your Machine Cloud Browser
| |
| <-- secure tunnel --> |
| |
localhost:3000 agent browses localhost:3000
```
The tunnel is automatic. Just give your agent a localhost URL and it works.
## Example: Test a Local App
Ask your agent:
> "Go to localhost:3000, click through the signup flow, and tell me if there are any broken links or UI issues"
Your agent will browse your local dev server, interact with it like a real user, and report back.
## Example: Access Internal Tools
> "Go to 192.168.1.100:8080/admin, check the system status dashboard, and summarize any warnings"
Your agent can access anything on your local network that your machine can reach.
## Disabling the Tunnel
If you don't need local access and want the browser to use its own IP, tell your agent to use the `--no-proxy` flag when starting the session.
# OpenClaw
Source: https://docs.smooth.sh/cli/openclaw
Setup Smooth CLI for OpenClaw
## Installation
```bash theme={null}
pip install smooth-py
```
Get your API key from [app.smooth.sh](https://app.smooth.sh), then run:
```bash theme={null}
smooth config --api-key YOUR_API_KEY
```
```bash theme={null}
npx clawhub@latest install smooth-browser
```
Alternatively, you can copy the [SKILL.md file](https://raw.githubusercontent.com/circlemind-ai/smooth-sdk/refs/heads/master/skills/smooth-browser/SKILL.md) directly to your agent's skill folder.
Ask your agent to do something on the web. It will use Smooth automatically.
```
Find a one-way flight from London to NY leaving tomorrow
```
**Pro Tip:** You can also give your agent complex goals. Our skill will teach it how to break them into subtasks and distribute them across multiple concurrent browser sessions automatically.
# Other Agents
Source: https://docs.smooth.sh/cli/other-agents
Setup Smooth CLI for any agent
Smooth CLI works with any agent that can run CLI commands.
## Supported Agents
* Codex
* Cursor
* Antigravity
* Cline
* Factory AI
* Github Copilot
* Kiro
* OpenCode
* Windsurf
* Any other agent that can run CLI commands
## Installation
```bash theme={null}
pip install smooth-py
```
Get your API key from [app.smooth.sh](https://app.smooth.sh), then run:
```bash theme={null}
smooth config --api-key YOUR_API_KEY
```
```bash theme={null}
npx skills add https://github.com/circlemind-ai/smooth-sdk
```
Alternatively, you can copy the [SKILL.md file](https://raw.githubusercontent.com/circlemind-ai/smooth-sdk/refs/heads/master/skills/smooth-browser/SKILL.md) directly to your agent's skill folder.
Ask your agent to do something on the web. It will use Smooth automatically.
```
Find a one-way flight from London to NY leaving tomorrow
```
**Pro Tip:** You can also give your agent complex goals. Our skill will teach it how to break them into subtasks and distribute them across multiple concurrent browser sessions automatically.
# Overview
Source: https://docs.smooth.sh/cli/overview
Give your AI agent a browser that actually works
## The Problem
AI agents like Claude Code are powerful, but they're mostly stuck in the terminal. Meanwhile, most valuable work happens in the browser.
Current browser tools for agents (like `--chrome`, Playwright MCP, agent-browser) all make the same mistake: **they expose low-level actions like click, type, and scroll.** This forces your agent to think about button positions instead of your actual goals.
This creates three problems:
| Problem | Why it matters |
| --------------------- | -------------------------------------------------------------------------------------------------------- |
| **Slow & expensive** | Using a massive model to click buttons is wasteful. Every action costs tokens and time. |
| **Context pollution** | Every click and keystroke fills up the context window with UI noise instead of your task. |
| **Wrong expertise** | General-purpose models aren't trained to handle iframes, shadow DOMs, and the messy reality of websites. |
## The Solution
Smooth CLI is a browser built for AI agents. Instead of exposing hundreds of low-level tools, it gives agents a simple natural language interface.
**Your agent says what it wants. Smooth figures out how to do it.**
A specialized model handles the clicking so your agent can focus on thinking.
Stop using 1T+ params models and burning tokens on UI navigation.
Route traffic through your machine to avoid captchas, access geo-restricted content, and reach localhost services.
Runs in an isolated environment with no permissions by default.
Launch as many parallel browsers as needed, on-demand.
No setup, no configuration. Browsers run in the cloud, ready instantly.
## How It Works
Instead of this:
```
click(x=342, y=128)
type("search query")
click(x=401, y=130)
scroll(down=500)
click(x=220, y=340)
... (50 more steps)
```
Your agent just says:
```
"Search for flights from NYC to LA and find the cheapest option"
```
The agent thinks about your goals. Smooth handles the browser.
## Get Started
Setup for Claude Code
Setup for OpenClaw
Generic setup
# Form Filling
Source: https://docs.smooth.sh/cli/use-cases/form-filling
Have your agent fill out tedious web forms
Your agent can navigate complex multi-step forms, handle dropdowns and checkboxes, and submit data - saving you from repetitive data entry.
## What Your Agent Can Do
* **Job applications** - Fill out lengthy application forms with your details
* **Account signups** - Register for services with your information
* **Government forms** - Navigate bureaucratic portals and submissions
* **Data entry** - Input records into CRMs, databases, or admin panels
## Example: Job Application
> "Go to the Google Careers page, find Software Engineer positions in NYC, and fill out an application with my resume and these details: \[your info]"
Your agent navigates the application portal, fills each field, uploads your resume, and submits.
## Example: Expense Reports
> "Using my workday profile, go to expense reports and submit these 5 receipts with descriptions: \[receipt details]"
Your agent logs into Workday, creates expense entries, categorizes them, and attaches documentation.
## Example: Government Portal
> "Go to the DMV appointment scheduler, find the earliest available slot for a license renewal in my zip code, and book it"
Your agent handles the clunky government website navigation for you.
## Example: CRM Data Entry
> "Using my salesforce profile, create a new lead with these details: \[contact info]. Add notes about how we met at the conference."
Your agent fills out the lead form with all the fields you specify.
## Example: Bulk Registration
> "Register for accounts on these 3 newsletter services with my email. Use these preferences: \[preferences]"
Your agent handles multiple signups.
For forms requiring authentication, set up a profile first so your agent is already logged in.
Watch via live view for sensitive forms. You can review before the agent clicks submit.
# Check & Monitor
Source: https://docs.smooth.sh/cli/use-cases/monitoring
Have your agent check websites and report back
Your agent can check websites on demand, monitor for changes, or gather status updates from various sources.
## What Your Agent Can Do
* **Check availability** - See if products are in stock or appointments available
* **Monitor prices** - Check current prices across multiple sites
* **Gather status** - Check dashboards, reports, or metrics
* **Verify content** - Confirm information is displaying correctly
## Example: Check Stock Availability
> "Check if the PS5 is in stock at Best Buy, Amazon, and Walmart. Tell me price and availability for each."
Your agent visits all three retailers and reports back with current status.
## Example: Monitor Competitor Pricing
> "Check our three main competitors' pricing pages and tell me if anything changed from last week"
Your agent visits each site and compares to your baseline.
## Example: Check Application Status
> "Using my visa-portal profile, log in and check the status of my application. Let me know if there are any updates."
Your agent navigates the portal and reports any changes.
## Example: Gather Daily Metrics
> "Using my analytics profile, go to our Google Analytics dashboard and get yesterday's traffic numbers, top pages, and conversion rate"
Your agent pulls metrics from your authenticated dashboards.
## Example: Verify a Deployment
> "Check our production site and make sure the new banner is showing on the homepage"
Your agent verifies that recent changes are live.
## Example: Track Flight Prices
> "Check Google Flights for NYC to London on March 15th. What's the cheapest option right now?"
Your agent searches and reports current prices.
For recurring checks, your agent can run the same task periodically. Just ask it again whenever you need an update.
# Online Research
Source: https://docs.smooth.sh/cli/use-cases/research
Let your agent research topics across the web
Your agent can browse the web, visit multiple sources, and compile research on any topic. It handles the tedious clicking and reading while you get the summarized results.
## What Your Agent Can Do
* **Compare products** - Research options across multiple sites and summarize findings
* **Gather market intel** - Check competitor websites, pricing pages, and news
* **Find information** - Search for specific facts across multiple sources
* **Monitor trends** - Check social media, forums, and news for mentions
## Example: Product Research
> "Research the top 5 project management tools. For each one, find the pricing, key features, and what users complain about in reviews. Give me a comparison."
Your agent visits Asana, Monday, Notion, ClickUp, and Jira - checking their pricing pages, feature lists, and review sites like G2 and Capterra.
## Example: Competitive Analysis
> "Go to our three main competitors' websites and find out what new features they've announced in the last month"
Your agent checks their blogs, changelogs, and announcement pages.
## Example: Lead Research
> "I'm meeting with Acme Corp tomorrow. Research them - find their recent news, key executives, company size, and any recent product launches"
Your agent visits their website, LinkedIn, Crunchbase, and news sites to build a briefing.
## Example: Technical Research
> "Find the best practices for implementing OAuth 2.0 in a React app. Check the official docs, Stack Overflow, and any recent blog posts"
Your agent gathers information from multiple technical sources and synthesizes it.
Be specific about what sources you want checked. "Research X" is broad, but "Research X by checking their website, recent news, and customer reviews on G2" gives better results.
# QA & Testing
Source: https://docs.smooth.sh/cli/use-cases/testing
Have your agent test your web application
Your agent can explore your web app, try different user flows, and report issues - like having a QA tester on demand.
## What Your Agent Can Do
* **Exploratory testing** - Click around and find broken things
* **User flow testing** - Walk through signup, checkout, or other critical paths
* **Visual inspection** - Check for UI issues, broken images, or layout problems
* **Cross-browser behavior** - Verify functionality works as expected
## Example: Test a User Flow
> "Go to localhost:3000, create a new account, add an item to cart, and complete checkout. Tell me if anything breaks or looks wrong."
Your agent walks through the entire flow and reports any issues encountered.
## Example: Find Broken Links
> "Go to our marketing site and check all the links on the homepage. Report any that are broken or lead to 404 pages."
Your agent systematically clicks through links and documents failures.
## Example: Test After Deployment
> "Go to staging.ourapp.com and test the new password reset flow. Try with a valid email, invalid email, and empty form. Report results."
Your agent tests edge cases and normal cases, documenting what happens.
## Example: Check Mobile Layout
> "Go to our landing page on a mobile device and check if all elements are visible and the navigation works properly."
Your agent uses mobile emulation to test responsive design.
## Example: Accessibility Spot Check
> "Go to our signup page and try to complete the form using only the keyboard. Report any issues with focus states or navigation."
Your agent tests keyboard accessibility and reports problems.
Use localhost access to test against your local dev server before deploying. The agent can catch issues before they reach production.
Your agent provides natural language reports about what it found, not automated test results. It's exploratory testing, not assertion-based testing.
# Custom Extensions
Source: https://docs.smooth.sh/features/custom-extensions
Learn how to use custom browser extensions with Smooth
## Overview
Custom extensions allow you to load your own Chrome extensions into the browser session. This is useful for adding guardrails, taking determistic actions, or for testing extensions.
```python Python theme={null}
# pip install smooth-py
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
# Upload a custom extension (must be a .zip file)
extension = smooth_client.upload_extension(file=open("comic-sans-extension.zip", "rb"))
# Get the extension ID
extension_id = extension.id
# Use the extension in a task
task = smooth_client.run(
"Navigate to example.com and take a screenshot",
extensions=[extension_id]
)
# List all uploaded extensions
extensions = smooth_client.list_extensions()
for ext in extensions:
print(f"Extension ID: {ext.id}")
# Delete extension
smooth_client.delete_extension(extension_id)
```
Extensions must be packaged as Chrome extension .zip files with a valid manifest.json. The extension will be loaded into the browser session and remain active for the duration of the task.
## Extension Lifecycle
Extensions are uploaded to Smooth servers and can be reused across multiple tasks. Use `.delete_extension()` to permanently delete it.
```python theme={null}
# Upload once
extension = smooth_client.upload_extension(file=open("my-extension.zip", "rb"))
# Use multiple times
task1 = smooth_client.run("Task 1", extensions=[extension.id])
task2 = smooth_client.run("Task 2", extensions=[extension.id])
# Delete extension
smooth_client.delete_extension(extension.id)
```
## Multiple Extensions
You can load multiple extensions in a single session by passing a list of extension IDs:
```python theme={null}
extension1 = smooth_client.upload_extension(file=open("extension1.zip", "rb"))
extension2 = smooth_client.upload_extension(file=open("extension2.zip", "rb"))
task = smooth_client.run(
"Your task here",
extensions=[extension1.id, extension2.id]
)
```
# File Download
Source: https://docs.smooth.sh/features/file-download
Learn how to use Smooth to download files
## Overview
The following demonstrates how to instruct Smooth to download and return files from the web.
```python Python theme={null}
# pip install smooth-py
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
task = smooth_client.run("Go to https://pdfobject.com/examples/passing-element-styled.html and download the pdf file.")
print(f"Agent response: {task.result()}")
print(f"Downloaded file archive URL: {task.downloads_url()}")
```
Downloaded files are automatically deleted from our servers after 24 hours.
# File Upload
Source: https://docs.smooth.sh/features/file-upload
Learn how to use Smooth to upload files
## Overview
The following demonstrates how to instruct Smooth to handle custom data files.
```python Python theme={null}
# pip install smooth-py
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
file_handle = client.upload_file(open("README.md", "rb"))
task = smooth_client.run("Upload the given file to https://www.azurespeed.com/Azure/UploadLargeFile to test the upload speed.", files=[file_handle.id])
...
```
Files can be reused across multiple tasks but are automatically deleted from our servers after 24 hours.
If you want to manually delete a file when it is no longer necessary, you can use `.delete_file`.
Smooth has access to the file content and can analyze it if requested. Be careful when dealing with sensitive data.
## Guides
Check out our example for an in-depth tutorial.
Learn how to use Smooth to upload files.
# Live share
Source: https://docs.smooth.sh/features/live-share
Get an interactive live view of the browser
## Overview
When running a task, you will receive a `live_url`, which can be used to view the agent actions live.
## Getting the URL
A `live_url` is returned when you run a task.
```python Python theme={null}
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
task = smooth_client.run(
task="",
)
print(f"Live URL: {task.live_url()}")
```
If Python is not your language of choice, check out our [API Reference](/api-reference/introduction).
## Customizations
You can customize the URL to make it interactive or full screen. Just pass the following parameters
* `interactive=true` to get an interactive view
* `embed=true` to get an embeddable view (ideal for iframes)
```python Python theme={null}
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
task = smooth_client.run(
task="",
)
print(f"Live URL: {task.live_url(interactive=True, embed=True)}")
```
# Persistent Sessions
Source: https://docs.smooth.sh/features/persistent-sessions
Persist authentication across browser sessions
Browser profiles allow you to persist cookies and authentication across sessions. Log in once, then reuse that authentication for future tasks.
## How It Works
1. **Create a Profile** - Call `client.create_profile(profile_id="my-profile")` to create a new profile.
2. **Authenticate** - Open a session with the profile and navigate to `live_url()` to log in manually. Your cookies are saved to the profile.
3. **Reuse** - In future sessions, pass the same `profile_id`. The agent uses the stored cookies automatically.
## Example
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
# First run: create profile and authenticate
client.create_profile(profile_id="my-account")
with client.session(profile_id="my-account", url="https://example.com") as session:
print(f"Log in here: {session.live_url()}")
input("Press Enter after logging in...")
# Future runs: authentication is already saved
with client.session(profile_id="my-account", url="https://example.com") as session:
session.run_task("Do something that requires login")
```
## Learn More
Create, list, and delete profiles
Step-by-step guide for authenticated scraping
# Proxies
Source: https://docs.smooth.sh/features/proxies
Learn how to use a proxy with Smooth
## Overview
Proxies act as intermediaries between Smooth and the internet. They can help you bypass restrictions and enhance stealth mode.
Using proxies with Smooth allows you to run tasks from a different IP address, which can be useful for web scraping and automation tasks.
## Benefits of Using Proxies
1. **Stealth**: Proxies can be used to enhance stealth mode.
2. **Intranet**: Access content that is only available within your company's intranet.
3. **Load Balancing**: Distribute requests across multiple proxies to avoid hitting rate limits on target websites.
## Using Proxies with Smooth
To use a proxy with Smooth, you need to specify the proxy server details in your task parameters. Here’s how you can do it:
```python Python theme={null}
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
task = smooth_client.run(
task="",
proxy_server="",
proxy_username="",
proxy_password="",
)
print(f"Live URL: {task(.live_url())}")
print(f"Agent response: {task.result()}")
```
If Python is not your language of choice, check out our [API Reference](/api-reference/introduction).
# Video recording
Source: https://docs.smooth.sh/features/session-recording
Get a video recording of the agent run
## Overview
When running a task, you can use `enable_recording` to record a video of the agent run.
## Getting the URL
Use `recording_url()` to get the video recording after the task completes.
```python Python theme={null}
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
task = smooth_client.run(
task="",
enable_recording=True,
)
print(f"Live URL: {task.live_url()}")
print(f"Session recording URL: {task.recording_url()}") # Waits for task completion
```
If Python is not your language of choice, check out our [API Reference](/api-reference/introduction).
# Structured output
Source: https://docs.smooth.sh/features/structured-output
Learn how to use structured outputs
## Overview
Structured outputs allow you to write deterministic code based on the agent's output.
To activate structured outputs, set the `response_model` field by passing either a Pydantic Model or a JSON schema.
```python Python theme={null}
# pip install smooth-py
from smooth import SmoothClient
from pydantic import BaseModel
class MyStructuredOutput(BaseModel):
my_output_data: list[int] = Field(description="A list of random numbers.")
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
task = smooth_client.run("Output five random numbers.", response_model=MyStructuredOutput)
# or
# ..., response_model = {
# "type": "object",
# "properties": {
# "my_output_data": {
# "description": "A list of random numbers.",
# "items": {
# "type": "integer"
# },
# "type": "array"
# }
# }
# }
print(f"Agent response: {task.result()}")
```
# P2P Tunnel
Source: https://docs.smooth.sh/features/use-my-ip
Run browser automations through a peer-to-peer tunnel to your machine
By setting `proxy_server` to `"self"`, Smooth creates a **peer-to-peer tunnel** between the remote Smooth browser and your local machine, routing all browser traffic through your network. This is useful for:
* **Accessing localhost** — The tunnel lets the Smooth browser reach services running on your machine (e.g. `http://localhost:3000`), making it easy to test local development environments
* **Avoiding bot detection** — Many websites trust residential IPs more than datacenter IPs
* **Geo-localized automations** — Access location-specific content based on your actual location
* **Location-aware queries** — Run searches like "restaurants near me" that rely on IP geolocation
* **Accessing region-restricted content** — Browse content only available in your region
## Usage
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
# Simple task using your IP
task = client.run(
task="Search for 'coffee shops near me' and get the top 5 results",
proxy_server="self"
)
print(task.result().output)
```
## Accessing Localhost
Because the P2P tunnel connects the Smooth browser directly to your machine, the browser can reach any service running locally — just use `localhost` or `127.0.0.1` as you normally would:
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
# Access a local dev server running on port 3000
task = client.run(
task="Go to the homepage and tell me what you see",
url="http://localhost:3000",
proxy_server="self"
)
print(task.result().output)
```
This makes it straightforward to run automations or tests against a local app without exposing it to the internet.
## With Sessions
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
with client.session(proxy_server="self") as session:
session.run_task(
task="Search for nearby Italian restaurants",
url="https://www.google.com/maps"
)
results = session.extract(
schema={
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string", "description": "Restaurant name"},
"rating": {"type": "number", "description": "Star rating"},
"address": {"type": "string", "description": "Street address"}
}
}
},
prompt="Extract the top 5 restaurants from the results"
)
for restaurant in results.output:
print(f"{restaurant['name']} - {restaurant['rating']} stars")
```
## How It Works
When you set `proxy_server="self"`:
1. Smooth establishes a peer-to-peer tunnel between the remote browser and your machine
2. All browser traffic is routed through your local network
3. Websites see your IP address instead of Smooth's datacenter IP
4. Services running on `localhost` become reachable from the Smooth browser
5. The tunnel is automatically closed when the task or session ends
**Pro Tip:** Using your own IP is the most effective way to avoid captchas. Websites see the same IP they would see if you were browsing manually.
This feature is most useful when running automations from a local machine with a residential IP. If your code is running from a datacenter, the tunnel will still use a datacenter IP.
# Zero Data Retention
Source: https://docs.smooth.sh/features/zero-data-retention
Enable Zero Data Retention for enhanced data privacy
## Overview
Zero Data Retention is an enterprise feature that provides enhanced data privacy by allowing you to delete all data associated with completed tasks. When enabled, you have full control over when task data is removed from our systems.
This feature is available upon request on the **Enterprise plan**. Contact us to request Zero Data Retention for your organization.
## Deleting Task Data
Once Zero Data Retention is enabled for your account, you can request the deletion of data for any completed task using our dedicated API endpoint.
```bash cURL theme={null}
curl -X DELETE http://api.smooth.sh/api/v1/task//data \
-H "apikey: YOUR_API_KEY"
```
Task data deletion is permanent and cannot be undone. Make sure you have retrieved any necessary information before deleting task data.
## Use Cases
Zero Data Retention is ideal for organizations that:
* Handle sensitive or confidential information
* Must comply with strict data retention policies
* Want to minimize their data footprint
* Require enhanced privacy controls
# File uploads
Source: https://docs.smooth.sh/guides/file-uploads
Learn how to use Smooth to upload files
## Overview
This guide demonstrates how to use Smooth to upload files to websites.
You can use the `upload_file()` method to upload files that you will pass on to Smooth.
```python Python theme={null}
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
file = smooth_client.upload_file(
file=open("/path/to/file.png", "rb"),
name="meaningful_file_name.png"
)
```
When uploading multiple files, we recommend using meaningful file names to help Smooth disambiguate their content.
An optional `purpose` can be given to further highlight the role of the file (i.e., `purpose="the bank statement pdf"`).
Run your task using the `files` parameter.
```python Python theme={null}
task = smooth_client.run(
task="Give me an alt text for this image",
url="https://pallyy.com/tools/alt-text-generator",
files=[file.id]
)
print(f"Live URL: {task.live_url()}")
task_result = task.result()
print(f"Agent Response: {task_result.output}")
print(f"Task Video: {task_result.recording_url()}")
```
The agent will navigate to the website, upload the file, and return the alt text.
Once the task is finished and the uploaded files are not necessary anymore, you can delete them as following:
```python Python theme={null}
smooth_client.delete_file(file_id=file.id)
```
This step is optional, as files are automatically removed after 24 hours.
## Best Practices
* **Use Descriptive File Names**: Choose meaningful names for your files to make it easier to identify their content later.
* **Check File Size Limits**: Ensure your files do not exceed the maximum size limits set by the platform you are uploading to.
* **Include File Types**: When specifying a file name, make sure to include the file extension, such as `.pdf` or `.png`
## Community
Join our community for support and showcases
# Dynamic multi-step forms
Source: https://docs.smooth.sh/guides/fill-in-multi-step-dynamic-forms
Automate filling in complex multi-step forms
## Overview
This guide demonstrates how to use Smooth to fill in complex, multi-step forms. You'll learn how to structure the task prompt for these tasks.
Write detailed instructions that tell the agent exactly what to do. The best practice for form filling is to structure the prompt in 3 parts:
1. The goal
2. All information needed to fill-in the form
3. The expected output
```python Python theme={null}
task_instructions = """
Go to octopus.energy and get a quote for gas + electricity. Use the following information:
- 1, Garlic Row, Cambridge CB5 8HW
- Medium consumption
Return the final monthly estimate for all available tariffs.
"""
```
Run the task with a higher number of steps.
```python Python theme={null}
task = smooth_client.run(
task=task_instructions,
max_steps=64, # Multi-step forms may require many steps
)
print(f"Live URL: {task.live_url()}")
# Get the result
result = task.result()
print(f"Insurance quote result: {result}")
```
The agent will adapt to the form's structure, whether it's a single long page or multiple sequential steps, and handle dynamic fields that appear based on previous selections.
## Key Benefits
* **Dynamic Adaptation**: Agent handles forms that change based on user input
* **Multi-Step Navigation**: Seamlessly moves through complex form sequences
* **Error Recovery**: Can handle validation errors and retry with corrected information
## Best Practices
* **Give Clear Default Instructions**: Specify how to handle missing information scenarios
* **Set Adequate Step Limits**: Multi-step forms often require more steps than simple tasks
* **Include Guidance**: If needed, tell the agent how to handle common pitfalls
## Common Use Cases
You can use this approach for automating a variety of tasks, such as:
* Transfering data across systems
* Getting quotes
* Submitting forms
## Community
Join our community for support and showcases
# Flight search
Source: https://docs.smooth.sh/guides/flight-search
Search flights using Smooth
## Overview
This guide demonstrates how to use Smooth to search for flights using Google Flights.
Structure your flight search task prompt.
```python Python theme={null}
flight_search = """
Search for flights on Google Flights from New York to London, departing tomorrow, coming back in a week. Find the cheapest round-trip option and return:
- Flight details (airline, flight number, times)
- Price, duration, and number of stops
"""
```
Run your flight search task.
```python Python theme={null}
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
task = smooth_client.run(
task=flight_search,
enable_recording=True
)
print(f"Live URL: {task.live_url()}")
task_result = task.result()
print(f"Agent Response: {task_result.output}")
print(f"Task Video: {task_result.recording_url()}")
```
The agent will navigate Google Flights, input your search criteria, and analyze the results to find the best option.
This is a fun task! You could use the same template for automating procurement, online orders, and more.
## Community
Join our community for support and showcases
# Government portals
Source: https://docs.smooth.sh/guides/government-portals
Extract data from government portals like SEC filings
This guide demonstrates accessing public information without authentication. For authenticated access take a look at our [Guide: Scrape behind login walls](/guides/scrape-behind-login-walls).
## Overview
This guide shows how to use Smooth to extract information from government portals. You'll learn to automate the search and extraction of financial data from complex government websites, perfect for financial analysis, compliance research, and due diligence.
Navigate to the SEC's EDGAR database and search for a specific company's filings.
```python Python theme={null}
sec_search_task = """
Go to https://www.sec.gov/search-filings and search for "Apple Inc".
Find their most recent 10-K filing.
Extract:
- Filing date
- Document title
- Total net sales figure for the most recent fiscal year
Return the extracted info.
"""
```
Run the search task to find Apple's latest 10-K filing.
```python Python theme={null}
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
task = smooth_client.run(
task=sec_search_task,
enable_recording=True
)
print(f"Live URL: {task.live_url()}")
task_result = task.result()
print(f"Agent Response: {task_result.output}")
print(f"Task Video: {task.recording_url()}")
```
The agent will navigate to the SEC portal, perform the search, identify the most recent 10-K filing, and extract the total net sales.
## Use Cases
* **Financial Analysis**: Extract key financial metrics from public filings
* **Compliance Research**: Monitor regulatory submissions and updates
* **Due Diligence**: Gather comprehensive financial data for investment decisions
* **Competitive Intelligence**: Track competitors' financial performance over time
* **Regulatory Monitoring**: Stay updated on companies' regulatory disclosures
* **Investment Research**: Access detailed financial statements and footnotes
Smooth can navigate complex government portal layouts and extract structured data even when document formats vary between filings.
## Community
Join our community for support and showcases
# Invoice retrieval
Source: https://docs.smooth.sh/guides/invoice-retrieval
Learn how to retrieve invoice information with Smooth
## Overview
This guide demonstrates how to use Smooth to retrieve invoice information from subscription services like Calendly. You'll learn to authenticate manually once via a live URL, then have Smooth navigate to billing sections to extract payment data automatically.
Create a profile and session for Calendly. The profile will persist your authentication cookies.
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
# Create the profile first (only needed once)
client.create_profile(profile_id="calendly-billing")
with client.session(profile_id="calendly-billing", url="https://calendly.com") as session:
# Get the live URL for manual authentication
print(f"Please log in at: {session.live_url()}")
# Wait for user to authenticate
input("Press Enter after you've logged in to Calendly...")
# Now extract invoice data
result = session.run_task(
task="Go to the billing page and find my most recent invoice"
)
invoice = session.extract(
schema={
"type": "object",
"properties": {
"invoice_date": {"type": "string", "description": "Invoice date"},
"amount_paid": {"type": "number", "description": "Amount paid"},
"currency": {"type": "string", "description": "Currency code"},
"invoice_number": {"type": "string", "description": "Invoice number"}
}
},
prompt="Extract the most recent invoice details from this page"
)
print(f"Invoice: {invoice.output}")
```
Open the `live_url` in your browser and log in to Calendly. Once authenticated, press Enter to continue.
In future runs, use the same profile ID to skip manual authentication and retrieve invoices directly.
```python Python theme={null}
with client.session(profile_id="calendly-billing", url="https://calendly.com") as session:
session.run_task(
task="Navigate to the billing page"
)
invoices = session.extract(
schema={
"type": "array",
"items": {
"type": "object",
"properties": {
"invoice_date": {"type": "string", "description": "Invoice date"},
"amount_paid": {"type": "number", "description": "Amount paid"},
"currency": {"type": "string", "description": "Currency code"},
"invoice_number": {"type": "string", "description": "Invoice number"}
}
}
},
prompt="Extract all invoices from the billing page"
)
for inv in invoices.output:
print(f"{inv['invoice_date']}: {inv['currency']} {inv['amount_paid']}")
```
## Use Cases
* **Expense Tracking**: Automatically extract billing information for accounting
* **Budget Monitoring**: Track recurring payments across multiple services
* **Invoice Automation**: Integrate payment data with expense management systems
* **Audit Compliance**: Maintain accurate records of business subscriptions
This approach works with any web service that has a billing dashboard. The key is authenticating manually first, then letting the agent navigate to extract the specific invoice data you need.
## Community
Join our community for support and showcases
# Lead capture
Source: https://docs.smooth.sh/guides/lead-capture
Extract structured contact data for lead generation
## Overview
This guide demonstrates how to use Smooth to capture leads from conference speaker directories. You'll learn to extract speaker information from The AI Summit London website and format the output as structured data for your CRM or lead generation workflows.
Structure your lead capture task with specific requirements for the data you want to extract.
```python Python theme={null}
lead_capture_task = """
Go to https://london.theaisummit.com/conference-agenda/speakers-2025 and extract information about all speakers.
For each speaker, collect:
- Full name
- Speaker description
Return the data in JSON format as an array of speaker objects.
"""
```
Run your lead capture task to extract all speaker information from the conference website.
```python Python theme={null}
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
task = smooth_client.run(
task=lead_capture_task,
enable_recording=True
)
print(f"Live URL: {task.live_url()}")
task_result = task.result()
print(f"Agent Response: {task_result.output}")
print(f"Task Video: {task_result.recording_url}")
```
The agent will navigate the speaker directory, extract information from each profile, and return structured JSON data.
For more control over the output structure, specify exactly how you want the data formatted.
```python Python theme={null}
structured_extraction = """
Go to https://london.theaisummit.com/conference-agenda/speakers-2025 and extract speaker information.
Return the data in this exact JSON format:
{
"total_speakers": ,
"speakers": [
{
"name": "Full Name",
"title": "Job Title",
"company": "Company Name",
}
]
}
Ensure all fields are included even if empty (use None for missing data).
"""
task = smooth_client.run(
task=structured_extraction,
enable_recording=True
)
result = task.result()
speakers_data = result.output
print(speakers_data)
```
## Use Cases
* **Conference Networking**: Build prospect lists from industry events
* **Competitor Research**: Analyze speaker lineups at competitor events
* **Partnership Opportunities**: Identify potential collaborators or customers
* **Market Intelligence**: Track industry thought leaders and trends
* **Sales Prospecting**: Generate targeted outreach lists
## Best Practices
* **Respect Website Terms**: Always check robots.txt and terms of service
* **Data Quality**: Validate extracted data before importing to your systems
* **Privacy Compliance**: Follow GDPR and other privacy regulations
This approach works for any public directory or listing page. The key is being specific about the data structure you want in your task prompt.
## Community
Join our community for support and showcases
# QA testing
Source: https://docs.smooth.sh/guides/qa-testing
Automate QA testing workflows
## Overview
This guide demonstrates how to use Smooth for automated QA testing. You'll learn to create comprehensive test scenarios that validate user workflows, form submissions, and application functionality.
Structure your QA test with clear validation criteria. Define what constitutes success and failure for each test case.
```python Python theme={null}
test_scenario = """
Test this flow on apple.com:
1. Navigate to the apple website
2. Go to the page to buy the latest iphone
3. Select the basic configuration, black, maximum storage
4. No trade in
5. Return the price options
"""
```
Execute your test scenario.
```python Python theme={null}
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
task = smooth_client.run(
task=test_scenario,
enable_recording=True # For QA testing, we recommend enabling recording for later analysis
)
print(f"Live URL: {task.live_url()}")
task_result = task.result()
print(f"Agent Response: {task_result.output}")
print(f"Task Video: {task_result.recording_url}")
```
The agent will systematically execute each test step and provide the requested information.
Test boundary conditions and error handling by creating scenarios with invalid inputs or unusual user behavior.
```python Python theme={null}
edge_case_test = """
Test form validation on https://github.com/signup:
1. Try submitting with empty required fields
2. Enter invalid email formats (test@, @example.com, plain text)
3. Verify error messages are clear and helpful
Report any issues. If none, just say it.
"""
task = smooth_client.run(task=edge_case_test, max_steps=24)
task_result = task.result()
print(f"Agent Response: {task_result.output}")
print(f"Task Video: {task_result.recording_url}")
```
## Key Benefits
* **Automated Regression Testing**: Run consistent tests across application updates
* **Test Responsive Behavior**: Verify functionality across different screen sizes
* **User Experience Verification**: Validate complete user journeys from start to finish
* **Error Detection**: Identify UI bugs, broken links, and form validation issues
## Best Practices
* **Write Detailed Test Cases**: Include specific validation criteria and expected outcomes
* **Test User Journeys**: Focus on complete workflows rather than isolated features
* **Document Failures Clearly**: Capture videos for later analysis
## Common QA Test Types
You can automate various testing scenarios:
* **Functional Testing**: Verify features work as intended
* **Form Validation**: Test input validation and error handling
* **Navigation Testing**: Ensure all links and menus function correctly
QA testing with Smooth provides real browser interaction, making it ideal for testing web applications and complex user interfaces that traditional testing tools might miss.
## Community
Join our community for support and showcases
# Real-time people enrichment
Source: https://docs.smooth.sh/guides/real-time-people-enrichment
Enrich contact profiles with real-time data
This guide demonstrates unauthenticated lookups. For authenticated access take a look at our [Guide: Scrape behind login walls](/guides/scrape-behind-login-walls).
## Overview
This guide shows how to use Smooth to enrich people profiles with real-time social media data. You'll learn to extract public data from LinkedIn and Twitter, perfect for sales research, lead qualification, and competitive intelligence.
Extract the title and details of someone's most recent LinkedIn article or post.
```python Python theme={null}
linkedin_enrichment_task = """
Go to Bill Gates' LinkedIn profile and find his latest article.
Extract:
- Title of the latest article/post
- Publication date
- Brief summary of the content (2-3 sentences)
- Number of likes/reactions if visible
"""
```
Run the enrichment task to get the latest LinkedIn article.
```python Python theme={null}
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
task = smooth_client.run(
task=linkedin_enrichment_task,
enable_recording=True
)
print(f"Live URL: {task.live_url()}")
task_result = task.result()
print(f"Agent Response: {task_result.output}")
print(f"Task Video: {task_result.recording_url}")
```
The agent will navigate to the LinkedIn profile, identify the most recent article, and extract the relevant details.
Extract topics and themes from someone's Twitter profile.
```python Python theme={null}
twitter_enrichment_task = """
Go to Bill Gates' Twitter profile and analyze the visible posts.
Identify the top 3-5 themes or topics he frequently discusses.
Return the top themes and topics he frequently discusses.
"""
task = smooth_client.run(
task=twitter_enrichment_task,
enable_recording=True
)
result = task.result()
print(f"Agent response: {result.output}")
```
## Use Cases
* **Sales Intelligence**: Research prospects before outreach calls
* **Competitive Analysis**: Track competitor executives' public statements
* **Partnership Research**: Understand potential partners' current focus areas
* **Media Monitoring**: Stay updated on key industry leaders' perspectives
* **Lead Qualification**: Assess prospect engagement and interests
* **Content Strategy**: Identify trending topics among target audiences
Smooth adapts and finds alternative ways to access the information even when social media platforms update their layouts.
## Community
Join our community for support and showcases
# Scrape behind login walls
Source: https://docs.smooth.sh/guides/scrape-behind-login-walls
Learn how to scrape data from protected websites
## Overview
This guide demonstrates how to scrape data from websites that require user authentication by using browser sessions. You'll learn to launch a session, authenticate manually via a live URL, and then run automated tasks within the same authenticated session.
Create a profile and session. The profile will persist your authentication cookies for future sessions.
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
# Create the profile first (only needed once)
client.create_profile(profile_id="gmail-session")
with client.session(profile_id="gmail-session", url="https://mail.google.com") as session:
# Get the live URL for manual authentication
print(f"Please log in at: {session.live_url()}")
# Wait for user to authenticate
input("Press Enter after you've logged in...")
# Now run tasks in the authenticated session
result = session.run_task(
task="Get the subject and sender of the most recent email"
)
print(f"Last email: {result.output}")
```
Open the `live_url` in your browser and log in to Gmail. Once authenticated, press Enter to continue with the automated task.
In future runs, use the same profile ID to skip manual authentication. Your login state is already saved.
```python Python theme={null}
with client.session(profile_id="gmail-session", url="https://mail.google.com") as session:
result = session.run_task(
task="Get the subject and sender of my 5 most recent emails",
response_model={
"type": "array",
"items": {
"type": "object",
"properties": {
"subject": {"type": "string", "description": "Email subject"},
"sender": {"type": "string", "description": "Sender name or email"}
}
}
}
)
for email in result.output:
print(f"From: {email['sender']} - {email['subject']}")
```
## Key Benefits
* **Persistent Authentication**: Profiles maintain login state across multiple sessions
* **Manual Control**: You handle the authentication process manually for security
* **Automated Execution**: Once authenticated, run complex tasks automatically
* **Session Reuse**: The same profile can be used for multiple related tasks
## Best Practices
* Use descriptive profile IDs for better organization
* Keep profiles secure and don't share profile IDs
* Test authentication manually before running automated tasks
* Handle rate limits and be respectful to the target website
Browser profiles persist cookies and authentication state, making them perfect for accessing protected content while maintaining security through manual authentication.
## Community
Join our community for support and showcases
# Social media automation
Source: https://docs.smooth.sh/guides/social-media-automation
Automate actions on social media using Smooth
## Overview
This guide demonstrates how to use Smooth to automate sending a LinkedIn connection. You'll learn to authenticate to LinkedIn via a live URL and then run automated tasks to connect with specific professionals.
Create a profile and session for LinkedIn. The profile will persist your authentication cookies.
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
# Create the profile first (only needed once)
client.create_profile(profile_id="linkedin-automation")
with client.session(profile_id="linkedin-automation", url="https://www.linkedin.com") as session:
# Get the live URL for manual authentication
print(f"Please log in at: {session.live_url()}")
# Wait for user to authenticate
input("Press Enter after you've logged in to LinkedIn...")
# Now run tasks in the authenticated session
result = session.run_task(
task="""
Search for "Antonio Vespoli Circlemind" on LinkedIn.
Find his profile and send a connection request with the message:
"Hi Antonio, I love Smooth. Would love to connect!"
"""
)
print(f"Agent response: {result.output}")
```
Open the `live_url` in your browser and log in to LinkedIn. Once authenticated, press Enter to continue.
In future runs, use the same profile ID to skip manual authentication.
```python Python theme={null}
with client.session(profile_id="linkedin-automation", url="https://www.linkedin.com/feed") as session:
result = session.run_task(
task="Like the first 3 posts"
)
print(f"Agent response: {result.output}")
```
## Advanced Use Cases
* **Bulk Connections**: Connect with multiple professionals in your industry
* **Content Engagement**: Automatically like and comment on posts from your network on Twitter, Linkedin, and more
* **Lead Generation**: Find and connect with potential customers or partners
* **Profile Management**: Update your status, share content, or manage your profile
## Best Practices
* **Respect Rate Limits**: LinkedIn has strict limits on daily connection requests
* **Respect Website Terms**: Use your best judgment to respect the website Terms of Service.
Always follow LinkedIn's Terms of Service and use automation responsibly. Manual authentication ensures compliance with security policies while enabling powerful automation capabilities.
## Community
Join our community for support and showcases
# Introduction
Source: https://docs.smooth.sh/index
Welcome to Smooth
## Overview
Smooth is a browser agent that can go to the web and perform tasks autonomously. It's the most reliable browser agent yet while also being 7x cheaper and 7x faster than traditional browser use.
* **Most reliable browser agent yet**: Smooth is state-of-the-art on the leading browser automation benchmarks.
* **Blazing fast**: 7x faster than browser use. Ideal for time-sensitive use cases.
* **Massively cheaper**: 7x more affordable than browser use.
* **Plug-and-play**: Run a task in just 4 lines of code, making it easy to integrate into your workflow.
* **Infinitely scalable & serverless API**: From one-off runs to millions of executions, Smooth scales instantly with zero infrastructure management.
* **Instant browser spin-up**: Spins-up browser sessions that are immediately ready for action with no infrastructure management.
* **Custom proxy configuration**: Tailor the browsing configuration with flexible proxy settings to suit your needs.
* **Persistent sessions**: Maintain state across multiple tasks to preserve cookies and authentication.
* **Auto-CAPTCHA solvers**: Bypass CAPTCHA challenges automatically, allowing for uninterrupted task execution.
**Enterprise ready:** Security compliance reports - On-prem options - BAA agreements - SSO
## Getting started
Run your first task in 4 lines of code.
Step-by-step instructions to launch your first browser task.
Explore our full APIs for advanced integration.
## Need inspiration?
Browse our step-by-step tutorials and guides.
Extract data from secured sites with persistent sessions.
Fill-in interactive forms with step-by-step instructions.
Automate interactions on any social media platform.
Quickly find and compare flights across multiple sites.
Enrich people or company profiles with fresh information.
Gather potential customers from any online resource.
Access and interact with official government services online.
Automatically retrieve invoices from any website.
## Community
Join our community for support and showcases
# Custom tools
Source: https://docs.smooth.sh/methods/custom-tools
Extend Smooth with custom Python functions that run in your environment
## Overview
Custom tools allow you to give Smooth any arbitrary function as a tool. This is similar to MCP (Model Context Protocol) but without having to deal with servers or additional infrastructure.
Custom tools can be used for virtually anything, including:
* **Browser interaction** - Execute JavaScript to manipulate the DOM or extract data
* **Human-in-the-loop** - Ask questions and get input from a human operator
* **Handling OTP scenarios** - Retrieve one-time passwords from your email or SMS service
* **Database operations** - Add, update, or query data in your local or remote databases
* **API integrations** - Call external APIs that require credentials or complex logic
* **File system operations** - Read configuration files, process local data, etc.
* **Custom validation** - Implement business-specific validation logic
## Basic Usage
Use the `@client.tool()` decorator to register a Python function as a custom tool:
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
@client.tool(
name="ask_human",
description="Asks a human operator for input when you need clarification or additional information.",
inputs={
"question": {
"type": "string",
"description": "The question to ask the human operator",
}
},
output="string"
)
def ask_human(question: str):
"""
Prompts a human operator for input and returns their response.
Useful for human-in-the-loop workflows.
"""
print(f"\nQuestion for human: {question}")
response = input("Your answer: ")
return response
# Use the custom tool in a task
task = client.run(
task="Ask the human for their favorite book and then find the price on Amazon",
custom_tools=[ask_human]
)
```
## Browser JavaScript Execution
Within custom tools, you can execute JavaScript directly in the browser context. Your JavaScript code can access to the DOM, browser APIs, and page state.
To use this feature, add a `task: smooth.TaskHandle` parameter to your tool function:
```python Python theme={null}
import smooth
client = smooth.SmoothClient(api_key="cmzr-YOUR_API_KEY")
@client.tool(
name="extract_page_data",
description="Extracts and analyzes data from the current page using JavaScript",
inputs={},
output="any"
)
def extract_page_data(task: smooth.TaskHandle):
# Execute JavaScript in the browser and return the result
result = task.exec_js("""
() => {
// Full access to DOM and browser APIs
const data = {
title: document.title,
url: window.location.href,
links: Array.from(document.querySelectorAll('a')).map(a => a.href),
hasLoginForm: !!document.querySelector('input[type="password"]'),
visibleText: document.body.innerText.substring(0, 50)
};
return data;
}
""")
# Process the result in Python
return f"Extracted {len(result['links'])} links on {result['title']}"
```
Use JavaScript for browser-level automation and data extraction, and Python for backend processing and integrations. Combine both for powerful workflows.
## Tool Decorator Parameters
The `@client.tool()` decorator accepts the following parameters:
The name of the tool that the agent will see and use to call it. Use descriptive, clear names.
A clear description of what the tool does and when to use it. The agent uses this to decide when to call your tool.
A dictionary describing the input parameters for the tool. Each key is a parameter name, and the value is an object with:
* `type` (string): The data type (e.g., "string", "number", "boolean", "object", "array")
* `description` (string): A clear description of what this parameter is for
The return type of the tool (e.g., "string", "number", "boolean", "object", "array")
## Error Handling
Custom tools support two types of error handling:
### ToolCallError (Non-Fatal)
Use `ToolCallError` for expected errors that the agent should handle gracefully. The error message will be sent to the agent, allowing it to retry or adjust its approach.
```python theme={null}
from smooth import ToolCallError
@client.tool(
name="validate_code",
description="Validates a verification code",
inputs={"code": {"type": "string", "description": "The code to validate"}},
output="boolean"
)
def validate_code(code: str):
if not code.isdigit():
raise ToolCallError("Code must contain only digits")
if len(code) != 6:
raise ToolCallError("Code must be exactly 6 digits")
return True
```
### Fatal Exceptions
Any other exception raised by your code is considered fatal and will immediately interrupt the task execution.
```python theme={null}
@client.tool(
name="query_database",
description="Queries the user database",
inputs={"user_id": {"type": "string", "description": "User ID to query"}},
output="object"
)
def query_database(user_id: str):
try:
# Database query logic
result = db.query(user_id)
return result
except ConnectionError:
# This will stop the task immediately
raise Exception("Database connection failed - critical error")
```
**Tool Descriptions**: Write clear, specific descriptions for your tools and inputs. The agent relies on these descriptions to decide when and how to use your tools effectively.
## Best Practices
1. **Keep tools simple to use** - Each tool should do one thing well
2. **Use descriptive names** - Name tools clearly so the agent knows when to use them
3. **Handle errors gracefully** - Use `ToolCallError` for recoverable errors
4. **Validate inputs** - Check that inputs are in the expected format before processing
5. **Return meaningful values** - Provide clear, actionable responses that help the agent continue
6. **Test independently** - Test your tool functions separately before using them in tasks
# Overview
Source: https://docs.smooth.sh/methods/overview
Understanding Simple Tasks vs Session Workflows
Smooth provides two ways to automate browser tasks, each suited for different use cases:
* **Simple Task** — Run a task with a single method call. No session management required.
* **Session Workflow** — Multi-step execution where you can orchestrate smaller tasks, navigate to URLs, and extract data within a persistent browser session.
## Simple Task
The simplest way to run a browser automation. Give Smooth a task, and the agent handles everything autonomously.
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
task = client.run("Find the cheapest flight from NYC to LA")
print(task.result())
```
**Characteristics:**
* One-shot execution
* Agent handles navigation and actions autonomously
* Best for standalone tasks and quick automations
Full documentation for client.run()
***
## Session Workflow
For complex workflows, create a browser session and orchestrate multiple steps. This gives you fine-grained control and higher reliability by breaking tasks into smaller, composable actions.
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
with client.session() as session:
session.run_task("Search for flights from NYC to LA leaving tomorrow")
results = session.extract(FlightSchema)
session.run_task("Select the cheapest option")
```
**Characteristics:**
* Break big tasks into smaller, reliable tasks
* Mix deterministic actions (goto) with agent tasks (run\_task)
* Extract structured data at any point in the workflow
* Best for complex workflows requiring higher reliability
* Great for multi-turn integrations in agents
Full documentation for browser sessions
***
Under the hood, `client.run()` is a convenient shorthand that creates a session, runs a single task, and returns the result.
# Manage Profiles
Source: https://docs.smooth.sh/methods/profiles
Create, list, and delete browser profiles
For an overview of how profiles work, see [Persistent Sessions](/features/persistent-sessions).
## Create a Profile
Before using a profile, you must create it:
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
# Create a new profile
client.create_profile(profile_id="my-profile")
```
## Use a Profile
Pass the `profile_id` when creating a session or running a task:
```python Python theme={null}
# Use the profile in a session
with client.session(profile_id="my-profile", url="https://example.com") as session:
session.run_task("Log in with my credentials")
# Authentication is now saved to the profile
```
## List Profiles
```python Python theme={null}
response = client.list_profiles()
print(f"Profile IDs: {response.profile_ids}")
```
## Delete a Profile
```python Python theme={null}
client.delete_profile(profile_id="my-profile")
```
If Python is not your language of choice, check out our [API Reference](/api-reference/introduction).
# Create Session
Source: https://docs.smooth.sh/methods/session
Create browser sessions for multi-step workflows
Browser sessions allow you to run multiple sequential tasks in a single browser instance. This enables you to break complex workflows into smaller, more reliable steps.
## Creating a Session
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
with client.session() as session:
session.run_task("Search for flights from NYC to LA")
data = session.extract(schema)
session.run_task("Select the cheapest option")
```
When using the `with` statement, the session is automatically closed when the block exits. Otherwise, you must call `session.close()` manually.
## Request
All parameters available when creating a session.
The starting URL for the session. If not provided, the browser will start on a blank page.
Example: `https://amazon.com`
The type of device for the session. Choose between `mobile` or `desktop`. Default: `mobile`.
Example: `desktop`
Toggles the option to record a video of the session. Default: `True`.
The agent that will run tasks in this session. Currently, only `smooth` is available. Default: `smooth`.
### Advanced Parameters
List of allowed URL patterns using wildcard syntax. If None, all URLs are allowed.
Example: `["google.com/*", "*mydomain.*//*"]`
A list of file ids to be passed to the session.
Check out our guide on [File uploads](/guides/file-uploads).
The browser profile ID to be utilized. Each profile retains its own state, including login credentials and cookies. You must create the profile first using `client.create_profile()`. See [Browser Profiles](/methods/profiles).
Example: `profile_12345`
If true, the profile specified by `profile_id` will be loaded in read-only mode. Changes made during the session will not be saved back to the profile. Default: `False`.
Enable adblock for the browser session. Default: `True`.
Activates stealth mode for the browser, which helps in avoiding detection. Default: `False`.
The hostname or IP address of the proxy server that will be used for the session.
Set to `"self"` to create a P2P tunnel through your machine, routing traffic via your IP and enabling access to localhost. See [P2P Tunnel](/features/use-my-ip) for details.
Example: `proxy.example.com` or `self`
The username for authenticating with the proxy server, if authentication is required.
Example: `user123`
The password for authenticating with the proxy server, if authentication is required.
Example: `password123`
List of client certificates to use when accessing secure websites. Each certificate is a dictionary with the following fields:
* `file`: p12 file object to be uploaded (e.g., open('my\_cert.p12', 'rb'));
* `password` (optional): The password for the certificate file, if applicable.
```python Python theme={null}
with client.session(
certificates=[{
"file": open("my_cert.p12", "rb"),
"password": "my_password"
}]
) as session:
...
```
Additional tools to enable for tasks in this session. Each tool is a `{tool_name: tool_kwargs}` pair. Use `tool_kwargs = None` for the default configuration of any tool.
See the [Tools](/methods/tools-overview) page for a complete list of available tools and their configuration options.
```python Python theme={null}
with client.session(
additional_tools={
"screenshot": {"full_page": True},
"hover": None
}
) as session:
...
```
A list of custom Python functions that the agent can call during task execution. Custom tools run in your local environment and can be used for OTP handling, human-in-the-loop scenarios, database operations, API integrations, and more.
See the [Custom Tools](/methods/custom-tools) page for detailed documentation and examples.
A list of browser extension paths to load into the session.
See the [Custom Extensions](/features/custom-extensions) page for more details.
Show the cursor in the browser during the session. Useful for debugging or recordings. Default: `False`.
Experimental features to enable for the session.
## Response
Returns a `SessionHandle` with the following methods.
### Session Methods
Run an agent task within the session. See [Run Task](/methods/session-run-task) for full documentation.
Navigate to a specific URL deterministically. See [Goto](/methods/session-goto) for full documentation.
Extract structured data from the current page. See [Extract](/methods/session-extract) for full documentation.
Execute JavaScript in the browser context. See [Evaluate JS](/methods/session-evaluate-js) for full documentation.
Close the session and save any profile changes. Automatically called when using the `with` statement.
### Status Methods
Returns the ID of the session.
Returns a live URL where you can see the browser in action.
Set `interactive=True` to get an interactive view.
Set `embed=True` to get an embeddable view (ideal for iframes).
Returns a recording URL if `enable_recording` was enabled. You can use this link to download the video recording of the session.
Returns a URL of the archive containing the files downloaded during the session. If no file was downloaded raises `ApiError`.
Returns a `SessionResponse` with session information.
## Session Response
The `result()` method returns an object with the following attributes.
The ID of the session.
The status of the session.
The number of credits used. 1 credit corresponds to \$0.01.
The timestamp when the session was created.
## Writing Multi-Step Workflow
Here’s an example of a multi-step browser automation workflow using a session:
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
with client.session(url="https://flights.example.com") as session:
# Step 1: Let the agent search for flights
session.run_task("Search for one-way flights from NYC to LA on March 15")
# Step 2: Extract the results
flights = session.extract({
"type": "array",
"items": {
"type": "object",
"properties": {
"airline": {"type": "string"},
"price": {"type": "number"}
}
}
})
# Step 3: Use the data to guide the next action
cheapest = min(flights, key=lambda f: f["price"])
session.run_task(f"Select the {cheapest['airline']} flight for ${cheapest['price']}")
# Step 4: Get session info
print(f"Credits used: {session.result().credits_used}")
```
## Manual Session Management
If you're not using the `with` statement, you must close the session manually.
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
session = client.session()
try:
session.run_task("Do something")
session.run_task("Do something else")
finally:
session.close() # Always close the session
```
## Core Session Methods
Run an agent task within the session. The agent will perform the task autonomously starting from the current page state.
Navigate to a specific URL deterministically. Use this when you know exactly where you need to go.
Extract structured data from the current page. Define a schema and get back typed data.
Execute JavaScript in the browser context. Useful for advanced interactions or reading page state.
# Evaluate JS
Source: https://docs.smooth.sh/methods/session-evaluate-js
Execute JavaScript in the browser context
Execute JavaScript code in the browser context. This is useful for advanced interactions, reading page state, or performing actions that require direct DOM manipulation.
## Usage
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
with client.session() as session:
session.goto("https://example.com")
# Get the page title
result = session.evaluate_js("document.title")
print(f"Page title: {result.output}")
```
## Request
The JavaScript code to execute in the browser. The code should be an expression or a function that returns a value.
Example: `document.title`
Optional dictionary of arguments to pass to the JavaScript function. When using args, your code must be a function that receives `args` as its parameter.
Example: `{"selector": ".product-price", "index": 0}`
## Response
Returns an object with the following attributes.
The result of the JavaScript evaluation. Complex objects are serialized to JSON.
The number of credits used for this action. 1 credit corresponds to \$0.01.
The duration in seconds taken to execute the JavaScript.
## Examples
**Read page information:**
```python Python theme={null}
with client.session() as session:
session.goto("https://example.com")
# Get current URL
result = session.evaluate_js("window.location.href")
print(f"Current URL: {result.output}")
# Get page title
result = session.evaluate_js("document.title")
print(f"Title: {result.output}")
# Check if an element exists
result = session.evaluate_js("!!document.querySelector('.login-button')")
print(f"Has login button: {result.output}")
```
**Extract data from the DOM:**
```python Python theme={null}
with client.session() as session:
session.goto("https://example.com/products")
result = session.evaluate_js("""
Array.from(document.querySelectorAll('.product-card')).map(card => ({
name: card.querySelector('.name').textContent,
price: parseFloat(card.querySelector('.price').textContent.replace('$', ''))
}))
""")
for product in result.output:
print(f"{product['name']}: ${product['price']}")
```
**With arguments:**
```python Python theme={null}
with client.session() as session:
session.goto("https://example.com")
result = session.evaluate_js(
"(args) => { return document.querySelectorAll(args.selector).length; }",
args={"selector": ".product-item"}
)
print(f"Found {result.output} products")
```
**Scroll the page:**
```python Python theme={null}
with client.session() as session:
session.goto("https://example.com/infinite-scroll")
# Scroll to bottom to load more content
session.evaluate_js("window.scrollTo(0, document.body.scrollHeight)")
# Wait a moment for content to load, then extract
session.run_task("Wait for new content to load")
data = session.extract({"type": "array", "items": {"type": "object", "properties": {"title": {"type": "string"}}}})
```
**Interact with page elements:**
```python Python theme={null}
with client.session() as session:
session.goto("https://example.com/form")
# Set a value directly
session.evaluate_js(
"(args) => { document.querySelector(args.selector).value = args.value; }",
args={"selector": "#email", "value": "test@example.com"}
)
# Click a button
session.evaluate_js("document.querySelector('#submit-btn').click()")
```
**Check page state before proceeding:**
```python Python theme={null}
with client.session() as session:
session.goto("https://example.com")
session.run_task("Log in with my credentials")
# Verify login succeeded
result = session.evaluate_js("""
{
isLoggedIn: !!document.querySelector('.user-avatar'),
username: document.querySelector('.username')?.textContent || null
}
""")
if result.output['isLoggedIn']:
print(f"Logged in as {result.output['username']}")
session.run_task("Go to my account settings")
else:
print("Login failed")
```
# Extract
Source: https://docs.smooth.sh/methods/session-extract
Extract structured data from the current page
Extract structured data from the current page by providing a schema. The extraction uses the current page state, making it ideal for capturing data mid-workflow.
## Usage
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
with client.session() as session:
session.goto("https://news.ycombinator.com")
result = session.extract(
schema={
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {"type": "string", "description": "The title of the story"},
"url": {"type": "string", "description": "Link to the story"},
"points": {"type": "integer", "description": "Number of upvotes"}
}
}
},
prompt="Extract the top 5 stories from the front page"
)
for story in result.output:
print(f"{story['title']} - {story['points']} points")
```
## Request
A JSON schema describing the structure of the data to extract. Include `description` fields to help guide the extraction.
Example:
```json theme={null}
{
"type": "object",
"properties": {
"title": {"type": "string", "description": "The product title"},
"price": {"type": "number", "description": "Price in USD"},
"in_stock": {"type": "boolean", "description": "Whether the item is available"}
}
}
```
Optional prompt to guide the extraction. Use this to filter results, specify quantity, or handle ambiguous cases.
Example: `Extract only the products that are currently in stock and under $50`
## Response
Returns an object with the following attributes.
The extracted data conforming to the provided schema.
The number of credits used for this action. 1 credit corresponds to \$0.01.
The duration in seconds taken to perform the extraction.
## Examples
**Extract a single object:**
```python Python theme={null}
with client.session() as session:
session.goto("https://example.com/product/12345")
result = session.extract(
schema={
"type": "object",
"properties": {
"name": {"type": "string", "description": "Product name"},
"price": {"type": "number", "description": "Price in USD"},
"rating": {"type": "number", "description": "Average rating out of 5"},
"reviews_count": {"type": "integer", "description": "Number of reviews"}
}
},
prompt="Extract the main product details from this page"
)
product = result.output
print(f"{product['name']}: ${product['price']} ({product['rating']} stars)")
```
**Extract a list of items:**
```python Python theme={null}
with client.session() as session:
session.goto("https://example.com/search?q=laptops")
result = session.extract(
schema={
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string", "description": "Product name"},
"price": {"type": "number", "description": "Price in USD"},
"specs": {"type": "string", "description": "Key specifications"}
}
}
},
prompt="Extract the first 10 laptops from the search results"
)
for laptop in result.output:
print(f"{laptop['name']}: ${laptop['price']}")
```
**Filtering with prompt:**
Use the prompt to filter, sort, or apply conditions that go beyond what the schema can express.
```python Python theme={null}
with client.session() as session:
session.run_task(
task="Search for one-way flights from NYC to LA on March 15",
url="https://www.google.com/travel/flights"
)
result = session.extract(
schema={
"type": "array",
"items": {
"type": "object",
"properties": {
"airline": {"type": "string", "description": "Airline name"},
"departure": {"type": "string", "description": "Departure time"},
"arrival": {"type": "string", "description": "Arrival time"},
"price": {"type": "number", "description": "Price in USD"}
}
}
},
prompt="Extract only non-stop flights under $300, sorted by price from lowest to highest"
)
for flight in result.output:
print(f"{flight['airline']}: ${flight['price']} ({flight['departure']} - {flight['arrival']})")
```
**Extract and use data to guide next action:**
```python Python theme={null}
with client.session() as session:
session.goto("https://shop.example.com/category/electronics")
result = session.extract(
schema={
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string", "description": "Product name"},
"price": {"type": "number", "description": "Price in USD"},
"in_stock": {"type": "boolean", "description": "Whether the item is available"}
}
}
},
prompt="Extract all products that are in stock"
)
# Find the cheapest in-stock item
products = result.output
if products:
cheapest = min(products, key=lambda p: p['price'])
session.run_task(f"Add '{cheapest['name']}' to cart")
```
# Goto
Source: https://docs.smooth.sh/methods/session-goto
Navigate to a URL deterministically
Navigate to a specific URL deterministically. Use this when you know exactly where you need to go, rather than asking the agent to navigate.
## Usage
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
with client.session() as session:
result = session.goto("https://example.com/products")
print(f"Navigation took {result.duration} seconds")
session.run_task("Click on the first product")
```
## Request
The URL to navigate to.
Example: `https://example.com/login`
## Response
Returns an object with the following attributes.
Always `0`. Navigation is free.
The duration in seconds taken to perform the navigation.
## Examples
**Navigate and extract data:**
```python Python theme={null}
with client.session() as session:
session.goto("https://news.ycombinator.com")
stories = session.extract(
schema={
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {"type": "string"},
"url": {"type": "string"},
"points": {"type": "integer"}
}
}
},
prompt="Extract the top 5 stories"
)
for story in stories.output:
print(f"{story['title']} ({story['points']} points)")
```
# Run Task
Source: https://docs.smooth.sh/methods/session-run-task
Run an agent task within a browser session
Run an agent task within the current browser session. The agent will perform the task autonomously, starting from the current page state.
## Usage
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
with client.session() as session:
result = session.run_task(
task="Fill out the contact form with test data",
url="https://example.com/contact"
)
print(f"Output: {result.output}")
print(f"Credits used: {result.credits_used}")
```
## Request
The task for the agent to execute.
Example: `Fill out the contact form and submit it`
The upper limit on the number of steps the agent can take during task execution. The range is from 2 to 128. Default: `32`.
Example: `64`
If provided, enforces a structured output schema. It should be a dictionary describing a JSON schema.
Default: `None`.
Example:
```json theme={null}
{
"type": "object",
"properties": {
"confirmation_number": {
"type": "string",
"description": "The confirmation number after form submission"
}
}
}
```
Navigate to this URL before executing the task. If not provided, the task starts from the current page.
Example: `https://example.com/contact`
A dictionary containing variables or parameters that will be passed to the agent.
Example: `{"username": "test_user", "email": "test@example.com"}`
## Response
Returns an object with the following attributes.
The task output returned by the agent. If `response_model` was provided, this will conform to the specified schema.
The number of credits used for this task. 1 credit corresponds to \$0.01.
The duration in seconds taken to perform the task.
## Examples
**Basic task:**
```python Python theme={null}
with client.session() as session:
result = session.run_task(
task="Find the top story and tell me its title",
url="https://news.ycombinator.com"
)
print(result.output)
```
**With structured output:**
```python Python theme={null}
with client.session() as session:
result = session.run_task(
task="Search for one-way flights from NYC to LA on March 15, then return the 3 cheapest options",
url="https://www.google.com/travel/flights",
response_model={
"type": "array",
"items": {
"type": "object",
"properties": {
"airline": {"type": "string"},
"price": {"type": "number"},
"departure_time": {"type": "string"},
"duration": {"type": "string"}
}
}
}
)
for flight in result.output:
print(f"{flight['airline']}: ${flight['price']} - {flight['departure_time']} ({flight['duration']})")
```
**With metadata:**
```python Python theme={null}
with client.session() as session:
result = session.run_task(
task="Fill out the shipping form with the provided address",
url="https://shop.example.com/checkout",
metadata={
"name": "John Smith",
"address": "123 Main Street",
"city": "New York",
"zip": "10001"
}
)
```
**Chaining multiple tasks:**
```python Python theme={null}
status_schema = {
"type": "object",
"properties": {
"success": {"type": "boolean", "description": "Whether the task was completed successfully"},
"message": {"type": "string", "description": "Details about what was done or why it failed"}
}
}
with client.session(url="https://shop.example.com") as session:
# Task 1: Search for a product
result = session.run_task(
task="Search for 'wireless headphones'",
response_model=status_schema
)
if not result.output["success"]:
raise Exception(f"Search failed: {result.output['message']}")
# Task 2: Apply filters
result = session.run_task(
task="Filter by price under $100 and rating 4+ stars",
response_model=status_schema
)
if not result.output["success"]:
raise Exception(f"Filter failed: {result.output['message']}")
# Extract the results
products = session.extract(
schema={
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"price": {"type": "number"}
}
}
},
prompt="Extract the first 3 products from the search results"
)
print(products.output)
```
**Pro Tip:** Use `response_model` to have the agent report whether each task succeeded before moving to the next one. This prevents your workflow from continuing blindly when something went wrong.
# Simple Task
Source: https://docs.smooth.sh/methods/simple-task
Run a one-shot browser automation task
For complex multi-step workflows or when you need more control, see [Session Workflows](/methods/session).
## Setting up
Get your free API key and start running tasks in minutes.
Unlock your free welcome credits. No credit card required.
## Launch a task using the Python SDK
Launch your first task in 4 lines of code. Under the hood, `client.run()` creates a browser session, runs the task, and returns the result.
```python Python theme={null}
# pip install smooth-py
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
task = smooth_client.run("Go to google flights and find the cheapest flight from London to Paris today")
print(f"Live URL: {task.live_url()}")
print(f"Agent response: {task.result()}")
```
If Python is not your language of choice, check out our [API Reference](/api-reference/introduction).
## Request
All parameters available when running a task.
The task for the agent to execute.
Example: `Go to Google Flights and find the cheapest flight from London to Paris today`
If provided, enforces a structured output schema.
It can be a dictionary describing a JSON schema or a Pydantic Model.
Default: `None`.
Example:
```json theme={null}
{
"type": "object",
"properties": {
"output": {
"type": "integer",
"description": "The integer part of the result of the calculation"
}
}
}
```
The agent that will run the task. Currently, only `smooth` is available. Default: `smooth`.
Example: `smooth`
The upper limit on the number of steps the agent can take during task execution. The range is from 2 to 128. Default: 32.
Example: `64`
The type of device for the task execution. Choose between `mobile` or `desktop`. Default: `mobile`.
Example: `desktop`
Toggles the option to record a video of the task execution. Default: True.
Example: `True`
### Advanced Parameters
The starting URL for the task. If not provided, the agent will infer it from the task.
Example: `https://amazon.com`
List of allowed URL patterns using wildcard syntax. If None, all URLs are allowed.
Example: \["google.com/\*", "\*mydomain.\*/\*"]
A dictionary containing variables or parameters that will be passed to the agent.
Example: `{"username": "my_username"}`
A list of file ids to be passed to the agent.
Check out our guide on [File uploads](/guides/file-uploads)
The browser profile ID to be utilized. Each profile retains its own state, including login credentials and cookies. You must create the profile first using `client.create_profile()`. See [Browser Profiles](/methods/profiles).
Example: `profile_12345`
If true, the profile specified by `profile_id` will be loaded in read-only mode. Changes made during the task will not be saved back to the profile. Default: False.
Enable adblock for the browser session. Default is True.
Activates stealth mode for the browser, which helps in avoiding detection. Default: True.
Example: `True`
The hostname or IP address of the proxy server that will be used for the session.
Set to `"self"` to create a P2P tunnel through your machine, routing traffic via your IP and enabling access to localhost. See [P2P Tunnel](/features/use-my-ip) for details.
Example: `proxy.example.com` or `self`
The username for authenticating with the proxy server, if authentication is required.
Example: `user123`
The password for authenticating with the proxy server, if authentication is required.
Example: `password123`
List of client certificates to use when accessing secure websites. Each certificate is a dictionary with the following fields:
* `file`: p12 file object to be uploaded (e.g., open('my\_cert.p12', 'rb'));
* `password` (optional): The password for the certificate file, if applicable.
```python Python theme={null}
task = client.run(
...,
certificates=[{
"file": open("my_cert.p12", "rb"),
"password": "my_password"
}]
)
```
Additional tools to enable for the task. Each tool is a `{tool_name: tool_kwargs}` pair. Use `tool_kwargs = None` for the default configuration of any tool.
See the [Tools](/methods/tools-overview) page for a complete list of available tools and their configuration options.
```python Python theme={null}
task = client.run(
...,
additional_tools={
"screenshot": {"full_page": True},
"hover": None
}
)
```
A list of custom Python functions that the agent can call during task execution. Custom tools run in your local environment and can be used for OTP handling, human-in-the-loop scenarios, database operations, API integrations, and more.
See the [Custom Tools](/methods/custom-tools) page for detailed documentation and examples.
Experimental features to enable for the task.
## Response
Returns a `TaskHandle` with the following attributes.
Returns the ID of the task.
Returns a live URL where you can see the agent in action.
Set `interactive=True` to get an interactive view.
Set `embed=True` to get an embeddable view (ideal for iframes).
Waits for the task completion and returns a `TaskResponse` upon completion.
Waits for the task completion and returns a recording URL upon completion if `enable_recording` was enabled. You can use this link to download the video recording of the task. The video will be ready shortly after the task completes.
Waits for the task completion and returns a URL of the archive containing the files downloaded during the execution. If no file was downloaded raises `ApiError`.
Cancel the task execution.
## Waiting for task completion
Use `result()` to wait for task completion.
```python Python highlight={6} theme={null}
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
task = smooth_client.run("Go to google flights and find the cheapest flight from London to Paris today")
task_result = task.result() # Waits for the agent response
if task_result.status == "done":
print(f"Agent response: {task_result.output}")
print(f"Total cost: ${task_result.credits_used * 0.01}")
else:
print(f"There was an error: {task_result.error}")
```
Returns a `TaskResponse` with the following attributes.
The ID of the task.
The status of the task. One of: \["waiting", "running", "done", "failed"]
The final response from the agent.
The number of credits used. 1 credit corresponds to \$0.01.
The device type used for the task. One of: \["mobile", "desktop"]
The timestamp when the task was created.
## Cancelling a running task
Use `stop()` to cancel a running task.
```python Python highlight={6} theme={null}
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
task = smooth_client.run("Go to google flights and find the cheapest flight from London to Paris today")
task.stop() # Cancel the task execution
task_result = task.result() # Waits for the agent response
assert task_result.status == "cancelled"
```
# Drag and Drop
Source: https://docs.smooth.sh/methods/tools-drag-drop
Drag and drop elements on the page
The Drag and Drop tool allows the agent to drag elements from one location to another on the page. This is useful for interacting with drag-and-drop interfaces like file uploaders, sortable lists, kanban boards, or any UI that requires drag interactions.
## Usage
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
task = client.run(
task="Drag the item from the source container to the target container",
additional_tools={
"drag_drop": None
}
)
```
## Parameters
This tool has no configurable parameters. Pass `None` to enable it with default behavior.
## Examples
**Reordering items in a list:**
```python Python theme={null}
task = client.run(
task="Drag the 'High Priority' task to the top of the list",
additional_tools={
"drag_drop": None
}
)
```
**Moving cards on a kanban board:**
```python Python theme={null}
task = client.run(
task="Drag the task card from 'To Do' column to 'In Progress'",
additional_tools={
"drag_drop": None
}
)
```
# Hover
Source: https://docs.smooth.sh/methods/tools-hover
Hover over elements on the page
The Hover tool allows the agent to hover over elements on the page. This is useful for revealing hidden content like dropdown menus, tooltips, or other hover-triggered UI elements.
## Usage
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
task = client.run(
task="Hover over the navigation menu to reveal the dropdown options",
additional_tools={
"hover": None
}
)
```
## Parameters
This tool has no configurable parameters. Pass `None` to enable it with default behavior.
## Examples
**Revealing dropdown menus:**
```python Python theme={null}
task = client.run(
task="Hover over the 'Products' menu and click on 'Enterprise'",
additional_tools={
"hover": None
}
)
```
**Viewing tooltips:**
```python Python theme={null}
task = client.run(
task="Hover over the info icon next to the price to see the tooltip",
additional_tools={
"hover": None
}
)
```
# Overview
Source: https://docs.smooth.sh/methods/tools-overview
Built-in tools to extend your agent
Additional tools are built-in capabilities that can be enabled for any task. Enable them via the `additional_tools` parameter when running a task.
## Usage
```python Python theme={null}
task = client.run(
task="Take a screenshot of the page",
additional_tools={
"screenshot": {"full_page": True},
"hover": None
}
)
```
Pass a configuration dictionary for tools with parameters, or `None` to use default settings.
## Available Tools
Capture screenshots of the browser viewport or full page.
Hover over elements to reveal hidden content like dropdown menus.
Save the current page as a PDF.
Drag and drop elements on the page.
# Print Page
Source: https://docs.smooth.sh/methods/tools-print-page
Save the current page as a PDF
The Print Page tool allows the agent to save the current page as a PDF. The generated PDF can be accessed via `.downloads_url()` after the task completes.
## Usage
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
task = client.run(
task="Go to the invoice page and save it as a PDF",
url="https://example.com/invoice",
additional_tools={
"print_page": None
}
)
result = task.result()
print(f"Download PDF: {task.downloads_url()}")
```
## Parameters
This tool has no configurable parameters. Pass `None` to enable it with default behavior.
## Examples
**Save an invoice as PDF:**
```python Python theme={null}
task = client.run(
task="Navigate to my billing page and print the latest invoice",
additional_tools={
"print_page": None
}
)
```
**Save a receipt:**
```python Python theme={null}
task = client.run(
task="Go to the order confirmation page and save it as a PDF for my records",
url="https://shop.example.com/order/12345",
additional_tools={
"print_page": None
}
)
```
# Screenshot
Source: https://docs.smooth.sh/methods/tools-screenshot
Capture screenshots of the browser
The Screenshot tool allows the agent to take screenshots of the browser viewport. The captured screenshots can be accessed via `.downloads_url()` after the task completes.
## Usage
```python Python theme={null}
from smooth import SmoothClient
client = SmoothClient()
task = client.run(
task="Go to example.com and take a screenshot",
additional_tools={
"screenshot": {"full_page": True}
}
)
result = task.result()
print(f"Download screenshots: {task.downloads_url()}")
```
## Parameters
Whether to capture the entire scrollable page rather than just the visible viewport. Default: `False`.
## Examples
**Viewport screenshot (default):**
```python Python theme={null}
task = client.run(
task="Take a screenshot of the visible area",
additional_tools={
"screenshot": None # Uses default settings
}
)
```
**Full page screenshot:**
```python Python theme={null}
task = client.run(
task="Go to the url and take a full page screenshot",
url="https://smooth.sh",
additional_tools={
"screenshot": {"full_page": True}
}
)
```
# Performance
Source: https://docs.smooth.sh/performance
Learn how Smooth compares to its alternatives
## Free API credits
Unlock your free credits and get a free API key.
Unlock your free welcome credits. No credit card required.
## WebVoyager
Smooth is the most accurate browser agent on WebVoyager while also being 5x faster and 7x cheaper than Browser Use.
## Alternatives
We found that existing solutions are:
* **Too expensive** Usually require large models - can easily end up costing a few dollars even for simple tasks.
* **Too slow** Can take way too long to complete - e.g. 4 min to book a flight.
* **Unreliable** Even when provided with detailed instructions, they can struggle to complete the task.
The ultimate test is trying first-hand. We encourage you to try Smooth for free in our [Playground](https://app.smooth.sh).
## Community
Join our community for support and showcases
# Plans & Pricing
Source: https://docs.smooth.sh/pricing
Learn how to get and manage your Smooth API credits
## Free API credits
Unlock your free credits and get a free API key.
Unlock your free welcome credits. No credit card required.
## Plans Overview
Smooth operates on a simple, credit-based model.
| | Free (\$0) | Starter (\$50/month) | Growth (\$500/month) | Enterprise (Custom) |
| ------------------------------- | :--------: | :-------------------: | :-------------------: | :-------------------: |
| **Agent step** | \$0.005 | \$0.005 | \$0.005 | Custom |
| **Credits included** | \$5 | \$50 | \$500 | Unlimited |
| **Concurrent browsers** | 2 | 5 | 50 | Unlimited |
| **Custom proxy** | | | | |
| **Browser profiles** | | | | |
| **Implementation team** | | | | |
| **24/7 support** | | | | |
| **Single tenant** | | | | |
| **BYOC** | | | | |
| **BAA agreements** | | | | |
| **SSO and SAML authentication** | | | | |
Go to the [dashboard](https://app.smooth.sh) to manage your plan.
# Quickstart
Source: https://docs.smooth.sh/quickstart
Running your first task
## Setting up
Get your free API key and start running tasks in minutes.
Unlock your free welcome credits. No credit card required.
## Install Smooth
Smooth has both a Python and a Typescript SDK.
The Typescript SDK is automatically generate from the Python SDK and provides the same interface.
```bash Python theme={null}
pip install smooth-py
```
```bash Typescript theme={null}
npm installl @circlemind-ai/smooth-ts
```
To directly interface with the APIs, refer to the [API documentation](https://docs.smooth.sh/api-reference/introduction)
## Run a task
Submit a task in seconds.
```python Python theme={null}
from smooth import SmoothClient
smooth_client = SmoothClient(api_key="cmzr-YOUR_API_KEY")
task = smooth_client.run("Go to google flights and find the cheapest flight from London to Paris today")
print(f"Live URL: {task.live_url()}")
print(f"Agent response: {task.result()}")
```
```typescript Typescript theme={null}
import { SmoothClient } from "@circlemind-ai/smooth-ts";
const client = new SmoothClient({
api_key: "cmzr-YOUR_API_KEY",
});
const task = await client.run({
task: "Go to google flights and find the cheapest flight from London to Paris today",
})
console.log("Live URL:", await task.live_url())
console.log("Agent response:", await task.result())
```
```javascript Javascript theme={null}
fetch('https://api.smooth.sh/api/v1/task/smooth', {
method: 'POST',
headers: {
'apikey': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({ 'max_steps': 32, 'task': 'your_task_description' })
})
```
```curl cURL theme={null}
curl -X POST https://api.smooth.sh/api/v1/task/smooth \
-H "apikey: cmzr-YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"max_steps": 32, "task": "your_task_description"}'
```
## Next steps
That's all it takes to start automating with Smooth!
If you want to learn how to implement more complex workflows in Python, check out our [examples](/features/).
Or, dive deep into our API and read about the different parameters on our [API Reference](/api-reference/introduction).
To play around with the API, check out our [playground](https://app.smooth.sh).