diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 6b7b74c..fea3454 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.3.0"
+ ".": "1.0.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 211f6e3..f35ed26 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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)
diff --git a/README.md b/README.md
index 894765b..091fe42 100644
--- a/README.md
+++ b/README.md
@@ -1,47 +1,28 @@
-# Browser Use Python API library
+
-
-[)](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://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
@@ -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())
@@ -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
diff --git a/assets/cloud-banner-python.png b/assets/cloud-banner-python.png
new file mode 100644
index 0000000..77e2aee
Binary files /dev/null and b/assets/cloud-banner-python.png differ
diff --git a/examples/async_create.py b/examples/async_create.py
index 311dd01..9cfac77 100755
--- a/examples/async_create.py
+++ b/examples/async_create.py
@@ -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}")
@@ -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,
)
diff --git a/examples/async_retrieve.py b/examples/async_retrieve.py
index 60e46c0..1868e7e 100755
--- a/examples/async_retrieve.py
+++ b/examples/async_retrieve.py
@@ -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}")
@@ -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,
)
diff --git a/examples/async_run.py b/examples/async_run.py
index 3f12f9d..ecb1d11 100755
--- a/examples/async_run.py
+++ b/examples/async_run.py
@@ -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}")
@@ -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,
)
diff --git a/examples/async_stream.py b/examples/async_stream.py
index 0dc923d..772b781 100755
--- a/examples/async_stream.py
+++ b/examples/async_stream.py
@@ -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,
)
diff --git a/examples/create.py b/examples/create.py
index 35da8f6..b0af2db 100755
--- a/examples/create.py
+++ b/examples/create.py
@@ -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)
@@ -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,
)
diff --git a/examples/retrieve.py b/examples/retrieve.py
index 6569a5f..e2c4e47 100755
--- a/examples/retrieve.py
+++ b/examples/retrieve.py
@@ -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}")
@@ -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,
)
diff --git a/examples/run.py b/examples/run.py
index dfe95ce..14b0c00 100755
--- a/examples/run.py
+++ b/examples/run.py
@@ -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}")
@@ -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,
)
diff --git a/examples/stream.py b/examples/stream.py
index eea579b..e017260 100755
--- a/examples/stream.py
+++ b/examples/stream.py
@@ -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()
@@ -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,
)
diff --git a/pyproject.toml b/pyproject.toml
index 7c9b988..f4106db 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -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"
diff --git a/src/browser_use_sdk/_version.py b/src/browser_use_sdk/_version.py
index 4e9d259..486d1c9 100644
--- a/src/browser_use_sdk/_version.py
+++ b/src/browser_use_sdk/_version.py
@@ -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
diff --git a/src/browser_use_sdk/resources/tasks.py b/src/browser_use_sdk/resources/tasks.py
index 5cf36c8..8e96d22 100644
--- a/src/browser_use_sdk/resources/tasks.py
+++ b/src/browser_use_sdk/resources/tasks.py
@@ -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,