# Welcome to Scrapybara
> Scrapybara hosts remote desktop instances for computer use agents
Setup Scrapybara and deploy your first computer use agent in minutes
## What is Scrapybara?
Scrapybara provides powerful virtual desktop infrastructure for computer use agents with:
* **Lightning speed**: Instantly spin up multiple [Ubuntu](/ubuntu) and [Browser](/browser) instances under 1 second.
* **Flexible deployments**: Easily deploy and manage remote instances with our [API](/api-reference), [SDKs](/sdk-reference), and [dashboard](https://scrapybara.com/dashboard). Configure instance types and timeouts to match your needs.
* **Unified integration**: Seamlessly connect your instances to models like [OpenAI CUA](/openai) and [Claude Computer Use](/anthropic) and execute powerful agentic workflows with [structured outputs](/act-sdk), [multi-turn conversations](/conversations), and [custom tools](/tools).
* **Granular control**: Get complete remote desktop access with all the tools your agents need, including [Browser](/protocols/browser), [Code Execution](/protocols/code), [Environment Variables](/protocols/env), and [Filesystem](/protocols/file).
* **Authenticated sessions**: Save and reuse browser [auth states](/auth-states) across instances.
## Explore our guides
Take action with computer use models and custom tools with one line of code
Save and reuse browser authentication states across instances
## Get inspired
} href="/cookbook/copycapy">
Scrape and transform websites into capybara-themed versions with Scrapybara Act SDK and Playwright.
`TypeScript` `Act SDK` `Playwright`
} href="/cookbook/wide-research">
Deep Research but wide. Scrape YC W25 companies and find the best way to contact each company in parallel batches.
`Python` `Act SDK` `Structured Outputs`
## Dashboard
Get your API key and manage your instances on the [dashboard](https://scrapybara.com/dashboard).
## Get support
Need help or want to stay up to date? Reach out to us on Discord or X.
Join our Discord community
Follow us on X
# Quickstart
> Deploy your first instance
## Deploy your first instance
Sign up on our [dashboard](https://scrapybara.com/dashboard). An API key will be generated for you automatically.
Install the Python SDK with pip.
```bash
pip install scrapybara
```
Configure the client with your API key.
```python
from scrapybara import Scrapybara
client = Scrapybara(api_key="your_api_key")
```
Start an instance with your desired configuration. You can choose from Ubuntu, Browser, and Windows instances. We recommend using Ubuntu for most tasks.
```python
instance = client.start_ubuntu(
timeout_hours=1,
)
# browser_instance = client.start_browser(
# timeout_hours=1,
# )
```
Get the stream URL to view and interact with the instance manually.
```python
stream_url = instance.get_stream_url().stream_url
```
Interact with the instance with `computer` and `bash`.
```python Move the mouse
instance.computer(
action="move_mouse",
coordinates=[200, 100]
)
```
```python Left click
instance.computer(
action="click_mouse",
button="left"
)
```
```python Type hello
instance.computer(
action="type_text",
text="Hello, world!"
)
```
```python Run a bash command
result = instance.bash(
command="ls -la"
)
```
Connect to the browser with Playwright to enable programmatic browser control and authenticated browser sessions. Learn more [here](/browser).
```python Connect to Playwright
from playwright.sync_api import sync_playwright
cdp_url = instance.browser.start().cdp_url
playwright = sync_playwright().start()
browser = playwright.chromium.connect_over_cdp(cdp_url)
```
```python Save the auth state
auth_state_id = instance.browser.save_auth(name="default").auth_state_id
```
```python Reuse the auth state on other instances
instance.browser.authenticate(auth_state_id=auth_state_id)
```
Build your first agent with the Act SDK to control your Scrapybara instance with `BashTool`, `ComputerTool`, `EditTool`. Learn more [here](/act-sdk).
```python
from scrapybara.tools import BashTool, ComputerTool, EditTool
from scrapybara.openai import OpenAI, UBUNTU_SYSTEM_PROMPT
response = client.act(
model=OpenAI(),
tools=[
BashTool(instance),
ComputerTool(instance),
EditTool(instance),
],
system=UBUNTU_SYSTEM_PROMPT,
prompt="Go to the top link on Hacker News",
on_step=lambda step: print(step.text),
)
```
Stop the instance when you're done. This will delete all data stored during the session.
```python
instance.stop()
```
Sign up on our [dashboard](https://scrapybara.com/dashboard). An API key will be generated for you automatically.
Install the TypeScript SDK with npm, yarn, or pnpm.
```bash
npm install scrapybara
yarn add scrapybara
pnpm add scrapybara
```
Configure the client with your API key.
```typescript
import { ScrapybaraClient } from "scrapybara";
const client = new ScrapybaraClient({ apiKey: "your_api_key" });
```
Start an instance with your desired configuration. You can choose from Ubuntu, Browser, and Windows instances. We recommend using Ubuntu for most tasks.
```typescript
const instance = await client.startUbuntu({
timeoutHours: 1,
});
// const browserInstance = await client.startBrowser({
// timeoutHours: 1,
// });
```
Get the stream URL to view and interact with the instance manually.
```typescript
const streamUrl = await instance.getStreamUrl().streamUrl;
```
Interact with the instance with `computer` and `bash`.
```typescript Move the mouse
await instance.computer({
action: "move_mouse",
coordinates: [200, 100]
});
```
```typescript Left click
await instance.computer({
action: "click_mouse",
button: "left"
});
```
```typescript Type hello
await instance.computer({
action: "type_text",
text: "Hello, world!"
});
```
```typescript Run a bash command
const result = await instance.bash({
command: "ls -la"
});
```
Connect to the browser with Playwright to enable programmatic browser control and authenticated browser sessions. Learn more [here](/browser).
```typescript Connect to Playwright
import { chromium } from "playwright";
const cdpUrl = await instance.browser.start().cdpUrl;
const browser = await chromium.connectOverCDP(cdpUrl);
```
```typescript Save the auth state
const authStateId = await instance.browser.saveAuth({ name: "default" }).authStateId;
```
```typescript Reuse the auth state on other instances
await instance.browser.authenticate({ authStateId });
```
Build your first agent with the Act SDK to control your Scrapybara instance with `BashTool`, `ComputerTool`, `EditTool`. Learn more [here](/act-sdk).
```typescript
import { bashTool, computerTool, editTool } from "scrapybara/tools";
import { openai, UBUNTU_SYSTEM_PROMPT } from "scrapybara/openai";
const { messages, steps, text, usage } = await client.act({
tools: [
bashTool(instance),
computerTool(instance),
editTool(instance),
],
model: openai(),
system: UBUNTU_SYSTEM_PROMPT,
prompt: "Go to the top link on Hacker News",
onStep: (step) => console.log(step.text),
});
```
Stop the instance when you're done. This will delete all data stored during the session.
```typescript
await instance.stop();
```
## Start building
Be sure to check out our other resources to learn more. Happy building! ₍ᐢ•(ܫ)•ᐢ₎
Take action with computer use agents
Check out our API reference docs
Learn about the UbuntuInstance
Learn about the BrowserInstance
# Best Practices
> Best practices for using Scrapybara
## Manage instance usage
Instances are billed per usage. When launching an instance, you can specify its timeout before it is automatically terminated (default is 1 hour).
```python
instance = client.start_ubuntu(timeout=3) # 3 hours
```
```typescript
const instance = await client.startUbuntu({ timeout: 3 }); // 3 hours
```
To save costs, pause the instance to resume it later, or stop the instance once you no longer need to control the desktop environment and access its stored data.
```python Pause/resume
instance.pause()
instance.resume()
```
```python Stop
instance.stop()
```
```typescript Pause/resume
await instance.pause();
await instance.resume();
```
```typescript Stop
await instance.stop();
```
## Take actions programmatically
When possible, take actions programmatically rather than relying on the agent to do so. For example, using `instance.bash` provides a faster way to launch apps compared to having the model use mouse/keyboard interactions. If you know the agent's workflow will happen on a specific application, you can launch it before prompting the agent to take actions. The same applies for browser automation: it is often easier to manipulate the browser programmatically with `instance.browser` and Playwright than relying on the agent itself.
## Initialize the browser
For agents requiring programmatic browser interaction, initialize and configure the browser immediately after instance creation. This ensures the browser environment is ready before any browser tool calls are made.
```python
instance = client.start_ubuntu()
instance.browser.start()
instance.browser.authenticate(auth_state_id="auth_state_id")
```
```typescript
const instance = await client.startUbuntu();
await instance.browser.start();
await instance.browser.authenticate(auth_state_id="auth_state_id");
```
## Optimize your prompt
Each model (OpenAI and Anthropic) has its own specialized system prompt optimized for computer use tasks. We recommend using these model-specific prompts for best results.
```python OpenAI
from scrapybara.openai import OpenAI, UBUNTU_SYSTEM_PROMPT as OPENAI_UBUNTU_PROMPT
client.act(
model=OpenAI(),
system=OPENAI_UBUNTU_PROMPT,
# ...
)
```
```python Anthropic
from scrapybara.anthropic import Anthropic, UBUNTU_SYSTEM_PROMPT as ANTHROPIC_UBUNTU_PROMPT
client.act(
model=Anthropic(),
system=ANTHROPIC_UBUNTU_PROMPT,
# ...
)
```
```typescript OpenAI
import { openai, UBUNTU_SYSTEM_PROMPT as OPENAI_UBUNTU_PROMPT } from "scrapybara/openai";
await client.act({
model: openai(),
system: OPENAI_UBUNTU_PROMPT,
// ...
});
```
```typescript Anthropic
import { anthropic, UBUNTU_SYSTEM_PROMPT as ANTHROPIC_UBUNTU_PROMPT } from "scrapybara/anthropic";
await client.act({
model: anthropic(),
system: ANTHROPIC_UBUNTU_PROMPT,
// ...
});
```
For more complex tasks, you can extend the model-specific system prompt by defining additional task requirements:
```python
# Use the appropriate system prompt for your chosen model
system = f"""{UBUNTU_SYSTEM_PROMPT}
{task}
"""
```
```typescript
// Use the appropriate system prompt for your chosen model
const system = `${UBUNTU_SYSTEM_PROMPT}
${task}
`;
```
Here are some tips from [Anthropic](https://docs.anthropic.com/en/docs/build-with-claude/computer-use) to improve the agent's performance:
1. Specify simple, well-defined tasks and provide explicit instructions for each step.
2. Models sometimes assume outcomes of their actions without explicitly checking their results. To prevent this you can prompt the model with `After each step, take a screenshot and carefully evaluate if you have achieved the right outcome. Explicitly show your thinking: "I have evaluated step X..." If not correct, try again. Only when you confirm a step was executed correctly should you move on to the next one.`
3. Some UI elements (like dropdowns and scrollbars) might be tricky for models to manipulate using mouse movements. If you experience this, try prompting the model to use keyboard shortcuts.
4. For repeatable tasks or UI interactions, include example screenshots and tool calls of successful outcomes in your prompt.
5. If you need the model to log in, provide it with the username and password in your prompt inside xml tags like ``. Using computer use within applications that require login increases the risk of bad outcomes as a result of prompt injection.
# Act SDK
> Build computer use agents with one unified SDK — any model, any tool
## What is the Act SDK?
The Act SDK is a unified SDK for building computer use agents with Python and TypeScript. It provides a simple interface for executing looping agentic actions with support for many models and tools. Build production-ready computer use agents with pre-built tools to connect to Scrapybara instances.
## How it works
`act` initiates an interaction loop that continues until the agent achieves its objective. Each iteration of the loop is called a `step`, which consists of the agent's text response, the agent's tool calls, and the results of those tool calls. The loop terminates when the agent returns a message without invoking any tools, and returns `messages`, `steps`, `text`, `output` (if `schema` is provided), and `usage` after the agent's execution.
```python
response = client.act(
model=OpenAI(),
tools=[
BashTool(instance),
ComputerTool(instance),
EditTool(instance),
],
system=UBUNTU_SYSTEM_PROMPT,
prompt="Go to the top link on Hacker News",
on_step=lambda step: print(step.text),
)
messages = response.messages
steps = response.steps
text = response.text
usage = response.usage
```
```typescript
const { messages, steps, text, usage } = await client.act({
model: openai(),
tools: [
bashTool(instance),
computerTool(instance),
editTool(instance),
],
system: UBUNTU_SYSTEM_PROMPT,
prompt: "Go to the top link on Hacker News",
onStep: (step) => console.log(step.text),
});
```
An `act` call consists of 3 core components:
### Model
The model specifies the base LLM for the agent. At each step, the model examines the previous messages, the current state of the computer, and uses tools to take action. Each step will cost an amount of agent credits depending on the model. You can also bring your own API key to bill model charges directly.
```python
from scrapybara.openai import OpenAI
model = OpenAI()
# Use your own API key
model = OpenAI(api_key="your_api_key")
```
```typescript
import { openai } from "scrapybara/openai";
const model = openai();
// Use your own API key
const model = openai({ apiKey: "your_api_key" });
```
### Tools
Tools are functions that enable agents to interact with the computer. Each tool is defined by a `name`, `description`, and how it can be executed with `parameters` and an execution function. A tool can take in a Scrapybara instance to interact with it directly. Learn more about pre-built tools and how to define custom tools [here](/tools).
```python
from scrapybara import Scrapybara
from scrapybara.tools import BashTool, ComputerTool, EditTool
client = Scrapybara()
instance = client.start_ubuntu()
tools = [
BashTool(instance),
ComputerTool(instance),
EditTool(instance),
]
```
```typescript
import { ScrapybaraClient } from "scrapybara";
import { bashTool, computerTool, editTool } from "scrapybara/tools";
const client = new ScrapybaraClient();
const instance = await client.startUbuntu();
const tools = [
bashTool(instance),
computerTool(instance),
editTool(instance),
];
```
### Prompt
The prompt is split into two parts, the `system` prompt and a user `prompt`. `system` defines the general behavior of the agent, such as its capabilities and constraints. You can use our provided `UBUNTU_SYSTEM_PROMPT`, `BROWSER_SYSTEM_PROMPT`, and `WINDOWS_SYSTEM_PROMPT` to get started, or define your own. `prompt` should denote the agent's current objective. Alternatively, you can provide `messages` instead of `prompt` to start the agent with a history of messages. `act` conveniently returns `messages` after the agent's execution, so you can reuse it in another `act` call.
```python
from scrapybara.prompts import UBUNTU_SYSTEM_PROMPT
system = UBUNTU_SYSTEM_PROMPT
prompt = "Go to the top link on Hacker News"
```
```typescript
import { UBUNTU_SYSTEM_PROMPT } from "scrapybara/prompts";
const system = UBUNTU_SYSTEM_PROMPT;
const prompt = "Go to the top link on Hacker News";
```
## Structured output
Use the `schema` parameter to define a desired structured output. The response's `output` field will contain the typed data returned by the model. This is particularly useful when scraping or collecting structured data from websites.
Under the hood, we pass in a `StructuredOutputTool` to enforce and parse the schema.
```python
from pydantic import BaseModel
from typing import List
class HNSchema(BaseModel):
class Post(BaseModel):
title: str
url: str
points: int
posts: List[Post]
response = client.act(
model=OpenAI(),
tools=[
ComputerTool(instance),
],
schema=HNSchema,
system=UBUNTU_SYSTEM_PROMPT,
prompt="Get the top 10 posts on Hacker News",
)
posts = response.output.posts
```
```typescript
import { z } from "zod";
const { output } = await client.act({
model: openai(),
tools: [
computerTool(instance),
],
schema: z.object({
posts: z.array(
z.object({
title: z.string(),
url: z.string(),
points: z.number(),
})
),
}),
system: UBUNTU_SYSTEM_PROMPT,
prompt: "Get the top 10 posts on Hacker News",
});
const posts = output?.posts;
```
## Agent credits
Consume agent credits or bring your own API key. Without an API key, each step consumes 1 [agent credit](https://scrapybara.com/#pricing). With your own API key, model charges are billed directly to your provider API key.
## Full example
Here is how you can build a computer use agent that can output structured data.
```python
from scrapybara import Scrapybara
from scrapybara.openai import OpenAI
from scrapybara.prompts import UBUNTU_SYSTEM_PROMPT
from scrapybara.tools import ComputerTool
from pydantic import BaseModel
from typing import List
client = Scrapybara()
instance = client.start_ubuntu()
class HNSchema(BaseModel):
class Post(BaseModel):
title: str
url: str
points: int
posts: List[Post]
response = client.act(
model=OpenAI(),
tools=[
ComputerTool(instance),
],
system=UBUNTU_SYSTEM_PROMPT,
prompt="Get the top 10 posts on Hacker News",
schema=HNSchema,
on_step=lambda step: print(step.text),
)
posts = response.output.posts
print(posts)
instance.stop()
```
```typescript
import { ScrapybaraClient } from "scrapybara";
import { openai } from "scrapybara/openai";
import { UBUNTU_SYSTEM_PROMPT } from "scrapybara/prompts";
import { computerTool } from "scrapybara/tools";
import { z } from "zod";
const client = new ScrapybaraClient();
const instance = await client.startUbuntu();
const { output } = await client.act({
model: openai(),
tools: [
computerTool(instance),
],
system: UBUNTU_SYSTEM_PROMPT,
prompt: "Get the top 10 posts on Hacker News",
schema: z.object({
posts: z.array(
z.object({
title: z.string(),
url: z.string(),
points: z.number(),
})
),
}),
onStep: (step) => console.log(step.text),
});
const posts = output?.posts;
console.log(posts);
await instance.browser.stop();
await instance.stop();
```
# Auth States
> Save and load browser auth states
## What are auth states?
Auth states in Scrapybara allow you to capture, save, and reuse website authentication data across browser sessions. This feature is extremely useful for:
* Automating login flows
* Persisting authentication between browser instances
* Avoiding repetitive login procedures in your automation scripts
* Testing authenticated features without manual intervention
Auth states capture cookies, local storage, session storage, and other browser-based authentication data.
## Save auth state
When you've successfully authenticated with a website in the browser, you can save the authentication state for future use. Each auth state is identified by a unique ID and an optional name (defaults to "default" if not specified).
```python
# First, create a browser instance
instance = client.start_browser()
# Open the stream, navigate to the login page, and log in
webbrowser.open(instance.get_stream_url().stream_url)
```
Once you've logged in, you can save the auth state with a name:
```python
# Now save the auth state with a name
auth_state_id = instance.save_auth(name="example_site").auth_state_id
print(f"Saved auth state ID: {auth_state_id}")
```
```typescript
// First, create a browser instance
const instance = await client.startBrowser();
// Open the stream, navigate to the login page, and log in
window.open(instance.getStreamUrl().streamUrl, "_blank");
```
Once you've logged in, you can save the auth state with a name:
```typescript
// Now save the auth state with a name
const authStateId = await instance.saveAuth({name: "example_site"}).authStateId;
console.log(`Saved auth state ID: ${authStateId}`);
```
## Modify auth state
If you have an existing auth state that you want to update with new authentication data, you can use the `modify_auth` functionality. This is useful when credentials have changed or when an existing auth state needs to be refreshed.
```python
# First, create a browser instance
instance = client.start_browser()
# Open the stream, navigate to the login page, and log in with new credentials
webbrowser.open(instance.get_stream_url().stream_url)
```
Once you've logged in, you can modify the auth state with a new name:
```python
# Update an existing auth state with the new credentials
# You can optionally provide a new name
instance.modify_auth(auth_state_id="your_existing_auth_state_id", name="renamed_auth_state")
```
```typescript
// First, create a browser instance
const instance = await client.startBrowser();
// Open the stream, navigate to the login page, and log in with new credentials
window.open(instance.getStreamUrl().streamUrl, "_blank");
```
Once you've logged in, you can modify the auth state with a new name:
```typescript
// Update an existing auth state with the new credentials
// You can optionally provide a new name
await instance.modifyAuth({
authStateId: "your_existing_auth_state_id",
name: "renamed_auth_state"
});
```
## Load auth state
Once you've saved an auth state, you can use it to authenticate future browser sessions without going through the login process again:
```python
# Create a new browser instance
instance = client.start_browser()
# Authenticate using a previously saved auth state
instance.authenticate(auth_state_id="your_auth_state_id")
```
```typescript
// Create a new browser instance
const instance = await client.startBrowser();
// Authenticate using a previously saved auth state
await instance.authenticate({authStateId: "your_auth_state_id"});
```
## Using auth states in the playground
The Scrapybara Playground provides a visual interface for managing auth states:
1. **Create an auth state**:
* Head to the Scrapybara [auth](https://scrapybara.com/auth) page
* Click on the "Create auth state" button
* Log in to the website and save the auth state
2. **Use an existing auth state**:
* Click the fingerprint icon next to the instance dropdown menu
* Choose your saved auth state from the dropdown
* When you start the instance, it will automatically apply the authentication data
## Best practices
Here are some best practices for working with auth states:
1. **Use descriptive names**: Name your auth states clearly (e.g., "github\_login" or "shopify\_admin") for easy identification.
2. **Refresh regularly**: Authentication tokens can expire. Consider updating your auth states periodically using the `modify_auth` functionality.
3. **Test before use**: Always verify that your auth state is still valid before relying on it for critical automation.
4. **Multiple auth states**: Maintain separate auth states for different environments (e.g., production, staging, development).
# Conversations
> Build stateful agents with persistent conversations
## Understanding multi-turn conversations
Multi-turn conversations in Scrapybara enable your agents to maintain context and state across multiple interactions.
The Act SDK provides a structured way to manage these conversations through its message architecture.
## Message architecture
The Act SDK uses a structured message system with three primary message types and five different part types. Understanding these components is crucial for building sophisticated multi-turn agents.
### Message types
```python
# Message types
class UserMessage:
role: str = "user" # Always "user"
content: List[Union[TextPart, ImagePart]] # What the user sends
class AssistantMessage:
role: str = "assistant" # Always "assistant"
content: List[Union[TextPart, ToolCallPart, ReasoningPart]] # The agent's response
response_id: Optional[str] = None # Unique identifier for the response
class ToolMessage:
role: str = "tool" # Always "tool"
content: List[ToolResultPart] # Results from tool operations
Message = Union[UserMessage, AssistantMessage, ToolMessage]
```
### Message part types
Each message type contains various "parts" that serve different purposes:
```python
# Message part types
class TextPart:
type: str = "text" # Always "text"
text: str # Plain text content
class ImagePart:
type: str = "image" # Always "image"
image: str # Base64 encoded image or URL
mime_type: Optional[str] = None # e.g., "image/png", "image/jpeg"
class ToolCallPart:
type: str = "tool-call" # Always "tool-call"
id: Optional[str] = None # Unique identifier for the tool call
tool_call_id: str # ID matching the tool result
tool_name: str # Name of the tool being called
args: dict[str, Any] # Arguments passed to the tool
class ToolResultPart:
type: str = "tool-result" # Always "tool-result"
tool_call_id: str # ID matching the original tool call
tool_name: str # Name of the tool that was called
result: Any # Result returned by the tool
is_error: Optional[bool] = False # Whether the tool execution resulted in an error
class ReasoningPart:
type: str = "reasoning" # Always "reasoning"
id: Optional[str] = None # Unique identifier for the reasoning part
reasoning: str # The agent's internal reasoning
signature: Optional[str] = None # Cryptographic signature for verification
instructions: Optional[str] = None # Additional context about the reasoning
```
## Building multi-turn conversations
Instead of providing a single `prompt`, you can pass a complete message history using the `messages` parameter. This allows you to maintain the full conversation context.
The Act SDK returns a `messages` field in the response that contains the complete conversation history. You can reuse this directly in your next `act` call.
```python
from scrapybara import Scrapybara
from scrapybara.anthropic import Anthropic
from scrapybara.prompts import UBUNTU_SYSTEM_PROMPT
from scrapybara.tools import BashTool, ComputerTool, EditTool
client = Scrapybara()
instance = client.start_ubuntu()
# Initial conversation
response = client.act(
model=Anthropic(),
tools=[
BashTool(instance),
ComputerTool(instance),
EditTool(instance),
],
on_step=lambda step: print(step.text),
system=UBUNTU_SYSTEM_PROMPT,
prompt="Create a file called hello.py that prints 'Hello, World!'",
)
print('--------------------------------')
# Continue the conversation with the previous messages
follow_up_response = client.act(
model=Anthropic(),
tools=[
BashTool(instance),
ComputerTool(instance),
EditTool(instance),
],
on_step=lambda step: print(step.text),
system=UBUNTU_SYSTEM_PROMPT,
messages=response.messages + [
{
"role": "user",
"content": [
{
"type": "text",
"text": "Now modify the file to accept a name as a command line argument and print 'Hello, {name}!'"
}
]
}
]
)
instance.stop()
```
```typescript
import { ScrapybaraClient } from "scrapybara";
import { anthropic } from "scrapybara/anthropic";
import { UBUNTU_SYSTEM_PROMPT } from "scrapybara/prompts";
import { bashTool, computerTool, editTool } from "scrapybara/tools";
const client = new ScrapybaraClient();
const instance = await client.startUbuntu();
// Initial conversation
const response = await client.act({
model: anthropic(),
tools: [
bashTool(instance),
computerTool(instance),
editTool(instance),
],
onStep: (step) => console.log(step.text),
system: UBUNTU_SYSTEM_PROMPT,
prompt: "Create a file called hello.py that prints 'Hello, World!'",
});
console.log('--------------------------------')
// Continue the conversation with the previous messages
const followUpResponse = await client.act({
model: anthropic(),
tools: [
bashTool(instance),
computerTool(instance),
editTool(instance),
],
onStep: (step) => console.log(step.text),
system: UBUNTU_SYSTEM_PROMPT,
messages: [
...response.messages,
{
role: "user",
content: [
{
type: "text",
text: "Now modify the file to accept a name as a command line argument and print 'Hello, {name}!'"
}
]
}
]
});
await instance.stop();
```
## Including screenshots in messages
Screenshots are a powerful way to provide visual context to your agent. You can include them in user messages using the `ImagePart` type.
```python
from scrapybara import Scrapybara
from scrapybara.anthropic import Anthropic
from scrapybara.prompts import UBUNTU_SYSTEM_PROMPT
client = Scrapybara()
instance = client.start_ubuntu()
# Take a screenshot
screenshot = instance.screenshot().base_64_image
# Send the screenshot to the agent
messages = [
{
"role": "user",
"content": [
{
"type": "text",
"text": "What do you see in this screenshot? Describe the desktop environment."
},
{
"type": "image",
"image": 'data:image/png;base64,' + screenshot,
"mime_type": "image/png"
}
]
}
]
response = client.act(
model=Anthropic(),
system=UBUNTU_SYSTEM_PROMPT,
messages=messages,
)
print(response.text)
instance.stop()
```
```typescript
import { ScrapybaraClient } from "scrapybara";
import { anthropic } from "scrapybara/anthropic";
import { UBUNTU_SYSTEM_PROMPT } from "scrapybara/prompts";
const client = new ScrapybaraClient();
const instance = await client.startUbuntu();
// Take a screenshot
const screenshotResult = await instance.screenshot();
const screenshot = screenshotResult.base64Image;
// Send the screenshot to the agent
const messages = [
{
role: "user",
content: [
{
type: "text",
text: "What do you see in this screenshot? Describe the desktop environment."
},
{
type: "image",
image: 'data:image/png;base64,' + screenshot,
mime_type: "image/png"
}
]
}
];
const response = await client.act({
model: anthropic(),
system: UBUNTU_SYSTEM_PROMPT,
messages: messages,
});
console.log(response.text);
await instance.stop();
```
## Working with tools and reasoning
The Act SDK captures both tool calls and agent reasoning in its message architecture. Here's how you can access and work with this information:
### Examining tool calls and results
```python
from scrapybara import Scrapybara
from scrapybara.anthropic import Anthropic
from scrapybara.prompts import UBUNTU_SYSTEM_PROMPT
from scrapybara.tools import BashTool
client = Scrapybara()
instance = client.start_ubuntu()
response = client.act(
model=Anthropic(),
tools=[BashTool(instance)],
system=UBUNTU_SYSTEM_PROMPT,
prompt="Show me the current directory structure",
)
# Analyze the conversation steps
for message in response.messages:
if message.role == "assistant":
for part in message.content:
if part.type == "tool-call":
print(f"Tool called: {part.tool_name}")
print(f"Arguments: {part.args}")
elif message.role == "tool":
for part in message.content:
print(f"Tool result from {part.tool_name}: {part.result}")
instance.stop()
```
```typescript
import { ScrapybaraClient } from "scrapybara";
import { anthropic } from "scrapybara/anthropic";
import { UBUNTU_SYSTEM_PROMPT } from "scrapybara/prompts";
import { bashTool } from "scrapybara/tools";
const client = new ScrapybaraClient();
const instance = await client.startUbuntu();
const response = await client.act({
model: anthropic(),
tools: [bashTool(instance)],
system: UBUNTU_SYSTEM_PROMPT,
prompt: "Show me the current directory structure",
});
// Analyze the conversation steps
for (const message of response.messages) {
if (message.role === "assistant") {
for (const part of message.content) {
if (part.type === "tool-call") {
console.log(`Tool called: ${part.tool_name}`);
console.log(`Arguments: ${JSON.stringify(part.args)}`);
}
}
} else if (message.role === "tool") {
for (const part of message.content) {
console.log(`Tool result from ${part.tool_name}: ${JSON.stringify(part.result)}`);
}
}
}
await instance.stop();
```
### Accessing agent reasoning
```python
from scrapybara import Scrapybara
from scrapybara.anthropic import Anthropic
from scrapybara.prompts import UBUNTU_SYSTEM_PROMPT
from scrapybara.tools import BashTool, ComputerTool
client = Scrapybara()
instance = client.start_ubuntu()
response = client.act(
model=Anthropic(name="claude-3-7-sonnet-20250219-thinking"),
tools=[
BashTool(instance),
ComputerTool(instance),
],
system=UBUNTU_SYSTEM_PROMPT,
prompt="Open Firefox and navigate to scrapybara.com",
)
# Extract reasoning parts from assistant messages
for message in response.messages:
if message.role == "assistant":
for part in message.content:
if part.type == "reasoning":
print("Agent reasoning:")
print(part.reasoning)
# Or access reasoning directly from steps
for step in response.steps:
if step.reasoning_parts:
print(f"Step reasoning: {step.reasoning_parts}")
instance.stop()
```
```typescript
import { ScrapybaraClient } from "scrapybara";
import { anthropic } from "scrapybara/anthropic";
import { UBUNTU_SYSTEM_PROMPT } from "scrapybara/prompts";
import { bashTool, computerTool } from "scrapybara/tools";
const client = new ScrapybaraClient();
const instance = await client.startUbuntu();
const response = await client.act({
model: anthropic({ name: "claude-3-7-sonnet-20250219-thinking" }),
tools: [
bashTool(instance),
computerTool(instance),
],
system: UBUNTU_SYSTEM_PROMPT,
prompt: "Open Firefox and navigate to scrapybara.com",
});
// Extract reasoning parts from assistant messages
for (const message of response.messages) {
if (message.role === "assistant") {
for (const part of message.content) {
if (part.type === "reasoning") {
console.log("Agent reasoning:");
console.log(part.reasoning);
}
}
}
}
// Or access reasoning directly from steps
for (const step of response.steps) {
if (step.reasoning_parts) {
console.log(`Step reasoning: ${step.reasoning_parts}`);
}
}
await instance.stop();
```
## Best practices for multi-turn conversations
1. **Maintain message history**: Always use the returned `messages` from each call to maintain conversation context.
2. **Clear instructions**: Provide clear, specific instructions in each new user message.
3. **Handle context length**: For very long conversations, consider summarizing or truncating older messages to avoid exceeding model context limits.
4. **Include visual context**: Use screenshots when appropriate to provide additional context to the agent.
5. **Monitor token usage**: Track token usage through the `usage` field to prevent exceeding quotas or limits.
6. **Process message parts**: Parse and handle different message parts appropriately based on their type.
## Simple multi-turn example
Here's an interactive Read-Eval-Print Loop (REPL) implementation that allows you to have ongoing conversations with your agent:
```python
from scrapybara import Scrapybara
from scrapybara.anthropic import Anthropic
from scrapybara.prompts import UBUNTU_SYSTEM_PROMPT
from scrapybara.tools import BashTool, ComputerTool, EditTool
def agent_repl():
client = Scrapybara()
instance = client.start_ubuntu()
tools = [BashTool(instance), ComputerTool(instance), EditTool(instance)]
messages = []
print("Scrapybara REPL started. Type 'exit' to quit")
try:
while True:
# Get user input
user_input = input("\n> ")
# Exit command
if user_input.lower() == 'exit':
break
# Regular text command
messages.append({
"role": "user",
"content": [{"type": "text", "text": user_input}]
})
# Process with agent
print("Processing...")
response = client.act(
model=Anthropic(),
tools=tools,
system=UBUNTU_SYSTEM_PROMPT,
on_step=lambda step: print(step.text),
messages=messages
)
# Update conversation history
messages = response.messages
finally:
instance.stop()
print("Session ended.")
if __name__ == "__main__":
agent_repl()
```
```typescript
import * as readline from 'readline';
import { ScrapybaraClient } from "scrapybara";
import { anthropic } from "scrapybara/anthropic";
import { UBUNTU_SYSTEM_PROMPT } from "scrapybara/prompts";
import { bashTool, computerTool, editTool } from "scrapybara/tools";
async function agent_repl() {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const client = new ScrapybaraClient();
const instance = await client.startUbuntu();
const tools = [bashTool(instance), computerTool(instance), editTool(instance)];
let messages: any[] = [];
console.log("Scrapybara REPL started. Type 'exit' to quit");
const getInput = (): Promise => new Promise(resolve => rl.question("\n> ", resolve));
try {
while (true) {
// Get user input
const userInput = await getInput();
// Exit command
if (userInput.toLowerCase() === 'exit') {
break;
}
// Regular text command
messages.push({
role: "user",
content: [{type: "text", text: userInput}]
});
// Process with agent
console.log("Processing...");
const response = await client.act({
model: anthropic(),
tools: tools,
system: UBUNTU_SYSTEM_PROMPT,
onStep: (step) => console.log(step.text),
messages: messages
});
// Update conversation history
messages = response.messages;
}
} finally {
await instance.stop();
rl.close();
console.log("Session ended.");
}
}
agent_repl().catch(console.error);
```
# Tools
> Pre-built Scrapybara tools and how to define custom tools
## Scrapybara tools
### BashTool, ComputerTool, EditTool
`BashTool`, `ComputerTool`, and `EditTool` follow the same interface as the instance `bash`, `computer`, and `edit` methods. They each take in an `instance` parameter to interact with the instance.
* `ComputerTool` allows the agent to allows the agent to control mouse and keyboard. Supported for Ubuntu, Browser, and Windows instances.
* `BashTool` allows the agent to run bash commands. Supported only for Ubuntu instances.
* `EditTool` allows the agent to view, create, and edit files. Supported only for Ubuntu instances.
```python
from scrapybara import Scrapybara
from scrapybara.tools import BashTool, ComputerTool, EditTool
client = Scrapybara()
instance = client.start_ubuntu()
tools = [
BashTool(instance),
ComputerTool(instance),
EditTool(instance),
]
```
```typescript
import { ScrapybaraClient } from "scrapybara";
import { bashTool, computerTool, editTool } from "scrapybara/tools";
const client = new ScrapybaraClient();
const instance = await client.startUbuntu();
const tools = [
bashTool(instance),
computerTool(instance),
editTool(instance),
];
```
## Define custom tools
You can define custom tools. A tool needs a `name`, `description`, `parameters` (Pydantic model for Python, Zod object for TS), and an execute function (`__call__` for Python, `execute` for TS).
```python
from scrapybara.client import UbuntuInstance
from scrapybara.tools import Tool
from pydantic import BaseModel
class CapyParameters(BaseModel):
# Define your parameters here
pass
class CapyTool(Tool):
_instance: UbuntuInstance
def __init__(self, instance: UbuntuInstance) -> None:
super().__init__(
name="capy",
description="Use a capybara",
parameters=CapyParameters,
)
self._instance = instance
def __call__(self, **kwargs: Any) -> Any:
# Implement your tool logic here
pass
```
```typescript
import { UbuntuInstance } from "scrapybara";
import { tool } from "scrapybara/tools";
import { z } from "zod";
export function capyTool(instance: UbuntuInstance) {
return tool({
name: "capy",
description: "Use a capybara",
parameters: z.object({}), // Define your parameters here
execute: async () => {
// Implement your tool logic here
},
});
}
```
### BrowserTool
`BrowserTool` allows the agent to interact with a browser using Playwright.
Custom tools like BrowserTool may degrade model performance, as the models have not been trained on custom tools. For browser automation, we recommend sticking to ComputerTool.
The BrowserTool requires the browser to be started first.
```python
from playwright.sync_api import sync_playwright
class BrowserToolParameters(BaseModel):
"""Parameters for browser interaction commands."""
command: Literal[
"go_to", # Navigate to a URL
"get_html", # Get current page HTML
"evaluate", # Run JavaScript code
"click", # Click on an element
"type", # Type into an element
"screenshot", # Take a screenshot
"get_text", # Get text content of element
"get_attribute", # Get attribute of element
] = Field(
description="The browser command to execute. Required parameters per command:\n"
"- go_to: requires 'url'\n"
"- evaluate: requires 'code'\n"
"- click: requires 'selector'\n"
"- type: requires 'selector' and 'text'\n"
"- get_text: requires 'selector'\n"
"- get_attribute: requires 'selector' and 'attribute'\n"
"- get_html: no additional parameters\n"
"- screenshot: no additional parameters"
)
url: Optional[str] = Field(
None, description="URL for go_to command (required for go_to)"
)
selector: Optional[str] = Field(
None,
description="CSS selector for element operations (required for click, type, get_text, get_attribute)",
)
code: Optional[str] = Field(
None, description="JavaScript code for evaluate command (required for evaluate)"
)
text: Optional[str] = Field(
None, description="Text to type for type command (required for type)"
)
timeout: Optional[int] = Field(
30000, description="Timeout in milliseconds for operations"
)
attribute: Optional[str] = Field(
None,
description="Attribute name for get_attribute command (required for get_attribute)",
)
class BrowserTool(Tool):
"""A browser interaction tool that allows the agent to interact with a browser."""
_instance: Union[UbuntuInstance, BrowserInstance]
def __init__(self, instance: Union[UbuntuInstance, BrowserInstance]) -> None:
super().__init__(
name="browser",
description="Interact with a browser for web scraping and automation",
parameters=BrowserToolParameters,
)
self._instance = instance
def __call__(self, **kwargs: Any) -> Any:
params = BrowserToolParameters.model_validate(kwargs)
command = params.command
url = params.url
selector = params.selector
code = params.code
text = params.text
timeout = params.timeout or 30000
attribute = params.attribute
cdp_url = self._instance.browser.get_cdp_url().cdp_url
if cdp_url is None:
raise ValueError("CDP URL is not available, start the browser first")
with sync_playwright() as playwright:
browser = playwright.chromium.connect_over_cdp(cdp_url)
context = browser.contexts[0]
if not context.pages:
page = context.new_page()
else:
page = context.pages[0]
try:
if command == "go_to":
if not url:
raise ValueError("URL is required for go_to command")
page.goto(url, timeout=timeout)
return True
elif command == "get_html":
try:
return page.evaluate("() => document.documentElement.outerHTML")
except Exception:
# If page is navigating, just return what we can get
return page.evaluate("() => document.documentElement.innerHTML")
elif command == "evaluate":
if not code:
raise ValueError("Code is required for evaluate command")
return page.evaluate(code)
elif command == "click":
if not selector:
raise ValueError("Selector is required for click command")
page.click(selector, timeout=timeout)
return True
elif command == "type":
if not selector:
raise ValueError("Selector is required for type command")
if not text:
raise ValueError("Text is required for type command")
page.type(selector, text, timeout=timeout)
return True
elif command == "screenshot":
return image_result(
base64.b64encode(page.screenshot(type="png")).decode("utf-8")
)
elif command == "get_text":
if not selector:
raise ValueError("Selector is required for get_text command")
element = page.wait_for_selector(selector, timeout=timeout)
if element is None:
raise ValueError(f"Element not found: {selector}")
return element.text_content()
elif command == "get_attribute":
if not selector:
raise ValueError(
"Selector is required for get_attribute command"
)
if not attribute:
raise ValueError(
"Attribute is required for get_attribute command"
)
element = page.wait_for_selector(selector, timeout=timeout)
if element is None:
raise ValueError(f"Element not found: {selector}")
return element.get_attribute(attribute)
else:
raise ValueError(f"Unknown command: {command}")
except Exception as e:
raise ValueError(f"Browser command failed: {str(e)}")
finally:
browser.close()
```
```typescript
import { chromium } from "playwright";
export function browserTool(instance: UbuntuInstance | BrowserInstance) {
return tool({
name: "browser",
description: "Interact with a browser for web scraping and automation",
parameters: z.object({
command: z
.enum(["go_to", "get_html", "evaluate", "click", "type", "screenshot", "get_text", "get_attribute"])
.describe(
"The browser command to execute. Required parameters per command:\n- go_to: requires 'url'\n- evaluate: requires 'code'\n- click: requires 'selector'\n- type: requires 'selector' and 'text'\n- get_text: requires 'selector'\n- get_attribute: requires 'selector' and 'attribute'\n- get_html: no additional parameters\n- screenshot: no additional parameters"
),
url: z.string().optional().describe("URL for go_to command (required for go_to)"),
selector: z
.string()
.optional()
.describe("CSS selector for element operations (required for click, type, get_text, get_attribute)"),
code: z.string().optional().describe("JavaScript code for evaluate command (required for evaluate)"),
text: z.string().optional().describe("Text to type for type command (required for type)"),
timeout: z.number().optional().default(30000).describe("Timeout in milliseconds for operations"),
attribute: z
.string()
.optional()
.describe("Attribute name for get_attribute command (required for get_attribute)"),
}),
execute: async (params) => {
const { command, url, selector, code, text, timeout = 30000, attribute } = params;
const cdpUrl = await instance.browser.getCdpUrl();
if (!cdpUrl.cdpUrl) {
throw new Error("CDP URL is not available, start the browser first");
}
const browser = await chromium.connectOverCDP(cdpUrl.cdpUrl);
try {
const context = browser.contexts()[0];
const page = context.pages().length ? context.pages()[0] : await context.newPage();
try {
switch (command) {
case "go_to":
if (!url) throw new Error("URL is required for go_to command");
await page.goto(url, { timeout });
return true;
case "get_html":
try {
return await page.evaluate("document.documentElement.outerHTML");
} catch {
// If page is navigating, just return what we can get
return await page.evaluate("document.documentElement.innerHTML");
}
case "evaluate":
if (!code) throw new Error("Code is required for evaluate command");
return await page.evaluate(code);
case "click":
if (!selector) throw new Error("Selector is required for click command");
await page.click(selector, { timeout });
return true;
case "type":
if (!selector) throw new Error("Selector is required for type command");
if (!text) throw new Error("Text is required for type command");
await page.type(selector, text, { timeout });
return true;
case "screenshot":
const screenshot = await page.screenshot({ type: "png" });
return imageResult(screenshot.toString("base64"));
case "get_text":
if (!selector) throw new Error("Selector is required for get_text command");
const textElement = await page.waitForSelector(selector, { timeout });
if (!textElement) throw new Error(`Element not found: ${selector}`);
return await textElement.textContent();
case "get_attribute":
if (!selector) throw new Error("Selector is required for get_attribute command");
if (!attribute) throw new Error("Attribute is required for get_attribute command");
const element = await page.waitForSelector(selector, { timeout });
if (!element) throw new Error(`Element not found: ${selector}`);
return await element.getAttribute(attribute);
default:
throw new Error(`Unknown command: ${command}`);
}
} catch (error: any) {
throw new Error(`Browser command failed: ${error?.message || String(error)}`);
}
} finally {
await browser.close();
}
},
});
}
```
# OpenAI
> Build Scrapybara agents with OpenAI models
## Act SDK
Use OpenAI models with the Act SDK:
* Default: `computer-use-preview` (computer use beta)
Consume agent credits or bring your own API key. Without an API key, each step consumes 1 [agent credit](https://scrapybara.com/#pricing). With your own API key, model charges are billed directly to your OpenAI account.
```python Import model
from scrapybara.openai import OpenAI, UBUNTU_SYSTEM_PROMPT
# Consume agent credits
model = OpenAI()
# Bring your own API key
model = OpenAI(api_key="your_api_key")
```
```python Take action
from scrapybara import Scrapybara
from scrapybara.tools import BashTool, ComputerTool, EditTool
client = Scrapybara()
instance = client.start_ubuntu()
client.act(
tools=[
BashTool(instance),
ComputerTool(instance),
EditTool(instance),
],
model=model,
system=UBUNTU_SYSTEM_PROMPT,
prompt="Research Scrapybara",
)
```
```typescript Import model
import { openai, UBUNTU_SYSTEM_PROMPT } from "scrapybara/openai";
// Consume agent credits
const model = () => openai();
// Bring your own API key
const model = () => openai({ apiKey: "your_api_key" });
```
```typescript Take action
import { ScrapybaraClient } from "scrapybara";
import { bashTool, computerTool, editTool } from "scrapybara/tools";
const client = new ScrapybaraClient();
const instance = await client.startUbuntu();
await client.act({
tools: [
bashTool(instance),
computerTool(instance),
editTool(instance),
],
model,
system: UBUNTU_SYSTEM_PROMPT,
prompt: "Research Scrapybara",
})
```
# Anthropic
> Build Scrapybara agents with Anthropic models
## Act SDK
Use Anthropic models with the Act SDK:
* Default: `claude-3-7-sonnet-20250219` (computer use beta)
* `claude-3-7-sonnet-20250219-thinking` (computer use beta and extended thinking)
* `claude-3-5-sonnet-20241022` (computer use beta)
* `claude-sonnet-4-20250514` (computer use beta)
* `claude-sonnet-4-20250514-thinking` (computer use beta and extended thinking)
Consume agent credits or bring your own API key. Without an API key, each step consumes 1 [agent credit](https://scrapybara.com/#pricing). With your own API key, model charges are billed directly to your Anthropic account.
```python Import model
from scrapybara.anthropic import Anthropic, UBUNTU_SYSTEM_PROMPT
# Consume agent credits
model = Anthropic()
# Bring your own API key
model = Anthropic(api_key="your_api_key")
# Use extended thinking
model = Anthropic(name="claude-3-7-sonnet-20250219-thinking")
# Use Claude Sonnet 4 with extended thinking
model = Anthropic(name="claude-sonnet-4-20250514-thinking")
```
```python Take action
from scrapybara import Scrapybara
from scrapybara.tools import BashTool, ComputerTool, EditTool
client = Scrapybara()
instance = client.start_ubuntu()
client.act(
tools=[
BashTool(instance),
ComputerTool(instance),
EditTool(instance),
],
model=model,
system=UBUNTU_SYSTEM_PROMPT,
prompt="Research Scrapybara",
)
```
```typescript Import model
import { anthropic, UBUNTU_SYSTEM_PROMPT } from "scrapybara/anthropic";
// Consume agent credits
const model = () => anthropic();
// Bring your own API key
const model = () => anthropic({ apiKey: "your_api_key" });
```
```typescript Take action
import { ScrapybaraClient } from "scrapybara";
import { bashTool, computerTool, editTool } from "scrapybara/tools";
const client = new ScrapybaraClient();
const instance = await client.startUbuntu();
await client.act({
tools: [
bashTool(instance),
computerTool(instance),
editTool(instance),
],
model,
system: UBUNTU_SYSTEM_PROMPT,
prompt: "Research Scrapybara",
})
```
# Ubuntu
> Deploy an Ubuntu instance
## UbuntuInstance
The `UbuntuInstance` is a Ubuntu 22.04 desktop that supports interactive streaming, computer actions, bash commands, filesystem management, built-in Jupyter notebooks, and Chromium browser support. We recommend using this instance type for most tasks.
* Fast start up time
* 1x compute cost
## Start an Ubuntu instance
`python instance = client.start_ubuntu() `
`typescript const instance = await client.startUbuntu(); `
## Available actions
### screenshot
Take a base64 encoded image of the current desktop
`python base_64_image = instance.screenshot().base_64_image `
`typescript const base64Image = await instance.screenshot(); `
### get\_stream\_url
Get the interactive stream URL
`python stream_url = instance.get_stream_url().stream_url `
`typescript const streamUrl = await instance.getStreamUrl(); `
### computer
Perform computer actions with the mouse and keyboard
#### `move_mouse`
Move mouse cursor to specific coordinates
\[x, y] coordinates to move to
List of modifier keys to hold during the action
Whether to take a screenshot after the action
```python Move mouse
instance.computer(action="move_mouse", coordinates=[100, 200])
```
```python Move mouse while holding shift
instance.computer(action="move_mouse", coordinates=[100, 200], hold_keys=["shift"])
```
```python Move mouse without taking a screenshot
instance.computer(action="move_mouse", coordinates=[100, 200], screenshot=False)
```
```typescript Move mouse
await instance.computer({action: "move_mouse", coordinates: [100, 200]});
```
```typescript Move mouse while holding shift
await instance.computer({action: "move_mouse", coordinates: [100, 200], holdKeys: ["shift"]});
```
```typescript Move mouse without taking a screenshot
await instance.computer({action: "move_mouse", coordinates: [100, 200], screenshot: false});
```
#### `click_mouse`
Perform a mouse click at current position or specified coordinates
Mouse button to click ("left", "right", "middle", "back", "forward")
Type of click action ("down", "up", "click")
\[x, y] coordinates to click at
Number of clicks
List of modifier keys to hold during the action
Whether to take a screenshot after the action
```python Left click at current position
instance.computer(action="click_mouse", button="left")
```
```python Right click at coordinates
instance.computer(action="click_mouse", button="right", coordinates=[300, 400])
```
```python Mouse down
instance.computer(action="click_mouse", button="left", click_type="down")
```
```python Double click at coordinates
instance.computer(action="click_mouse", button="left", num_clicks=2, coordinates=[500, 300])
```
```typescript Left click at current position
await instance.computer({action: "click_mouse", button: "left"});
```
```typescript Right click at coordinates
await instance.computer({action: "click_mouse", button: "right", coordinates: [300, 400]});
```
```typescript Mouse down
await instance.computer({action: "click_mouse", button: "left", clickType: "down"});
```
```typescript Double click at coordinates
await instance.computer({action: "click_mouse", button: "left", numClicks: 2, coordinates: [500, 300]});
```
#### `drag_mouse`
Click and drag from current position to specified coordinates
List of \[x, y] coordinate pairs defining the drag path
List of modifier keys to hold during the action
Whether to take a screenshot after the action
```python Drag to coordinates
instance.computer(action="drag_mouse", path=[[100, 200], [300, 400]])
```
```typescript Drag to coordinates
await instance.computer({action: "drag_mouse", path: [[100, 200], [300, 400]]});
```
#### `scroll`
Scroll horizontally and/or vertically
\[x, y] coordinates to scroll at
Horizontal scroll amount
Vertical scroll amount
List of modifier keys to hold during the action
Whether to take a screenshot after the action
```python Scroll down
instance.computer(action="scroll", coordinates=[100, 100], delta_x=0, delta_y=200)
```
```python Scroll right
instance.computer(action="scroll", coordinates=[100, 100], delta_x=200, delta_y=0)
```
```typescript Scroll down
await instance.computer({action: "scroll", coordinates: [100, 100], deltaX: 0, deltaY: 200});
```
```typescript Scroll right
await instance.computer({action: "scroll", coordinates: [100, 100], deltaX: 100, deltaY: 0});
```
#### `press_key`
Press a key or combination of keys. Scrapybara supports keys defined by [X keysyms](https://github.com/D-Programming-Deimos/libX11/blob/master/c/X11/keysymdef.h). Common aliases are also supported:
* `alt` → `Alt_L`
* `ctrl`, `control` → `Control_L`
* `meta` → `Meta_L`
* `super` → `Super_L`
* `shift` → `Shift_L`
List of keys to press
Time to hold keys in seconds
Whether to take a screenshot after the action
```python Press ctrl+c
instance.computer(action="press_key", keys=["ctrl", "c"])
```
```python Hold shift for 2 seconds
instance.computer(action="press_key", keys=["shift"], duration=2)
```
```python Press enter/return
instance.computer(action="press_key", keys=["Return"])
```
```typescript Press ctrl+c
await instance.computer({action: "press_key", keys: ["ctrl", "c"]});
```
```typescript Hold shift for 2 seconds
await instance.computer({action: "press_key", keys: ["shift"], duration: 2});
```
```typescript Press enter/return
await instance.computer({action: "press_key", keys: ["Return"]});
```
#### `type_text`
Type text into the active window
Text to type
List of modifier keys to hold while typing
Whether to take a screenshot after the action
```python Type text
instance.computer(action="type_text", text="Hello world")
```
```python Type text without taking a screenshot
instance.computer(action="type_text", text="Hello world", screenshot=False)
```
```typescript Type text
await instance.computer({action: "type_text", text: "Hello world"});
```
```typescript Type text without taking a screenshot
await instance.computer({action: "type_text", text: "Hello world", screenshot: false});
```
#### `wait`
Wait for a specified duration
Time to wait in seconds
Whether to take a screenshot after the action
```python Wait for 3 seconds
instance.computer(action="wait", duration=3)
```
```typescript Wait for 3 seconds
await instance.computer({action: "wait", duration: 3});
```
#### `take_screenshot`
Take a screenshot of the desktop
```python
screenshot = instance.computer(action="take_screenshot").base_64_image
```
```typescript
const screenshot = await instance.computer({action: "take_screenshot"}).base64Image;
```
#### `get_cursor_position`
Get current mouse cursor coordinates
```python
cursor_position = instance.computer(action="get_cursor_position").output
```
```typescript
const cursorPosition = await instance.computer({action: "get_cursor_position"}).output;
```
### bash
Run a bash command
> **Note:** Bash commands time out after 10 seconds by default, but you can customize this with the `timeout` parameter. When a command times out, it will continue running in the session. To run other commands while waiting for a long-running command to complete, start them in a different session. Use `check_session` to check back on a session's status. Once a command finishes execution, the session becomes available again for new commands.
```python Run a bash command
output = instance.bash(command="ls -la")
```
```python Run a command in a specific session
output = instance.bash(command="ls -la", session=1)
```
```python Run a command with custom timeout
output = instance.bash(command="sleep 30", timeout=60)
```
```python Restart a session
instance.bash(restart=True, session=1)
```
```python List available bash sessions
sessions = instance.bash(list_sessions=True)
```
```python Check the status of a session
session_exists = instance.bash(check_session=1)
```
```typescript Run a bash command
const output = await instance.bash({command: "ls -la"});
```
```typescript Run a command in a specific session
const output = await instance.bash({command: "ls -la", session: 1});
```
```typescript Run a command with custom timeout
const output = await instance.bash({command: "sleep 30", timeout: 60});
```
```typescript Restart a session
await instance.bash({restart: true, session: 1});
```
```typescript List available bash sessions
const sessions = await instance.bash({listSessions: true});
```
```typescript Check the status of a session
const sessionExists = await instance.bash({checkSession: 1});
```
### edit
> **Deprecated:** Please use the `file` tool instead which provides more comprehensive file management capabilities.
Edit a file on the instance
```python Create a new file
instance.edit(command="create", path="hello.txt", file_text="Hello world")
```
```python Replace text in a file
instance.edit(command="str_replace", path="hello.txt", old_str="Hello", new_str="Hi")
```
```python Insert text at a specific line
instance.edit(command="insert", path="hello.txt", insert_line=2, file_text="New line")
```
```typescript Create a new file
await instance.edit({command: "create", path: "hello.txt", fileText: "Hello world"});
```
```typescript Replace text in a file
await instance.edit({command: "str_replace", path: "hello.txt", oldStr: "Hello", newStr: "Hi"});
```
```typescript Insert text at a specific line
await instance.edit({command: "insert", path: "hello.txt", insertLine: 2, fileText: "New line"});
```
### file
Manage files and directories on the instance
#### `read`
Read the content of a file in text or binary mode
Path to the file to read
Read mode: "text" or "binary"
Text encoding when mode is "text"
```python Read text file
content = instance.file(command="read", path="my_file.txt")
```
```python Read binary file
binary_content = instance.file(command="read", path="image.png", mode="binary")
```
```typescript Read text file
const content = await instance.file({command: "read", path: "my_file.txt"});
```
```typescript Read binary file
const binaryContent = await instance.file({command: "read", path: "image.png", mode: "binary"});
```
#### `write`
Write content to a file, overwriting if it exists
Path to the file to write
Content to write to the file
Write mode: "text" or "binary" (base64 encoded for binary)
Text encoding when mode is "text"
```python Write text to file
instance.file(command="write", path="my_file.txt", content="Hello world")
```
```typescript Write text to file
await instance.file({command: "write", path: "my_file.txt", content: "Hello world"});
```
#### `append`
Append content to an existing file or create it if it doesn't exist
Path to the file to append to
Content to append to the file
Append mode: "text" or "binary" (base64 encoded for binary)
Text encoding when mode is "text"
```python Append text
instance.file(command="append", path="my_file.txt", content="New content")
```
```typescript Append text
await instance.file({command: "append", path: "my_file.txt", content: "New content"});
```
#### `exists`
Check if a path exists
Path to check
```python Check if file exists
exists = instance.file(command="exists", path="my_file.txt")
```
```typescript Check if file exists
const exists = await instance.file({command: "exists", path: "my_file.txt"});
```
#### `list`
List the contents of a directory
Path to the directory to list
```python List directory contents
files = instance.file(command="list", path="my_directory")
```
```typescript List directory contents
const files = await instance.file({command: "list", path: "my_directory"});
```
#### `mkdir`
Create a directory, including parent directories if needed
Path to the directory to create
```python Create directory
instance.file(command="mkdir", path="new_directory")
```
```typescript Create directory
await instance.file({command: "mkdir", path: "new_directory"});
```
#### `rmdir`
Remove an empty directory
Path to the directory to remove
```python Remove directory
instance.file(command="rmdir", path="empty_directory")
```
```typescript Remove directory
await instance.file({command: "rmdir", path: "empty_directory"});
```
#### `delete`
Delete a file or directory
Path to delete
Delete directory contents recursively
```python Delete file
instance.file(command="delete", path="file.txt")
```
```python Delete directory recursively
instance.file(command="delete", path="directory", recursive=True)
```
```typescript Delete file
await instance.file({command: "delete", path: "file.txt"});
```
```typescript Delete directory recursively
await instance.file({command: "delete", path: "directory", recursive: true});
```
#### `move`
Move or rename a file or directory
Source path
Destination path
```python Move or rename
instance.file(command="move", src="old_name.txt", dst="new_name.txt")
```
```typescript Move or rename
await instance.file({command: "move", src: "old_name.txt", dst: "new_name.txt"});
```
#### `copy`
Copy a file or directory
Source path
Destination path
```python Copy file or directory
instance.file(command="copy", src="source.txt", dst="destination.txt")
```
```typescript Copy file or directory
await instance.file({command: "copy", src: "source.txt", dst: "destination.txt"});
```
#### `view`
View file content with line numbers or list directory contents
Path to view
Optional \[start, end] line range to view
```python View with line numbers
content = instance.file(command="view", path="my_file.txt")
```
```python View specific line range
content = instance.file(command="view", path="my_file.txt", view_range=[10, 20])
```
```typescript View with line numbers
const content = await instance.file({command: "view", path: "my_file.txt"});
```
```typescript View specific line range
const content = await instance.file({command: "view", path: "my_file.txt", viewRange: [10, 20]});
```
#### `create`
Create a new file with the given content, failing if it already exists
Path to the file to create
Content to write to the new file
Create mode: "text" or "binary" (base64 encoded for binary)
Text encoding when mode is "text"
```python Create a new file
instance.file(command="create", path="new_file.txt", content="New file content")
```
```typescript Create a new file
await instance.file({command: "create", path: "new_file.txt", content: "New file content"});
```
#### `replace`
Replace a string in a file
Path to the file
String to replace
Replacement string
Replace all occurrences if true, only first occurrence if false
```python Replace first occurrence
instance.file(command="replace", path="my_file.txt", old_str="old text", new_str="new text")
```
```python Replace all occurrences
instance.file(command="replace", path="my_file.txt", old_str="old text", new_str="new text", all_occurrences=True)
```
```typescript Replace first occurrence
await instance.file({command: "replace", path: "my_file.txt", oldStr: "old text", newStr: "new text"});
```
```typescript Replace all occurrences
await instance.file({command: "replace", path: "my_file.txt", oldStr: "old text", newStr: "new text", allOccurrences: true});
```
#### `insert`
Insert text at a specific line in a file
Path to the file
Line number to insert at (1-based)
Text to insert
```python Insert at line
instance.file(command="insert", path="my_file.txt", line=2, text="New line content")
```
```typescript Insert at line
await instance.file({command: "insert", path: "my_file.txt", line: 2, text: "New line content"});
```
#### `delete_lines`
Delete specified lines from a file
Path to the file
Array of line numbers to delete (1-based)
```python Delete specific lines
instance.file(command="delete_lines", path="my_file.txt", lines=[2, 5, 10])
```
```typescript Delete specific lines
await instance.file({command: "delete_lines", path: "my_file.txt", lines: [2, 5, 10]});
```
#### `undo`
Undo the last text editing operation on a file
Path to the file
```python Undo last edit
instance.file(command="undo", path="my_file.txt")
```
```typescript Undo last edit
await instance.file({command: "undo", path: "my_file.txt"});
```
#### `grep`
Search for a pattern in a file or directory
Regular expression pattern to search for
Path to file or directory to search
Whether search is case sensitive
Search directories recursively (required for directory paths)
Include line numbers in results
```python Search in file
results = instance.file(command="grep", path="my_file.txt", pattern="search term")
```
```python Recursive search in directory
results = instance.file(command="grep", path="my_directory", pattern="search term",
recursive=True, case_sensitive=False)
```
```typescript Search in file
const results = await instance.file({command: "grep", path: "my_file.txt", pattern: "search term"});
```
```typescript Recursive search in directory
const results = await instance.file({command: "grep", path: "my_directory", pattern: "search term",
recursive: true, caseSensitive: false});
```
### `upload`
Upload a file to the instance
The file to upload, can be a file object, bytes, or string
Destination path on the instance
```python
# Upload a file from local path
with open("local_file.txt", "rb") as f:
response = instance.upload(file=f, path="uploaded_file.txt")
# Upload string content as a file
instance.upload(file="Hello World", path="hello.txt")
# Upload with explicit filename and content type
instance.upload(
file=("myfile.txt", "File content", "text/plain"),
path="myfile.txt"
)
```
```typescript
// Upload a file
const file = new File(["file content"], "filename.txt", { type: "text/plain" });
const response = await instance.upload(file, { path: "uploaded_file.txt" });
// Upload a Blob
const blob = new Blob(["Hello World"], { type: "text/plain" });
await instance.upload(blob, { path: "hello.txt" });
```
### `stop`
Stop the instance
`python instance.stop() `
`typescript await instance.stop(); `
### `pause`
Pause the instance
`python instance.pause() `
`typescript await instance.pause(); `
### `resume`
Resume the instance
```python Resume with default timeout
instance.resume()
```
```python Resume with custom timeout
instance.resume(timeout_hours=2.5)
```
```typescript Resume with default timeout
await instance.resume();
```
```typescript Resume with custom timeout
await instance.resume({timeoutHours: 2.5});
```
## Compatible tools
* `BashTool`
* `ComputerTool`
* `EditTool`
## Screen resolution
By default, the Ubuntu instance runs at 1024x768 resolution. You can specify a custom resolution when starting the instance:
```python
instance = client.start_ubuntu(resolution=[1920, 1080])
```
```typescript
const instance = await client.startUbuntu({resolution: [1920, 1080]});
```
## Additional protocols
The Ubuntu instance supports several protocols that provide additional functionality:
* [Browser](/protocols/browser) - Control the browser with Playwright
* [Code Execution](/protocols/code) - Execute code in Python and JavaScript
* [Environment Variables](/protocols/env) - Manage environment variables
# Browser
> Control a browser directly in your Scrapybara instance with Playwright
```python
from scrapybara import Scrapybara
client = Scrapybara(api_key="your_api_key")
instance = client.start_ubuntu()
```
```python
cdp_url = instance.browser.start().cdp_url
```
To save the authenticated state of a browser session, use the `saveAuth` method.
```python
auth_state_id = instance.browser.save_auth(name="default").auth_state_id
```
Now, you can reuse the saved auth state on other instances by passing the `auth_state_id` to the `authenticate` method. The browser needs to be started first.
```python
instance.browser.authenticate(auth_state_id=auth_state_id)
```
```python
from playwright.sync_api import sync_playwright
playwright = sync_playwright().start()
browser = playwright.chromium.connect_over_cdp(cdp_url)
```
```python
page = browser.new_page()
page.goto("https://scrapybara.com")
screenshot = page.screenshot()
```
```python
instance.browser.stop()
```
```typescript
import { ScrapybaraClient } from "scrapybara";
const client = new ScrapybaraClient({ apiKey: "your_api_key" });
const instance = await client.startUbuntu();
```
```typescript
const cdpUrl = await instance.browser.start().cdpUrl;
```
To save the authenticated state of a browser session, use the `saveAuth` method.
```typescript
const authStateId = await instance.browser.saveAuth({
name: "default",
}).authStateId;
```
Now, you can reuse the saved auth state on other instances by passing the `authStateId` to the `authenticate` method. The browser needs to be started first.
```typescript
await instance.browser.authenticate({ authStateId });
```
```typescript
import { chromium } from "playwright";
const browser = await chromium.connectOverCDP(cdpUrl);
```
```typescript
const page = await browser.newPage();
await page.goto("https://scrapybara.com");
const screenshot = await page.screenshot();
```
```typescript
await instance.browser.stop();
```
# Code Execution
> Execute code in your Scrapybara instance
```python
from scrapybara import Scrapybara
client = Scrapybara(api_key="your_api_key")
instance = client.start_ubuntu()
```
```python
result = instance.code.execute(
code="print('Hello from Scrapybara!')",
kernel_name="python3" # Optional: specify kernel
)
```
```python
kernels = instance.notebook.list_kernels()
```
```python
notebook = instance.notebook.create(
name="my_notebook",
kernel_name="python3"
)
```
```python
# Add a code cell
cell = instance.notebook.add_cell(
notebook_id=notebook.id,
type="code",
content="print('Hello from Scrapybara!')"
)
# Execute the cell
result = instance.notebook.execute_cell(
notebook_id=notebook.id,
cell_id=cell.id
)
```
```python
# Execute all cells in the notebook
results = instance.notebook.execute(notebook_id=notebook.id)
```
```python
# Delete the notebook when done
instance.notebook.delete(notebook_id=notebook.id)
```
```typescript
import { ScrapybaraClient } from "scrapybara";
const client = new ScrapybaraClient({ apiKey: "your_api_key" });
const instance = await client.startUbuntu();
```
```typescript
const result = await instance.code.execute({
code: "print('Hello from Scrapybara!')",
kernelName: "python3" // Optional: specify kernel
});
```
```typescript
const kernels = await instance.notebook.listKernels();
```
```typescript
const notebook = await instance.notebook.create({
name: "my_notebook",
kernelName: "python3"
});
```
```typescript
// Add a code cell
const cell = await instance.notebook.addCell({
notebookId: notebook.id,
type: "code",
content: "print('Hello from Scrapybara!')"
});
// Execute the cell
const result = await instance.notebook.executeCell({
notebookId: notebook.id,
cellId: cell.id
});
```
```typescript
// Execute all cells in the notebook
const results = await instance.notebook.execute({
notebookId: notebook.id
});
```
```typescript
// Delete the notebook when done
await instance.notebook.delete({
notebookId: notebook.id
});
```
# Environment Variables
> Manage environment variables in your Scrapybara instance
```python
from scrapybara import Scrapybara
client = Scrapybara(api_key="your_api_key")
instance = client.start_ubuntu()
```
```python
# Set one or more environment variables
instance.env.set(
variables={
"API_KEY": "secret_key",
"DEBUG": "true",
"DATABASE_URL": "postgresql://localhost:5432/db"
}
)
```
```python
# Get all environment variables
response = instance.env.get()
env_vars = response.variables
```
```python
# Delete specific environment variables
instance.env.delete(
keys=["API_KEY", "DEBUG"]
)
```
```typescript
import { ScrapybaraClient } from "scrapybara";
const client = new ScrapybaraClient({ apiKey: "your_api_key" });
const instance = await client.startUbuntu();
```
```typescript
// Set one or more environment variables
await instance.env.set({
variables: {
API_KEY: "secret_key",
DEBUG: "true",
DATABASE_URL: "postgresql://localhost:5432/db"
}
});
```
```typescript
// Get all environment variables
const response = await instance.env.get();
const envVars = response.variables;
```
```typescript
// Delete specific environment variables
await instance.env.delete({
keys: ["API_KEY", "DEBUG"]
});
```
# Browser
> Deploy a Browser instance
## BrowserInstance
The `BrowserInstance` is a lightweight Chromium instance that supports interactive streaming, computer actions, Playwright CDP control, and saving/loading auth states. We recommend using this instance type if your task is constrained to the browser.
* Fastest start up time
* 1x compute cost
## Start a browser instance
```python
instance = client.start_browser()
```
```typescript
const instance = await client.startBrowser();
```
## Available actions
### get\_cdp\_url
Get the Playwright CDP URL
```python
cdp_url = instance.get_cdp_url().cdp_url
```
```typescript
const cdpUrl = await instance.getCdpUrl().cdpUrl;
```
### save\_auth
Save the browser auth state
```python
auth_state_id = instance.browser.save_auth(name="default").auth_state_id
```
```typescript
const authStateId = await instance.browser.saveAuth({name: "default"}).authStateId;
```
### authenticate
Authenticate the browser using a saved auth state
```python
instance.browser.authenticate(auth_state_id=auth_state_id)
```
```typescript
await instance.browser.authenticate({authStateId: authStateId});
```
### screenshot
Take a base64 encoded image of the current desktop
```python
base_64_image = instance.screenshot().base_64_image
```
```typescript
const base64Image = await instance.screenshot();
```
### get\_stream\_url
Get the interactive stream URL
```python
stream_url = instance.get_stream_url().stream_url
```
```typescript
const streamUrl = await instance.getStreamUrl();
```
### computer
Perform computer actions with the mouse and keyboard
#### `move_mouse`
Move mouse cursor to specific coordinates
\[x, y] coordinates to move to
List of modifier keys to hold during the action
```python Move mouse
instance.computer(action="move_mouse", coordinates=[100, 200])
```
```python Move mouse while holding shift
instance.computer(action="move_mouse", coordinates=[100, 200], hold_keys=["shift"])
```
```typescript Move mouse
await instance.computer({action: "move_mouse", coordinates: [100, 200]});
```
```typescript Move mouse while holding shift
await instance.computer({action: "move_mouse", coordinates: [100, 200], holdKeys: ["shift"]});
```
#### `click_mouse`
Perform a mouse click at current position or specified coordinates
Mouse button to click ("left", "right", "middle", "back", "forward")
Type of click action ("down", "up", "click")
\[x, y] coordinates to click at
Number of clicks
List of modifier keys to hold during the action
```python Left click at current position
instance.computer(action="click_mouse", button="left")
```
```python Right click at coordinates
instance.computer(action="click_mouse", button="right", coordinates=[300, 400])
```
```python Mouse down
instance.computer(action="click_mouse", button="left", click_type="down")
```
```python Double click at coordinates
instance.computer(action="click_mouse", button="left", num_clicks=2, coordinates=[500, 300])
```
```typescript Left click at current position
await instance.computer({action: "click_mouse", button: "left"});
```
```typescript Right click at coordinates
await instance.computer({action: "click_mouse", button: "right", coordinates: [300, 400]});
```
```typescript Mouse down
await instance.computer({action: "click_mouse", button: "left", clickType: "down"});
```
```typescript Double click at coordinates
await instance.computer({action: "click_mouse", button: "left", numClicks: 2, coordinates: [500, 300]});
```
#### `drag_mouse`
Click and drag from current position to specified coordinates
List of \[x, y] coordinate pairs defining the drag path
List of modifier keys to hold during the action
```python Drag to coordinates
instance.computer(action="drag_mouse", path=[[100, 200], [300, 400]])
```
```typescript Drag to coordinates
await instance.computer({action: "drag_mouse", path: [[100, 200], [300, 400]]});
```
#### `scroll`
Scroll horizontally and/or vertically
\[x, y] coordinates to scroll at
Horizontal scroll amount
Vertical scroll amount
List of modifier keys to hold during the action
```python Scroll down
instance.computer(action="scroll", coordinates=[100, 100], delta_x=0, delta_y=200)
```
```python Scroll right
instance.computer(action="scroll", coordinates=[100, 100], delta_x=200, delta_y=0)
```
```typescript Scroll down
await instance.computer({action: "scroll", coordinates: [100, 100], deltaX: 0, deltaY: 200});
```
```typescript Scroll right
await instance.computer({action: "scroll", coordinates: [100, 100], deltaX: 100, deltaY: 0});
```
#### `press_key`
Press a key or combination of keys. Scrapybara supports keys defined by [X keysyms](https://github.com/D-Programming-Deimos/libX11/blob/master/c/X11/keysymdef.h). Common aliases are also supported:
* `alt` → `Alt_L`
* `ctrl`, `control` → `Control_L`
* `meta` → `Meta_L`
* `super` → `Super_L`
* `shift` → `Shift_L`
* `enter`, `return` → `Return`
List of keys to press
Time to hold keys in seconds
```python Press ctrl+c
instance.computer(action="press_key", keys=["ctrl", "c"])
```
```python Hold shift for 2 seconds
instance.computer(action="press_key", keys=["shift"], duration=2)
```
```python Press enter/return
instance.computer(action="press_key", keys=["Return"])
```
```typescript Press ctrl+c
await instance.computer({action: "press_key", keys: ["ctrl", "c"]});
```
```typescript Hold shift for 2 seconds
await instance.computer({action: "press_key", keys: ["shift"], duration: 2});
```
```typescript Press enter/return
await instance.computer({action: "press_key", keys: ["Return"]});
```
#### `type_text`
Type text into the active window
Text to type
List of modifier keys to hold while typing
```python Type text
instance.computer(action="type_text", text="Hello world")
```
```typescript Type text
await instance.computer({action: "type_text", text: "Hello world"});
```
#### `wait`
Wait for a specified duration
Time to wait in seconds
```python Wait for 3 seconds
instance.computer(action="wait", duration=3)
```
```typescript Wait for 3 seconds
await instance.computer({action: "wait", duration: 3});
```
#### `take_screenshot`
Take a screenshot of the desktop
```python
screenshot = instance.computer(action="take_screenshot").base64_image
```
```typescript
const screenshot = await instance.computer({action: "take_screenshot"}).base64Image;
```
#### `get_cursor_position`
Get current mouse cursor coordinates
```python
cursor_position = instance.computer(action="get_cursor_position").output
```
```typescript
const cursorPosition = await instance.computer({action: "get_cursor_position"}).output;
```
### `stop`
Stop the instance
```python
instance.stop()
```
```typescript
await instance.stop();
```
### `pause`
Pause the instance
```python
instance.pause()
```
```typescript
await instance.pause();
```
### `resume`
Resume the instance
```python Resume with default timeout
instance.resume()
```
```python Resume with custom timeout
instance.resume(timeout_hours=2.5)
```
```typescript Resume with default timeout
await instance.resume();
```
```typescript Resume with custom timeout
await instance.resume({timeoutHours: 2.5});
```
## Compatible tools
* `ComputerTool`
## Screen resolution
By default, the Browser instance runs at 1024x768 resolution. You can specify a custom resolution when starting the instance:
```python
instance = client.start_browser(resolution=[1920, 1080])
```
```typescript
const instance = await client.startBrowser({resolution: [1920, 1080]});
```
# Windows
> Deploy a Windows instance
Windows instances are in early access. Join our Discord to get started.
## WindowsInstance
The `WindowsInstance` is a full-fledged Windows 11 desktop that supports interactive streaming and computer actions. We recommend using this instance type if you need to interact with Windows-only applications.
* Slow start up time
* 2x compute cost
## Start a Windows instance
```python
instance = client.start_windows()
```
```typescript
const instance = await client.startWindows();
```
## Available actions
### screenshot
Take a base64 encoded image of the current desktop
```python
base_64_image = instance.screenshot().base_64_image
```
```typescript
const base64Image = await instance.screenshot();
```
### get\_stream\_url
Get the interactive stream URL
```python
stream_url = instance.get_stream_url().stream_url
```
```typescript
const streamUrl = await instance.getStreamUrl();
```
### computer
Perform computer actions with the mouse and keyboard
#### `move_mouse`
Move mouse cursor to specific coordinates
\[x, y] coordinates to move to
List of modifier keys to hold during the action
```python Move mouse
instance.computer(action="move_mouse", coordinates=[100, 200])
```
```python Move mouse while holding shift
instance.computer(action="move_mouse", coordinates=[100, 200], hold_keys=["shift"])
```
```typescript Move mouse
await instance.computer({action: "move_mouse", coordinates: [100, 200]});
```
```typescript Move mouse while holding shift
await instance.computer({action: "move_mouse", coordinates: [100, 200], holdKeys: ["shift"]});
```
#### `click_mouse`
Perform a mouse click at current position or specified coordinates
Mouse button to click ("left", "right", "middle", "back", "forward")
Type of click action ("down", "up", "click")
\[x, y] coordinates to click at
Number of clicks
List of modifier keys to hold during the action
```python Left click at current position
instance.computer(action="click_mouse", button="left")
```
```python Right click at coordinates
instance.computer(action="click_mouse", button="right", coordinates=[300, 400])
```
```python Mouse down
instance.computer(action="click_mouse", button="left", click_type="down")
```
```python Double click at coordinates
instance.computer(action="click_mouse", button="left", num_clicks=2, coordinates=[500, 300])
```
```typescript Left click at current position
await instance.computer({action: "click_mouse", button: "left"});
```
```typescript Right click at coordinates
await instance.computer({action: "click_mouse", button: "right", coordinates: [300, 400]});
```
```typescript Mouse down
await instance.computer({action: "click_mouse", button: "left", clickType: "down"});
```
```typescript Double click at coordinates
await instance.computer({action: "click_mouse", button: "left", numClicks: 2, coordinates: [500, 300]});
```
#### `drag_mouse`
Click and drag from current position to specified coordinates
List of \[x, y] coordinate pairs defining the drag path
List of modifier keys to hold during the action
```python Drag to coordinates
instance.computer(action="drag_mouse", path=[[100, 200], [300, 400]])
```
```typescript Drag to coordinates
await instance.computer({action: "drag_mouse", path: [[100, 200], [300, 400]]});
```
#### `scroll`
Scroll horizontally and/or vertically
\[x, y] coordinates to scroll at
Horizontal scroll amount
Vertical scroll amount
List of modifier keys to hold during the action
```python Scroll down
instance.computer(action="scroll", coordinates=[100, 100], delta_x=0, delta_y=200)
```
```python Scroll right
instance.computer(action="scroll", coordinates=[100, 100], delta_x=200, delta_y=0)
```
```typescript Scroll down
await instance.computer({action: "scroll", coordinates: [100, 100], deltaX: 0, deltaY: 200});
```
```typescript Scroll right
await instance.computer({action: "scroll", coordinates: [100, 100], deltaX: 100, deltaY: 0});
```
#### `press_key`
Press a key or combination of keys. Scrapybara supports keys defined by [X keysyms](https://github.com/D-Programming-Deimos/libX11/blob/master/c/X11/keysymdef.h). Common aliases are also supported:
* `alt` → `Alt_L`
* `ctrl`, `control` → `Control_L`
* `meta` → `Meta_L`
* `super` → `Super_L`
* `shift` → `Shift_L`
* `enter`, `return` → `Return`
List of keys to press
Time to hold keys in seconds
```python Press ctrl+c
instance.computer(action="press_key", keys=["ctrl", "c"])
```
```python Hold shift for 2 seconds
instance.computer(action="press_key", keys=["shift"], duration=2)
```
```python Press enter/return
instance.computer(action="press_key", keys=["Return"])
```
```typescript Press ctrl+c
await instance.computer({action: "press_key", keys: ["ctrl", "c"]});
```
```typescript Hold shift for 2 seconds
await instance.computer({action: "press_key", keys: ["shift"], duration: 2});
```
```typescript Press enter/return
await instance.computer({action: "press_key", keys: ["Return"]});
```
#### `type_text`
Type text into the active window
Text to type
List of modifier keys to hold while typing
```python Type text
instance.computer(action="type_text", text="Hello world")
```
```typescript Type text
await instance.computer({action: "type_text", text: "Hello world"});
```
#### `wait`
Wait for a specified duration
Time to wait in seconds
```python Wait for 3 seconds
instance.computer(action="wait", duration=3)
```
```typescript Wait for 3 seconds
await instance.computer({action: "wait", duration: 3});
```
#### `take_screenshot`
Take a screenshot of the desktop
```python
screenshot = instance.computer(action="take_screenshot").base64_image
```
```typescript
const screenshot = await instance.computer({action: "take_screenshot"}).base64Image;
```
#### `get_cursor_position`
Get current mouse cursor coordinates
```python
cursor_position = instance.computer(action="get_cursor_position").output
```
```typescript
const cursorPosition = await instance.computer({action: "get_cursor_position"}).output;
```
### `stop`
Stop the instance
```python
instance.stop()
```
```typescript
await instance.stop();
```
### `pause`
Pause the instance
```python
instance.pause()
```
```typescript
await instance.pause();
```
### `resume`
Resume the instance
```python Resume with default timeout
instance.resume()
```
```python Resume with custom timeout
instance.resume(timeout_hours=2.5)
```
```typescript Resume with default timeout
await instance.resume();
```
```typescript Resume with custom timeout
await instance.resume({timeoutHours: 2.5});
```
## Compatible tools
* `ComputerTool`
## Screen resolution
By default, the Windows instance runs at 1024x768 resolution.
# Starter Templates
> Templates for getting started with Scrapybara instances and the Act SDK
## Python
View the template
## TypeScript
View the template
# Cursor Rules
> Recommended .cursorrules for working with Cursor
## .cursorrules
```md .cursorrules
You are working with Scrapybara, a Python SDK for deploying and managing remote desktop instances for AI agents. Use this guide to properly interact with the SDK.
**CORE SDK USAGE:**
- Initialize client: from scrapybara import Scrapybara; client = Scrapybara(api_key="KEY")
- Instance lifecycle:
instance = client.start_ubuntu(timeout_hours=1)
instance.pause() # Pause to save resources
instance.resume(timeout_hours=1) # Resume work
instance.stop() # Terminate and clean up
- Instance types:
ubuntu_instance = client.start_ubuntu(): supports bash, computer, edit, browser
browser_instance = client.start_browser(): supports computer, browser
windows_instance = client.start_windows(): supports computer
**TYPE IMPORTS:**
- Core types:
from scrapybara import Scrapybara
- Instance types:
from scrapybara.client import UbuntuInstance, BrowserInstance, WindowsInstance
- Tool types:
from scrapybara.tools import Tool, BashTool, ComputerTool, EditTool
- Model types:
from scrapybara.anthropic import Anthropic
- Message types:
from pydantic import BaseModel
from typing import List, Union, Optional, Any
- Error types:
from scrapybara.core.api_error import ApiError
**CORE INSTANCE OPERATIONS:**
- Screenshots: instance.screenshot().base_64_image
- Bash commands: instance.bash(command="ls -la")
- Mouse control: instance.computer(action="move_mouse", coordinates=[x, y])
- Click actions: instance.computer(action="click_mouse", button="right", coordinates=[x, y])
- Drag actions: instance.computer(action="drag_mouse", path=[[x1, y1], [x2, y2]])
- Scroll actions: instance.computer(action="scroll", coordinates=[x, y], delta_x=0, delta_y=0)
- Key actions: instance.computer(action="press_key", keys=[keys])
- Type actions: instance.computer(action="type_text", text="Hello world")
- Wait actions: instance.computer(action="wait", duration=3)
- Get cursor position: instance.computer(action="get_cursor_position").output
- File operations: instance.file.read(path="/path/file"), instance.file.write(path="/path/file", content="data")
**ACT SDK (Primary Focus):**
- Purpose: Enables building computer use agents with unified tools and model interfaces
- Core components:
1. Model: Handles LLM integration (currently Anthropic)
from scrapybara.anthropic import Anthropic
model = Anthropic() # Or model = Anthropic(api_key="KEY") for own key
2. Tools: Interface for computer interactions
- BashTool: Run shell commands
- ComputerTool: Mouse/keyboard control
- EditTool: File operations
tools = [
BashTool(instance),
ComputerTool(instance),
EditTool(instance),
]
3. Prompt:
- system: system prompt, recommend to use UBUNTU_SYSTEM_PROMPT, BROWSER_SYSTEM_PROMPT, WINDOWS_SYSTEM_PROMPT
- prompt: simple user prompt
- messages: list of messages
- Only include either prompt or messages, not both
response = client.act(
model=Anthropic(),
tools=tools,
system=UBUNTU_SYSTEM_PROMPT,
prompt="Task",
on_step=handle_step
)
messages = response.messages
steps = response.steps
text = response.text
output = response.output
usage = response.usage
**MESSAGE HANDLING:**
- Response Structure: Messages are structured with roles (user/assistant/tool) and typed content
- Content Types:
- TextPart: Simple text content
TextPart(type="text", text="content")
- ImagePart: Base64 or URL images
ImagePart(type="image", image="base64...", mime_type="image/png")
- ReasoningPart: Model reasoning content
ReasoningPart(
type="reasoning",
id="id",
reasoning="reasoning",
signature="signature",
instructions="instructions"
)
- ToolCallPart: Tool invocations
ToolCallPart(
type="tool-call",
tool_call_id="id",
tool_name="bash",
args={"command": "ls"}
)
- ToolResultPart: Tool execution results
ToolResultPart(
type="tool-result",
tool_call_id="id",
tool_name="bash",
result="output",
is_error=False
)
**STEP HANDLING:**
def handle_step(step: Step):
if step.reasoning_parts:
print(f"Reasoning: {step.reasoning_parts}")
if step.text:
print(f"Text: {step.text}")
if step.tool_calls:
for call in step.tool_calls:
print(f"Tool: {call.tool_name}")
if step.tool_results:
for result in step.tool_results:
print(f"Result: {result.result}")
print(f"Tokens: {step.usage.total_tokens if step.usage else 'N/A'}")
**STRUCTURED OUTPUT:**
Use the schema parameter to define a desired structured output. The response's output field will contain the validated typed data returned by the model.
class HNSchema(BaseModel):
class Post(BaseModel):
title: str
url: str
points: int
posts: List[Post]
response = client.act(
model=Anthropic(),
tools=tools,
schema=HNSchema,
system=SYSTEM_PROMPT,
prompt="Get the top 10 posts on Hacker News",
)
posts = response.output.posts
**TOKEN USAGE:**
- Track token usage through TokenUsage objects
- Fields: prompt_tokens, completion_tokens, total_tokens
- Available in both Step and ActResponse objects
**EXAMPLE:**
from scrapybara import Scrapybara
from scrapybara.anthropic import Anthropic
from scrapybara.prompts import UBUNTU_SYSTEM_PROMPT
from scrapybara.tools import BashTool, ComputerTool, EditTool
client = Scrapybara()
instance = client.start_ubuntu()
instance.browser.start()
response = client.act(
model=Anthropic(),
tools=[
BashTool(instance),
ComputerTool(instance),
EditTool(instance),
],
system=UBUNTU_SYSTEM_PROMPT,
prompt="Go to the YC website and fetch the HTML",
on_step=lambda step: print(f"{step}\n"),
)
messages = response.messages
steps = response.steps
text = response.text
output = response.output
usage = response.usage
instance.browser.stop()
instance.stop()
**EXECUTION PATTERNS:**
1. Basic agent execution:
response = client.act(
model=Anthropic(),
tools=tools,
system="System context here",
prompt="Task description"
)
2. Browser automation:
cdp_url = instance.browser.start().cdp_url
auth_state_id = instance.browser.save_auth(name="default").auth_state_id # Save auth
instance.browser.authenticate(auth_state_id=auth_state_id) # Reuse auth
3. File management:
instance.file.write("/tmp/data.txt", "content")
content = instance.file.read("/tmp/data.txt").content
4. Environment variables:
instance.env.set({"API_KEY": "value"})
instance.env.get().variables
instance.env.delete(["VAR_NAME"])
**ERROR HANDLING:**
from scrapybara.core.api_error import ApiError
try:
client.start_ubuntu()
except ApiError as e:
print(f"Error {e.status_code}: {e.body}")
**IMPORTANT GUIDELINES:**
- Always stop instances after use to prevent unnecessary billing
- Use async client (AsyncScrapybara) for non-blocking operations
- Handle API errors with try/except ApiError blocks
- Default timeout is 60s; customize with timeout parameter or request_options
- Instance auto-terminates after 1 hour by default
- For browser operations, always start browser before BrowserTool usage
- Prefer bash commands over GUI interactions for launching applications
```
```md .cursorrules
You are working with Scrapybara, a TypeScript SDK for deploying and managing remote desktop instances for AI agents. Use this guide to properly interact with the SDK.
**CORE SDK USAGE:**
- Initialize client: import { ScrapybaraClient } from "scrapybara"; const client = new ScrapybaraClient({ apiKey: "KEY" });
- Instance lifecycle:
const instance = await client.startUbuntu({ timeoutHours: 1 });
await instance.pause(); // Pause to save resources
await instance.resume({ timeoutHours: 1 }); // Resume work
await instance.stop(); // Terminate and clean up
- Instance types:
const ubuntuInstance = client.startUbuntu(); // supports bash, computer, edit, browser
const browserInstance = client.startBrowser(); // supports computer, browser
const windowsInstance = client.startWindows(); // supports computer
**TYPE IMPORTS:**
- Core types:
import { ScrapybaraClient, UbuntuInstance, BrowserInstance, WindowsInstance } from "scrapybara";
- Tool types:
import { bashTool, computerTool, editTool } from "scrapybara/tools";
- Model types:
import { anthropic } from "scrapybara/anthropic";
- Message types:
import { z } from "zod";
- Error types:
import { ScrapybaraError } from "scrapybara";
- Request/Response types:
import { Scrapybara } from "scrapybara"; // Namespace containing all request/response types
**CORE INSTANCE OPERATIONS:**
- Screenshots: const base64Image = await instance.screenshot().base64Image;
- Bash commands: await instance.bash({ command: "ls -la" });
- Mouse control: await instance.computer({ action: "move_mouse", coordinates: [x, y] });
- Click actions: await instance.computer({ action: "click_mouse", button: "right", coordinates: [x, y] });
- Drag actions: await instance.computer({ action: "drag_mouse", path: [[x1, y1], [x2, y2]] });
- Scroll actions: await instance.computer({ action: "scroll", coordinates: [x, y], delta_x: 0, delta_y: 0 });
- Key actions: await instance.computer({ action: "press_key", keys: ["a", "b", "c"] });
- Type actions: await instance.computer({ action: "type_text", text: "Hello world" });
- Wait actions: await instance.computer({ action: "wait", duration: 3 });
- Get cursor position: await instance.computer({ action: "get_cursor_position" });
- File operations: await instance.file.read({ path: "/path/file" }), await instance.file.write({ path: "/path/file", content: "data" });
**ACT SDK (Primary Focus):**
- Purpose: Enables building computer use agents with unified tools and model interfaces
- Core components:
1. Model: Handles LLM integration (currently Anthropic)
import { anthropic } from "scrapybara/anthropic";
const model = anthropic(); // Or model = anthropic({ apiKey: "KEY" }) for own key
2. Tools: Interface for computer interactions
- bashTool: Run shell commands
- computerTool: Mouse/keyboard control
- editTool: File operations
const tools = [
bashTool(instance),
computerTool(instance),
editTool(instance),
];
3. Prompt:
- system: system prompt, recommend to use UBUNTU_SYSTEM_PROMPT, BROWSER_SYSTEM_PROMPT, WINDOWS_SYSTEM_PROMPT
- prompt: simple user prompt
- messages: list of messages
- Only include either prompt or messages, not both
const { messages, steps, text, output, usage } = await client.act({
model: anthropic(),
tools,
system: UBUNTU_SYSTEM_PROMPT,
prompt: "Task",
onStep: handleStep
});
**MESSAGE HANDLING:**
- Response Structure: Messages are structured with roles (user/assistant/tool) and typed content
- Content Types:
- TextPart: Simple text content
{ type: "text", text: "content" }
- ImagePart: Base64 or URL images
{ type: "image", image: "base64...", mimeType: "image/png" }
- ReasoningPart: Model reasoning content
{
type: "reasoning",
id: "id",
reasoning: "reasoning",
signature: "signature",
instructions: "instructions"
}
- ToolCallPart: Tool invocations
{
type: "tool-call",
toolCallId: "id",
toolName: "bash",
args: { command: "ls" }
}
- ToolResultPart: Tool execution results
{
type: "tool-result",
toolCallId: "id",
toolName: "bash",
result: "output",
isError: false
}
**STEP HANDLING:**
const handleStep = (step: Step) => {
console.log(`Text: ${step.text}`);
if (step.toolCalls) {
for (const call of step.toolCalls) {
console.log(`Tool: ${call.toolName}`);
}
}
if (step.toolResults) {
for (const result of step.toolResults) {
console.log(`Result: ${result.result}`);
}
}
console.log(`Tokens: ${step.usage?.totalTokens ?? 'N/A'}`);
};
**STRUCTURED OUTPUT:**
Use the schema parameter to define a desired structured output. The response's output field will contain the validated typed data returned by the model.
const schema = z.object({
posts: z.array(z.object({
title: z.string(),
url: z.string(),
points: z.number(),
})),
});
const { output } = await client.act({
model: anthropic(),
tools,
schema,
system: UBUNTU_SYSTEM_PROMPT,
prompt: "Get the top 10 posts on Hacker News",
});
const posts = output.posts;
**TOKEN USAGE:**
- Track token usage through TokenUsage objects
- Fields: promptTokens, completionTokens, totalTokens
- Available in both Step and ActResponse objects
**EXAMPLE:**
import { ScrapybaraClient } from "scrapybara";
import { anthropic } from "scrapybara/anthropic";
import { UBUNTU_SYSTEM_PROMPT } from "scrapybara/prompts";
import { bashTool, computerTool, editTool } from "scrapybara/tools";
const client = new ScrapybaraClient();
const instance = await client.startUbuntu();
await instance.browser.start();
const { messages, steps, text, output, usage } = await client.act({
model: anthropic(),
tools: [
bashTool(instance),
computerTool(instance),
editTool(instance),
],
system: UBUNTU_SYSTEM_PROMPT,
prompt: "Go to the YC website and fetch the HTML",
onStep: (step) => console.log(`${step}\n`),
});
await instance.browser.stop();
await instance.stop();
**EXECUTION PATTERNS:**
1. Basic agent execution:
const { messages, steps, text, output, usage } = await client.act({
model: anthropic(),
tools,
system: "System context here",
prompt: "Task description"
});
2. Browser automation:
const cdpUrl = await instance.browser.start().cdpUrl;
const authStateId = await instance.browser.saveAuth({ name: "default" }).authStateId; // Save auth
await instance.browser.authenticate({ authStateId }); // Reuse auth
3. File management:
await instance.file.write({ path: "/tmp/data.txt", content: "content" });
const content = await instance.file.read({ path: "/tmp/data.txt" }).content;
4. Environment variables:
await instance.env.set({ API_KEY: "value" });
const vars = await instance.env.get().variables;
await instance.env.delete(["VAR_NAME"]);
**ERROR HANDLING:**
import { ApiError } from "scrapybara/core";
try {
await client.startUbuntu();
} catch (e) {
if (e instanceof ApiError) {
console.error(`Error ${e.statusCode}: ${e.body}`);
}
}
**IMPORTANT GUIDELINES:**
- Always stop instances after use to prevent unnecessary billing
- Use async/await for all operations as they are asynchronous
- Handle API errors with try/catch blocks
- Default timeout is 60s; customize with timeout parameter or requestOptions
- Instance auto-terminates after 1 hour by default
- For browser operations, always start browser before browserTool usage
- Prefer bash commands over GUI interactions for launching applications
```
## llms-full.txt
Need more context? Check out [llms-full.txt](/llms-full.txt).
# Start instance
```http
POST https://api.scrapybara.com/v1/start
Content-Type: application/json
```
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/start \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{}'
```
```shell
curl -X POST https://api.scrapybara.com/v1/start \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{}'
```
# Get instance by ID
```http
GET https://api.scrapybara.com/v1/instance/{instance_id}
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl https://api.scrapybara.com/v1/instance/instance_id \
-H "x-api-key: "
```
```shell
curl https://api.scrapybara.com/v1/instance/:instance_id \
-H "x-api-key: "
```
# List all instances
```http
GET https://api.scrapybara.com/v1/instances
```
## Response Body
- 200: Successful Response
## Examples
```shell
curl https://api.scrapybara.com/v1/instances \
-H "x-api-key: "
```
# List all browser authentication states
```http
GET https://api.scrapybara.com/v1/auth_states
```
## Response Body
- 200: Successful Response
## Examples
```shell
curl https://api.scrapybara.com/v1/auth_states \
-H "x-api-key: "
```
# Take screenshot
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/screenshot
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/screenshot \
-H "x-api-key: "
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/screenshot \
-H "x-api-key: "
```
# Get stream URL
```http
GET https://api.scrapybara.com/v1/instance/{instance_id}/stream_url
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl https://api.scrapybara.com/v1/instance/instance_id/stream_url \
-H "x-api-key: "
```
```shell
curl https://api.scrapybara.com/v1/instance/:instance_id/stream_url \
-H "x-api-key: "
```
# Run computer actions
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/computer
Content-Type: application/json
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/computer \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{
"action": "move_mouse",
"coordinates": [
1
]
}'
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/computer \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{
"action": "move_mouse",
"coordinates": [
0
]
}'
```
# Run bash actions
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/bash
Content-Type: application/json
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/bash \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{}'
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/bash \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{}'
```
# Run edit actions
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/edit
Content-Type: application/json
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/edit \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{
"command": "view",
"path": "path"
}'
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/edit \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{
"command": "view",
"path": "string"
}'
```
# Run file actions
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/file
Content-Type: application/json
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/file \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{
"command": "command"
}'
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/file \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{
"command": "string"
}'
```
# Upload a file to the instance
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/upload
Content-Type: multipart/form-data
```
Upload a file to the instance.
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/upload \
-H "x-api-key: " \
-H "Content-Type: multipart/form-data" \
-F file=@ \
-F path="path"
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/upload \
-H "x-api-key: " \
-H "Content-Type: multipart/form-data" \
-F file=@ \
-F path="string"
```
# Stop instance
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/stop
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/stop \
-H "x-api-key: "
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/stop \
-H "x-api-key: "
```
# Pause instance
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/pause
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/pause \
-H "x-api-key: "
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/pause \
-H "x-api-key: "
```
# Resume instance
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/resume
```
## Path Parameters
- instance_id (required)
## Query Parameters
- timeout_hours (optional)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/resume \
-H "x-api-key: "
```
```shell
curl -X POST "https://api.scrapybara.com/v1/instance/:instance_id/resume?timeout_hours=1" \
-H "x-api-key: "
```
# Start browser
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/browser/start
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/browser/start \
-H "x-api-key: "
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/browser/start \
-H "x-api-key: "
```
# Get CDP URL
```http
GET https://api.scrapybara.com/v1/instance/{instance_id}/browser/cdp_url
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl https://api.scrapybara.com/v1/instance/instance_id/browser/cdp_url \
-H "x-api-key: "
```
```shell
curl https://api.scrapybara.com/v1/instance/:instance_id/browser/cdp_url \
-H "x-api-key: "
```
# Get current URL
```http
GET https://api.scrapybara.com/v1/instance/{instance_id}/browser/current_url
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl https://api.scrapybara.com/v1/instance/instance_id/browser/current_url \
-H "x-api-key: "
```
```shell
curl https://api.scrapybara.com/v1/instance/:instance_id/browser/current_url \
-H "x-api-key: "
```
# Save browser authentication state
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/browser/save_auth
```
## Path Parameters
- instance_id (required)
## Query Parameters
- name (optional)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/browser/save_auth \
-H "x-api-key: "
```
```shell
curl -X POST "https://api.scrapybara.com/v1/instance/:instance_id/browser/save_auth?name=string" \
-H "x-api-key: "
```
# Modify browser authentication state
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/browser/modify_auth
```
## Path Parameters
- instance_id (required)
## Query Parameters
- auth_state_id (required)
- name (optional)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST "https://api.scrapybara.com/v1/instance/instance_id/browser/modify_auth?auth_state_id=auth_state_id" \
-H "x-api-key: "
```
```shell
curl -X POST "https://api.scrapybara.com/v1/instance/:instance_id/browser/modify_auth?auth_state_id=string&name=string" \
-H "x-api-key: "
```
# Authenticate browser using saved state
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/browser/authenticate
```
## Path Parameters
- instance_id (required)
## Query Parameters
- auth_state_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST "https://api.scrapybara.com/v1/instance/instance_id/browser/authenticate?auth_state_id=auth_state_id" \
-H "x-api-key: "
```
```shell
curl -X POST "https://api.scrapybara.com/v1/instance/:instance_id/browser/authenticate?auth_state_id=string" \
-H "x-api-key: "
```
# Stop browser
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/browser/stop
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/browser/stop \
-H "x-api-key: "
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/browser/stop \
-H "x-api-key: "
```
# Execute code without notebook
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/code/execute
Content-Type: application/json
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/code/execute \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{
"code": "code"
}'
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/code/execute \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{
"code": "string"
}'
```
# List available notebook kernels
```http
GET https://api.scrapybara.com/v1/instance/{instance_id}/notebook/kernels
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl https://api.scrapybara.com/v1/instance/instance_id/notebook/kernels \
-H "x-api-key: "
```
```shell
curl https://api.scrapybara.com/v1/instance/:instance_id/notebook/kernels \
-H "x-api-key: "
```
# Create new notebook
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/notebook/create
Content-Type: application/json
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/notebook/create \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{
"name": "name"
}'
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/notebook/create \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{
"name": "string"
}'
```
# Get notebook by ID
```http
GET https://api.scrapybara.com/v1/instance/{instance_id}/notebook/{notebook_id}
```
## Path Parameters
- instance_id (required)
- notebook_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl https://api.scrapybara.com/v1/instance/instance_id/notebook/notebook_id \
-H "x-api-key: "
```
```shell
curl https://api.scrapybara.com/v1/instance/:instance_id/notebook/:notebook_id \
-H "x-api-key: "
```
# Delete notebook
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/notebook/{notebook_id}/delete
```
## Path Parameters
- instance_id (required)
- notebook_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/notebook/notebook_id/delete \
-H "x-api-key: "
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/notebook/:notebook_id/delete \
-H "x-api-key: "
```
# Add cell to notebook
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/notebook/{notebook_id}/cell
Content-Type: application/json
```
## Path Parameters
- instance_id (required)
- notebook_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/notebook/notebook_id/cell \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{
"type": "code",
"content": "content"
}'
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/notebook/:notebook_id/cell \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{
"type": "code",
"content": "string"
}'
```
# Execute notebook cell
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/notebook/{notebook_id}/cell/{cell_id}/execute
Content-Type: application/json
```
## Path Parameters
- instance_id (required)
- notebook_id (required)
- cell_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/notebook/notebook_id/cell/cell_id/execute \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{}'
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/notebook/:notebook_id/cell/:cell_id/execute \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{}'
```
# Execute all cells in notebook
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/notebook/{notebook_id}/execute
Content-Type: application/json
```
## Path Parameters
- instance_id (required)
- notebook_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/notebook/notebook_id/execute \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{}'
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/notebook/:notebook_id/execute \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{}'
```
# Get environment variables
```http
GET https://api.scrapybara.com/v1/instance/{instance_id}/env
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl https://api.scrapybara.com/v1/instance/instance_id/env \
-H "x-api-key: "
```
```shell
curl https://api.scrapybara.com/v1/instance/:instance_id/env \
-H "x-api-key: "
```
# Set environment variables
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/env
Content-Type: application/json
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/env \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{
"variables": {
"key": "value"
}
}'
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/env \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{
"variables": {
"string": "string"
}
}'
```
# Delete environment variables
```http
POST https://api.scrapybara.com/v1/instance/{instance_id}/env/delete
Content-Type: application/json
```
## Path Parameters
- instance_id (required)
## Response Body
- 200: Successful Response
- 422: Validation Error
## Examples
```shell
curl -X POST https://api.scrapybara.com/v1/instance/instance_id/env/delete \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{
"keys": [
"keys"
]
}'
```
```shell
curl -X POST https://api.scrapybara.com/v1/instance/:instance_id/env/delete \
-H "x-api-key: " \
-H "Content-Type: application/json" \
-d '{
"keys": [
"string"
]
}'
```
# Python SDK
[](https://pypi.python.org/pypi/scrapybara)
View the Python SDK source code
View the Scrapybara package on PyPI
The Scrapybara Python library provides convenient access to the Scrapybara API from Python.
## Installation
```sh
pip install scrapybara
```
## Reference
Please only refer to this documentation site for reference. The GitHub reference [here](https://github.com/scrapybara/scrapybara-python/blob/master/reference.md) is generated programmatically and incomplete.
## Usage
Instantiate and use the client with the following:
```python
from scrapybara import Scrapybara
client = Scrapybara(
api_key="YOUR_API_KEY",
)
instance = client.start_ubuntu()
```
## Async Client
The SDK also exports an `async` client so that you can make non-blocking calls to our API.
```python
import asyncio
from scrapybara import AsyncScrapybara
client = AsyncScrapybara(
api_key="YOUR_API_KEY",
)
async def main() -> None:
await client.start_ubuntu()
asyncio.run(main())
```
## Exception Handling
When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error
will be thrown.
```python
from scrapybara.core.api_error import ApiError
try:
client.start_ubuntu(...)
except ApiError as e:
print(e.status_code)
print(e.body)
```
## Advanced
### Retries
The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long
as the request is deemed retriable and the number of retry attempts has not grown larger than the configured
retry limit (default: 2).
A request is deemed retriable when any of the following HTTP status codes is returned:
* [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout)
* [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests)
* [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors)
Use the `max_retries` request option to configure this behavior.
```python
client.start_ubuntu(..., request_options={
"max_retries": 1
})
```
### Timeouts
The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level.
```python
from scrapybara import Scrapybara
client = Scrapybara(
...,
timeout=20.0,
)
# Override timeout for a specific method
client.start_ubuntu(..., request_options={
"timeout_in_seconds": 1
})
```
### Custom Client
You can override the `httpx` client to customize it for your use-case. Some common use-cases include support for proxies
and transports.
```python
import httpx
from scrapybara import Scrapybara
client = Scrapybara(
...,
httpx_client=httpx.Client(
proxies="http://my.test.proxy.example.com",
transport=httpx.HTTPTransport(local_address="0.0.0.0"),
),
)
```
## Contributing
While we value open-source contributions to this SDK, this library is generated programmatically.
Additions made directly to this library would have to be moved over to our generation code,
otherwise they would be overwritten upon the next generated release. Feel free to open a PR as
a proof of concept, but know that we will not be able to merge it as-is. We suggest opening
an issue first to discuss with us!
On the other hand, contributions to the README are always very welcome!
# TypeScript SDK
[](https://www.npmjs.com/package/scrapybara)
View the TypeScript SDK source code
View the Scrapybara package on NPM
The Scrapybara TypeScript library provides convenient access to the Scrapybara API from TypeScript.
## Installation
```sh
npm i -s scrapybara
```
## Reference
Please only refer to this documentation site for reference. The GitHub reference [here](https://github.com/scrapybara/scrapybara-ts/blob/main/reference.md) is generated programmatically and incomplete.
## Usage
Instantiate and use the client with the following:
```typescript
import { ScrapybaraClient } from "scrapybara";
const client = new ScrapybaraClient({ apiKey: "YOUR_API_KEY" });
await client.startUbuntu();
```
## Request And Response Types
The SDK exports all request and response types as TypeScript interfaces. Simply import them with the
following namespace:
```typescript
import { Scrapybara } from "scrapybara";
const request: Scrapybara.ComputerRequest = {
...
};
```
## Exception Handling
When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error
will be thrown.
```typescript
import { ScrapybaraError } from "scrapybara";
try {
await client.startUbuntu(...);
} catch (err) {
if (err instanceof ScrapybaraError) {
console.log(err.statusCode);
console.log(err.message);
console.log(err.body);
}
}
```
## Advanced
### Additional Headers
If you would like to send additional headers as part of the request, use the `headers` request option.
```typescript
const response = await client.startUbuntu(..., {
headers: {
'X-Custom-Header': 'custom value'
}
});
```
### Retries
The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long
as the request is deemed retriable and the number of retry attempts has not grown larger than the configured
retry limit (default: 2).
A request is deemed retriable when any of the following HTTP status codes is returned:
* [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout)
* [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests)
* [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors)
Use the `maxRetries` request option to configure this behavior.
```typescript
const response = await client.startUbuntu(..., {
maxRetries: 0 // override maxRetries at the request level
});
```
### Timeouts
The SDK defaults to a 60 second timeout. Use the `timeoutInSeconds` option to configure this behavior.
```typescript
const response = await client.startUbuntu(..., {
timeoutInSeconds: 30 // override timeout to 30s
});
```
### Aborting Requests
The SDK allows users to abort requests at any point by passing in an abort signal.
```typescript
const controller = new AbortController();
const response = await client.startUbuntu(..., {
abortSignal: controller.signal
});
controller.abort(); // aborts the request
```
### Runtime Compatibility
The SDK defaults to `node-fetch` but will use the global fetch client if present. The SDK works in the following
runtimes:
* Node.js 18+
* Vercel
* Cloudflare Workers
* Deno v1.25+
* Bun 1.0+
* React Native
### Customizing Fetch Client
The SDK provides a way for your to customize the underlying HTTP client / Fetch function. If you're running in an
unsupported environment, this provides a way for you to break glass and ensure the SDK works.
```typescript
import { ScrapybaraClient } from "scrapybara";
const client = new ScrapybaraClient({
...
fetcher: // provide your implementation here
});
```
## Contributing
While we value open-source contributions to this SDK, this library is generated programmatically.
Additions made directly to this library would have to be moved over to our generation code,
otherwise they would be overwritten upon the next generated release. Feel free to open a PR as
a proof of concept, but know that we will not be able to merge it as-is. We suggest opening
an issue first to discuss with us!
On the other hand, contributions to the README are always very welcome!
# Scrapybara Cookbook
> A collection of examples and guides for getting the most out of Scrapybara
## Web research and scraping
} href="/cookbook/copycapy">
Scrape and transform websites into capybara-themed versions with Scrapybara Act SDK and Playwright.
`TypeScript` `Act SDK` `Playwright`
} href="/cookbook/wide-research">
Deep Research but wide. Scrape YC W25 companies and find the best way to contact each company in parallel batches.
`Python` `Act SDK` `Structured Outputs`
## Personal assistant
Full-stack web interface for testing computer use models.
`TypeScript` `Act SDK` `Next.js`
A command line interface for Scrapybara.
`Python` `Act SDK`
## Other
} href="/cookbook/dungeon-crawler">
Computer-using agent that autonomously plays Dungeon Crawl Stone Soup (DCSS) with Scrapybara Act SDK.
`Python` `Act SDK`
# CopyCapy
> Scrape and transform websites into capybara-themed versions with Scrapybara Act SDK and Playwright.
Detailed explanation coming soon!
## Features
* HTML + CSS + image scraping with Playwright
* Agentic capybara-themed code generation
* Single-file output with embedded CSS
* Support for dynamically-loaded content
## Prerequisites
* Node.js 18+
* pnpm
* Scrapybara API key (get one at [scrapybara.com](https://scrapybara.com))
View the source code on GitHub
# Wide Research
> Deep Research but wide. Scrape YC W25 companies and find the best way to contact each company in parallel batches.
Detailed explanation coming soon!
## Features
* Automatically extract YC W25 companies at once
* Parallel batch instance launching and research
* Schema-based data extraction
* Create email drafts with LibreOffice
## Prerequisites
* Python 3.8+
* uv
* Scrapybara API key (get one at [scrapybara.com](https://scrapybara.com))
View the source code on GitHub
# Computer Use Playground
> Full-stack web interface for testing computer use models.
Detailed explanation coming soon!
## Features
* Interactive chat and instance management interface
* Support for OpenAI CUA and Anthropic Claude models
* Ubuntu and browser instance options
* Real-time streaming of responses and instance desktop
* Tool use visualization
## Prerequisites
* Node.js 18+
* npm or pnpm
* Scrapybara API key (get one at [scrapybara.com](https://scrapybara.com))
View the source code on GitHub
# Scrapybara CLI
> A command line interface for Scrapybara.
Detailed explanation coming soon!
## Prerequisites
* Python 3.8+
* uv
* Scrapybara API key (get one at [scrapybara.com](https://scrapybara.com))
View the source code on GitHub
# Dungeon Crawler
> Computer-using agent that autonomously plays Dungeon Crawl Stone Soup (DCSS) with Scrapybara Act SDK. The agent creates characters, explores dungeons, fights monsters, and makes strategic decisions in real-time.
Detailed explanation coming soon!
DCSS is a really good vibe check for computer use agents and an evaluation on our private CapyBench.
## Features
* Dungeon Crawl Stone Soup (DCSS) installation
* Automatic character creation, game start, and gameplay loop
## Prerequisites
* Python 3.8+
* uv
* Scrapybara API key (get one at [scrapybara.com](https://scrapybara.com))
View the source code on GitHub