Skip to content

Commit 88a1547

Browse files
authored
Merge pull request #8 from browser-use/release-please--branches--main--changes--next
release: 1.0.2
2 parents c26f419 + a35c419 commit 88a1547

File tree

9 files changed

+475
-142
lines changed

9 files changed

+475
-142
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
run: ./scripts/lint
3737

3838
build:
39-
if: github.repository == 'stainless-sdks/browser-use-python' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork)
39+
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
4040
timeout-minutes: 10
4141
name: build
4242
permissions:
@@ -61,12 +61,14 @@ jobs:
6161
run: rye build
6262

6363
- name: Get GitHub OIDC Token
64+
if: github.repository == 'stainless-sdks/browser-use-python'
6465
id: github-oidc
6566
uses: actions/github-script@v6
6667
with:
6768
script: core.setOutput('github_token', await core.getIDToken());
6869

6970
- name: Upload tarball
71+
if: github.repository == 'stainless-sdks/browser-use-python'
7072
env:
7173
URL: https://pkg.stainless.com/s
7274
AUTH: ${{ steps.github-oidc.outputs.github_token }}

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "1.0.1"
2+
".": "1.0.2"
33
}

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## 1.0.2 (2025-08-22)
4+
5+
Full Changelog: [v1.0.1...v1.0.2](https://github.com/browser-use/browser-use-python/compare/v1.0.1...v1.0.2)
6+
7+
### Chores
8+
9+
* update github action ([655a660](https://github.com/browser-use/browser-use-python/commit/655a6600972aee2cefcf83c73fdfd9e1ae68b852))
10+
311
## 1.0.1 (2025-08-21)
412

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

README.md

Lines changed: 109 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<img src="./assets/cloud-banner-python.png" alt="Browser Use Python" width="full"/>
1+
<img src="https://raw.githubusercontent.com/browser-use/browser-use-python/refs/heads/main/assets/cloud-banner-python.png" alt="Browser Use Python" width="full"/>
22

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

@@ -26,65 +26,6 @@ result.done_output
2626

2727
> The full API of this library can be found in [api.md](api.md).
2828
29-
## Async usage
30-
31-
Simply import `AsyncBrowserUse` instead of `BrowserUse` and use `await` with each API call:
32-
33-
```python
34-
import os
35-
import asyncio
36-
from browser_use_sdk import AsyncBrowserUse
37-
38-
client = AsyncBrowserUse(
39-
api_key=os.environ.get("BROWSER_USE_API_KEY"), # This is the default and can be omitted
40-
)
41-
42-
43-
async def main() -> None:
44-
task = await client.tasks.run(
45-
task="Search for the top 10 Hacker News posts and return the title and url.",
46-
)
47-
print(task.done_output)
48-
49-
50-
asyncio.run(main())
51-
```
52-
53-
Functionality between the synchronous and asynchronous clients is otherwise identical.
54-
55-
### With aiohttp
56-
57-
By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.
58-
59-
You can enable this by installing `aiohttp`:
60-
61-
```sh
62-
# install from PyPI
63-
pip install browser-use-sdk[aiohttp]
64-
```
65-
66-
Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:
67-
68-
```python
69-
import asyncio
70-
from browser_use_sdk import DefaultAioHttpClient
71-
from browser_use_sdk import AsyncBrowserUse
72-
73-
74-
async def main() -> None:
75-
async with AsyncBrowserUse(
76-
api_key="My API Key",
77-
http_client=DefaultAioHttpClient(),
78-
) as client:
79-
task = await client.tasks.run(
80-
task="Search for the top 10 Hacker News posts and return the title and url.",
81-
)
82-
print(task.done_output)
83-
84-
85-
asyncio.run(main())
86-
```
87-
8829
## Structured Output with Pydantic
8930

9031
Browser Use Python SDK provides first class support for Pydantic models.
@@ -115,6 +56,10 @@ asyncio.run(main())
11556

11657
## Streaming Updates with Async Iterators
11758

59+
> When presenting a long running task you might want to show updates as they happen.
60+
61+
Browser Use SDK exposes a `.stream` method that lets you subscribe to a sync or an async generator that automatically polls Browser Use Cloud servers and emits a new event when an update happens (e.g., live url becomes available, agent takes a new step, or agent completes the task).
62+
11863
```py
11964
class HackerNewsPost(BaseModel):
12065
title: str
@@ -132,7 +77,7 @@ async def main() -> None:
13277
structured_output_json=SearchResult,
13378
)
13479

135-
async for update in client.tasks.stream(structured_task.id, structured_output_json=SearchResult):
80+
async for update in client.tasks.stream(task.id, structured_output_json=SearchResult):
13681
if len(update.steps) > 0:
13782
last_step = update.steps[-1]
13883
print(f"{update.status}: {last_step.url} ({last_step.next_goal})")
@@ -152,6 +97,109 @@ async def main() -> None:
15297
asyncio.run(main())
15398
```
15499

100+
## Verifying Webhook Events
101+
102+
> You can configure Browser Use Cloud to emit Webhook events and process them easily with Browser Use Python SDK.
103+
104+
Browser Use SDK lets you easily verify the signature and structure of the payload you receive in the webhook.
105+
106+
```py
107+
import uvicorn
108+
import os
109+
from browser_use_sdk.lib.webhooks import Webhook, verify_webhook_event_signature
110+
111+
from fastapi import FastAPI, Request, HTTPException
112+
113+
app = FastAPI()
114+
115+
SECRET_KEY = os.environ['SECRET_KEY']
116+
117+
@app.post('/webhook')
118+
async def webhook(request: Request):
119+
body = await request.json()
120+
121+
timestamp = request.headers.get('X-Browser-Use-Timestamp')
122+
signature = request.headers.get('X-Browser-Use-Signature')
123+
124+
verified_webhook: Webhook = verify_webhook_event_signature(
125+
body=body,
126+
timestamp=timestamp,
127+
secret=SECRET_KEY,
128+
expected_signature=signature,
129+
)
130+
131+
if verified_webhook is not None:
132+
print('Webhook received:', verified_webhook)
133+
else:
134+
print('Invalid webhook received')
135+
136+
return {'status': 'success', 'message': 'Webhook received'}
137+
138+
if __name__ == '__main__':
139+
uvicorn.run(app, host='0.0.0.0', port=8080)
140+
```
141+
142+
## Async usage
143+
144+
Simply import `AsyncBrowserUse` instead of `BrowserUse` and use `await` with each API call:
145+
146+
```python
147+
import os
148+
import asyncio
149+
from browser_use_sdk import AsyncBrowserUse
150+
151+
client = AsyncBrowserUse(
152+
api_key=os.environ.get("BROWSER_USE_API_KEY"), # This is the default and can be omitted
153+
)
154+
155+
156+
async def main() -> None:
157+
task = await client.tasks.run(
158+
task="Search for the top 10 Hacker News posts and return the title and url.",
159+
)
160+
print(task.done_output)
161+
162+
163+
asyncio.run(main())
164+
```
165+
166+
Functionality between the synchronous and asynchronous clients is otherwise identical.
167+
168+
### With aiohttp
169+
170+
By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.
171+
172+
You can enable this by installing `aiohttp`:
173+
174+
```sh
175+
# install from PyPI
176+
pip install browser-use-sdk[aiohttp]
177+
```
178+
179+
Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:
180+
181+
```python
182+
import asyncio
183+
from browser_use_sdk import DefaultAioHttpClient
184+
from browser_use_sdk import AsyncBrowserUse
185+
186+
187+
async def main() -> None:
188+
async with AsyncBrowserUse(
189+
api_key="My API Key",
190+
http_client=DefaultAioHttpClient(),
191+
) as client:
192+
task = await client.tasks.run(
193+
task="Search for the top 10 Hacker News posts and return the title and url.",
194+
)
195+
print(task.done_output)
196+
197+
198+
asyncio.run(main())
199+
```
200+
201+
## Advanced
202+
155203
## Handling errors
156204

157205
When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `browser_use_sdk.APIConnectionError` is raised.
@@ -247,8 +295,6 @@ On timeout, an `APITimeoutError` is thrown.
247295

248296
Note that requests that time out are [retried twice by default](#retries).
249297

250-
## Advanced
251-
252298
### Logging
253299

254300
We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module.
@@ -294,58 +340,6 @@ These methods return an [`APIResponse`](https://github.com/browser-use/browser-u
294340

295341
The async client returns an [`AsyncAPIResponse`](https://github.com/browser-use/browser-use-python/tree/main/src/browser_use_sdk/_response.py) with the same structure, the only difference being `await`able methods for reading the response content.
296342

297-
#### `.with_streaming_response`
298-
299-
The above interface eagerly reads the full response body when you make the request, which may not always be what you want.
300-
301-
To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods.
302-
303-
```python
304-
with client.tasks.with_streaming_response.create(
305-
task="Search for the top 10 Hacker News posts and return the title and url.",
306-
) as response:
307-
print(response.headers.get("X-My-Header"))
308-
309-
for line in response.iter_lines():
310-
print(line)
311-
```
312-
313-
The context manager is required so that the response will reliably be closed.
314-
315-
### Making custom/undocumented requests
316-
317-
This library is typed for convenient access to the documented API.
318-
319-
If you need to access undocumented endpoints, params, or response properties, the library can still be used.
320-
321-
#### Undocumented endpoints
322-
323-
To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other
324-
http verbs. Options on the client will be respected (such as retries) when making this request.
325-
326-
```py
327-
import httpx
328-
329-
response = client.post(
330-
"/foo",
331-
cast_to=httpx.Response,
332-
body={"my_param": True},
333-
)
334-
335-
print(response.headers.get("x-foo"))
336-
```
337-
338-
#### Undocumented request params
339-
340-
If you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request
341-
options.
342-
343-
#### Undocumented response properties
344-
345-
To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You
346-
can also get all the extra fields on the Pydantic model as a dict with
347-
[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra).
348-
349343
### Configuring the HTTP client
350344

351345
You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including:
@@ -388,29 +382,6 @@ with BrowserUse() as client:
388382
# HTTP client is now closed
389383
```
390384

391-
## Versioning
392-
393-
This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
394-
395-
1. Changes that only affect static types, without breaking runtime behavior.
396-
2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_
397-
3. Changes that we do not expect to impact the vast majority of users in practice.
398-
399-
We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
400-
401-
We are keen for your feedback; please open an [issue](https://www.github.com/browser-use/browser-use-python/issues) with questions, bugs, or suggestions.
402-
403-
### Determining the installed version
404-
405-
If you've upgraded to the latest version but aren't seeing any new features you were expecting then your python environment is likely still using an older version.
406-
407-
You can determine the version that is being used at runtime with:
408-
409-
```py
410-
import browser_use_sdk
411-
print(browser_use_sdk.__version__)
412-
```
413-
414385
## Requirements
415386

416387
Python 3.8 or higher.

0 commit comments

Comments
 (0)