Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.3.0"
".": "1.0.0"
}
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 1.0.0 (2025-08-20)

Full Changelog: [v0.3.0...v1.0.0](https://github.com/browser-use/browser-use-python/compare/v0.3.0...v1.0.0)

## 0.3.0 (2025-08-20)

Full Changelog: [v0.2.0...v0.3.0](https://github.com/browser-use/browser-use-python/compare/v0.2.0...v0.3.0)
Expand Down
119 changes: 71 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,28 @@
# Browser Use Python API library
<img src="./assets/cloud-banner-python.png" alt="Browser Use Python" width="full"/>

<!-- prettier-ignore -->
[![PyPI version](https://img.shields.io/pypi/v/browser-use-sdk.svg?label=pypi%20(stable))](https://pypi.org/project/browser-use-sdk/)

The Browser Use Python library provides convenient access to the Browser Use REST API from any Python 3.8+
application. The library includes type definitions for all request params and response fields,
and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).

It is generated with [Stainless](https://www.stainless.com/).

## Documentation

The REST API documentation can be found on [docs.browser-use.com](https://docs.browser-use.com/cloud/). The full API of this library can be found in [api.md](api.md).

## Installation
[![PyPI version](<https://img.shields.io/pypi/v/browser-use-sdk.svg?label=pypi%20(stable)>)](https://pypi.org/project/browser-use-sdk/)

```sh
# install from PyPI
pip install browser-use-sdk
```

## Usage
## Quick Start

The full API of this library can be found in [api.md](api.md).
> Get your API Key at [Browser Use Cloud](https://cloud.browser-use.com)!

```python
import os
from browser_use_sdk import BrowserUse

client = BrowserUse(
api_key=os.environ.get("BROWSER_USE_API_KEY"), # This is the default and can be omitted
)
client = BrowserUse()

task = client.tasks.create(
task="Search for the top 10 Hacker News posts and return the title and url.",
run = client.tasks.run(
task="Search for the top 10 Hacker News posts and return the title and url."
)
print(task.id)

run.done_output
```

While you can provide an `api_key` keyword argument,
we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/)
to add `BROWSER_USE_API_KEY="My API Key"` to your `.env` file
so that your API Key is not stored in source control.
> The full API of this library can be found in [api.md](api.md).

## Async usage

Expand All @@ -58,10 +39,10 @@ client = AsyncBrowserUse(


async def main() -> None:
task = await client.tasks.create(
task = await client.tasks.run(
task="Search for the top 10 Hacker News posts and return the title and url.",
)
print(task.id)
print(task.done_output)


asyncio.run(main())
Expand Down Expand Up @@ -93,38 +74,80 @@ async def main() -> None:
api_key="My API Key",
http_client=DefaultAioHttpClient(),
) as client:
task = await client.tasks.create(
task = await client.tasks.run(
task="Search for the top 10 Hacker News posts and return the title and url.",
)
print(task.id)
print(task.done_output)


asyncio.run(main())
```

## Structured Output with Pydantic

Browser Use Python SDK provides first class support for Pydantic models.

```py
class HackerNewsPost(BaseModel):
title: str
url: str

class SearchResult(BaseModel):
posts: List[HackerNewsPost]

async def main() -> None:
result = await client.tasks.run(
task="""
Find top 10 Hacker News articles and return the title and url.
""",
structured_output_json=SearchResult,
)

if structured_result.parsed_output is not None:
print("Top HackerNews Posts:")
for post in structured_result.parsed_output.posts:
print(f" - {post.title} - {post.url}")

asyncio.run(main())
```

## Using types
## Streaming Updates with Async Iterators

Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like:
```py
class HackerNewsPost(BaseModel):
title: str
url: str

- Serializing back into JSON, `model.to_json()`
- Converting to a dictionary, `model.to_dict()`
class SearchResult(BaseModel):
posts: List[HackerNewsPost]

Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`.

## Nested params
async def main() -> None:
task = await client.tasks.create(
task="""
Find top 10 Hacker News articles and return the title and url.
""",
structured_output_json=SearchResult,
)

Nested parameters are dictionaries, typed using `TypedDict`, for example:
async for update in client.tasks.stream(structured_task.id, structured_output_json=SearchResult):
if len(update.steps) > 0:
last_step = update.steps[-1]
print(f"{update.status}: {last_step.url} ({last_step.next_goal})")
else:
print(f"{update.status}")

```python
from browser_use_sdk import BrowserUse
if update.status == "finished":
if update.parsed_output is None:
print("No output...")
else:
print("Top HackerNews Posts:")
for post in update.parsed_output.posts:
print(f" - {post.title} - {post.url}")

client = BrowserUse()
break

task = client.tasks.create(
task="x",
agent_settings={},
)
print(task.agent_settings)
asyncio.run(main())
```

## Handling errors
Expand Down
Binary file added assets/cloud-banner-python.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion examples/async_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ async def create_regular_task() -> None:
res = await client.tasks.create(
task="""
Find top 10 Hacker News articles and return the title and url.
"""
""",
agent_settings={"llm": "gemini-2.5-flash"},
)

print(f"Regular Task ID: {res.id}")
Expand All @@ -35,6 +36,7 @@ class SearchResult(BaseModel):
task="""
Find top 10 Hacker News articles and return the title and url.
""",
agent_settings={"llm": "gpt-4.1"},
structured_output_json=SearchResult,
)

Expand Down
4 changes: 3 additions & 1 deletion examples/async_retrieve.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ async def retrieve_regular_task() -> None:
regular_task = await client.tasks.create(
task="""
Find top 10 Hacker News articles and return the title and url.
"""
""",
agent_settings={"llm": "gemini-2.5-flash"},
)

print(f"Regular Task ID: {regular_task.id}")
Expand Down Expand Up @@ -58,6 +59,7 @@ class SearchResult(BaseModel):
task="""
Find top 10 Hacker News articles and return the title and url.
""",
agent_settings={"llm": "gpt-4.1"},
structured_output_json=SearchResult,
)

Expand Down
4 changes: 3 additions & 1 deletion examples/async_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ async def run_regular_task() -> None:
regular_result = await client.tasks.run(
task="""
Find top 10 Hacker News articles and return the title and url.
"""
""",
agent_settings={"llm": "gemini-2.5-flash"},
)

print(f"Regular Task ID: {regular_result.id}")
Expand All @@ -39,6 +40,7 @@ class SearchResult(BaseModel):
task="""
Find top 10 Hacker News articles and return the title and url.
""",
agent_settings={"llm": "gpt-4.1"},
structured_output_json=SearchResult,
)

Expand Down
1 change: 1 addition & 0 deletions examples/async_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class SearchResult(BaseModel):
task="""
Find top 10 Hacker News articles and return the title and url.
""",
agent_settings={"llm": "gpt-4.1"},
structured_output_json=SearchResult,
)

Expand Down
4 changes: 3 additions & 1 deletion examples/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ def create_regular_task() -> None:
res = client.tasks.create(
task="""
Find top 10 Hacker News articles and return the title and url.
"""
""",
agent_settings={"llm": "gemini-2.5-flash"},
)

print(res.id)
Expand All @@ -37,6 +38,7 @@ class SearchResult(BaseModel):
task="""
Find top 10 Hacker News articles and return the title and url.
""",
agent_settings={"llm": "gpt-4.1"},
structured_output_json=SearchResult,
)

Expand Down
4 changes: 3 additions & 1 deletion examples/retrieve.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ def retrieve_regular_task() -> None:
regular_task = client.tasks.create(
task="""
Find top 10 Hacker News articles and return the title and url.
"""
""",
agent_settings={"llm": "gemini-2.5-flash"},
)

print(f"Task ID: {regular_task.id}")
Expand Down Expand Up @@ -61,6 +62,7 @@ class SearchResult(BaseModel):
task="""
Find top 10 Hacker News articles and return the title and url.
""",
agent_settings={"llm": "gpt-4.1"},
structured_output_json=SearchResult,
)

Expand Down
4 changes: 3 additions & 1 deletion examples/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ def run_regular_task() -> None:
regular_result = client.tasks.run(
task="""
Find top 10 Hacker News articles and return the title and url.
"""
""",
agent_settings={"llm": "gemini-2.5-flash"},
)

print(f"Task ID: {regular_result.id}")
Expand All @@ -41,6 +42,7 @@ class SearchResult(BaseModel):
task="""
Find top 10 Hacker News articles and return the title and url.
""",
agent_settings={"llm": "gpt-4.1"},
structured_output_json=SearchResult,
)

Expand Down
6 changes: 5 additions & 1 deletion examples/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ def stream_regular_task() -> None:
for action in last_step.actions:
print(f" - {action}")

print("Done")
if res.status == "finished":
print(res.done_output)

print("Regular: DONE")


stream_regular_task()
Expand All @@ -50,6 +53,7 @@ class SearchResult(BaseModel):
task="""
Find top 10 Hacker News articles and return the title and url.
""",
agent_settings={"llm": "gpt-4.1"},
structured_output_json=SearchResult,
)

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "browser-use-sdk"
version = "0.3.0"
version = "1.0.0"
description = "The official Python library for the browser-use API"
dynamic = ["readme"]
license = "Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion src/browser_use_sdk/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

__title__ = "browser_use_sdk"
__version__ = "0.3.0" # x-release-please-version
__version__ = "1.0.0" # x-release-please-version
12 changes: 10 additions & 2 deletions src/browser_use_sdk/resources/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -1307,8 +1307,16 @@ async def _watch(

if res.status == "finished":
break

await asyncio.sleep(interval)
if res.status == "paused":
break
if res.status == "stopped":
break
if res.status == "started":
await asyncio.sleep(interval)
else:
raise ValueError(
f"Expected one of 'finished', 'paused', 'stopped', or 'started' but received {res.status!r}"
)

async def update(
self,
Expand Down