diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 7deae33..cce9d1c 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "1.6.0"
+ ".": "1.7.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 3aabff9..28a1de1 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 20
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fbrowserbase-b20f9fea14d79990ab1af3d276f931e026cd955ac623ec6ace80b2af90de170f.yml
-openapi_spec_hash: 943ff4b3297014503fdc9854544cb9a4
-config_hash: 55c54fdafc9e80be584829b5724b00ab
+configured_endpoints: 21
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fbrowserbase-9b1e2a2abf39dd780601935a9a9ee04cb939e2c3ba76627535f625b6aeaf5eb7.yml
+openapi_spec_hash: 12fe5f4306c43fdfb394a33f79391a82
+config_hash: cf04ecfb8dad5fbd8b85be25d6e9ec55
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a482d7b..d6cc7bb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
# Changelog
+## 1.7.0 (2026-03-16)
+
+Full Changelog: [v1.6.0...v1.7.0](https://github.com/browserbase/sdk-python/compare/v1.6.0...v1.7.0)
+
+### Features
+
+* [CORE-1796][apps/api] Update Node.js SDK ([ecafbb5](https://github.com/browserbase/sdk-python/commit/ecafbb511dbe9be78e84f4e1d34e1feefa1fce8e))
+* [GRO-000] docs: Add guide + docs for search api ([55e2d0d](https://github.com/browserbase/sdk-python/commit/55e2d0db2f31b2ca390eaa6855f22de1f1688a88))
+
## 1.6.0 (2026-03-11)
Full Changelog: [v1.5.0...v1.6.0](https://github.com/browserbase/sdk-python/compare/v1.5.0...v1.6.0)
diff --git a/api.md b/api.md
index ac45583..b6066cb 100644
--- a/api.md
+++ b/api.md
@@ -53,6 +53,18 @@ Methods:
- client.projects.list() -> ProjectListResponse
- client.projects.usage(id) -> ProjectUsage
+# Search
+
+Types:
+
+```python
+from browserbase.types import SearchWebResponse
+```
+
+Methods:
+
+- client.search.web(\*\*params) -> SearchWebResponse
+
# Sessions
Types:
diff --git a/pyproject.toml b/pyproject.toml
index 425bcc4..ad5ff02 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "browserbase"
-version = "1.6.0"
+version = "1.7.0"
description = "The official Python library for the Browserbase API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/browserbase/_client.py b/src/browserbase/_client.py
index 70a7b71..d642273 100644
--- a/src/browserbase/_client.py
+++ b/src/browserbase/_client.py
@@ -31,7 +31,8 @@
)
if TYPE_CHECKING:
- from .resources import contexts, projects, sessions, fetch_api, extensions
+ from .resources import search, contexts, projects, sessions, fetch_api, extensions
+ from .resources.search import SearchResource, AsyncSearchResource
from .resources.contexts import ContextsResource, AsyncContextsResource
from .resources.projects import ProjectsResource, AsyncProjectsResource
from .resources.fetch_api import FetchAPIResource, AsyncFetchAPIResource
@@ -129,6 +130,12 @@ def projects(self) -> ProjectsResource:
return ProjectsResource(self)
+ @cached_property
+ def search(self) -> SearchResource:
+ from .resources.search import SearchResource
+
+ return SearchResource(self)
+
@cached_property
def sessions(self) -> SessionsResource:
from .resources.sessions import SessionsResource
@@ -327,6 +334,12 @@ def projects(self) -> AsyncProjectsResource:
return AsyncProjectsResource(self)
+ @cached_property
+ def search(self) -> AsyncSearchResource:
+ from .resources.search import AsyncSearchResource
+
+ return AsyncSearchResource(self)
+
@cached_property
def sessions(self) -> AsyncSessionsResource:
from .resources.sessions import AsyncSessionsResource
@@ -476,6 +489,12 @@ def projects(self) -> projects.ProjectsResourceWithRawResponse:
return ProjectsResourceWithRawResponse(self._client.projects)
+ @cached_property
+ def search(self) -> search.SearchResourceWithRawResponse:
+ from .resources.search import SearchResourceWithRawResponse
+
+ return SearchResourceWithRawResponse(self._client.search)
+
@cached_property
def sessions(self) -> sessions.SessionsResourceWithRawResponse:
from .resources.sessions import SessionsResourceWithRawResponse
@@ -513,6 +532,12 @@ def projects(self) -> projects.AsyncProjectsResourceWithRawResponse:
return AsyncProjectsResourceWithRawResponse(self._client.projects)
+ @cached_property
+ def search(self) -> search.AsyncSearchResourceWithRawResponse:
+ from .resources.search import AsyncSearchResourceWithRawResponse
+
+ return AsyncSearchResourceWithRawResponse(self._client.search)
+
@cached_property
def sessions(self) -> sessions.AsyncSessionsResourceWithRawResponse:
from .resources.sessions import AsyncSessionsResourceWithRawResponse
@@ -550,6 +575,12 @@ def projects(self) -> projects.ProjectsResourceWithStreamingResponse:
return ProjectsResourceWithStreamingResponse(self._client.projects)
+ @cached_property
+ def search(self) -> search.SearchResourceWithStreamingResponse:
+ from .resources.search import SearchResourceWithStreamingResponse
+
+ return SearchResourceWithStreamingResponse(self._client.search)
+
@cached_property
def sessions(self) -> sessions.SessionsResourceWithStreamingResponse:
from .resources.sessions import SessionsResourceWithStreamingResponse
@@ -587,6 +618,12 @@ def projects(self) -> projects.AsyncProjectsResourceWithStreamingResponse:
return AsyncProjectsResourceWithStreamingResponse(self._client.projects)
+ @cached_property
+ def search(self) -> search.AsyncSearchResourceWithStreamingResponse:
+ from .resources.search import AsyncSearchResourceWithStreamingResponse
+
+ return AsyncSearchResourceWithStreamingResponse(self._client.search)
+
@cached_property
def sessions(self) -> sessions.AsyncSessionsResourceWithStreamingResponse:
from .resources.sessions import AsyncSessionsResourceWithStreamingResponse
diff --git a/src/browserbase/_version.py b/src/browserbase/_version.py
index a7f6a7d..1b8456b 100644
--- a/src/browserbase/_version.py
+++ b/src/browserbase/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "browserbase"
-__version__ = "1.6.0" # x-release-please-version
+__version__ = "1.7.0" # x-release-please-version
diff --git a/src/browserbase/resources/__init__.py b/src/browserbase/resources/__init__.py
index f5a2bf0..83a8788 100644
--- a/src/browserbase/resources/__init__.py
+++ b/src/browserbase/resources/__init__.py
@@ -1,5 +1,13 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+from .search import (
+ SearchResource,
+ AsyncSearchResource,
+ SearchResourceWithRawResponse,
+ AsyncSearchResourceWithRawResponse,
+ SearchResourceWithStreamingResponse,
+ AsyncSearchResourceWithStreamingResponse,
+)
from .contexts import (
ContextsResource,
AsyncContextsResource,
@@ -66,6 +74,12 @@
"AsyncProjectsResourceWithRawResponse",
"ProjectsResourceWithStreamingResponse",
"AsyncProjectsResourceWithStreamingResponse",
+ "SearchResource",
+ "AsyncSearchResource",
+ "SearchResourceWithRawResponse",
+ "AsyncSearchResourceWithRawResponse",
+ "SearchResourceWithStreamingResponse",
+ "AsyncSearchResourceWithStreamingResponse",
"SessionsResource",
"AsyncSessionsResource",
"SessionsResourceWithRawResponse",
diff --git a/src/browserbase/resources/search.py b/src/browserbase/resources/search.py
new file mode 100644
index 0000000..87f34e6
--- /dev/null
+++ b/src/browserbase/resources/search.py
@@ -0,0 +1,185 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..types import search_web_params
+from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from .._utils import maybe_transform, async_maybe_transform
+from .._compat import cached_property
+from .._resource import SyncAPIResource, AsyncAPIResource
+from .._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from .._base_client import make_request_options
+from ..types.search_web_response import SearchWebResponse
+
+__all__ = ["SearchResource", "AsyncSearchResource"]
+
+
+class SearchResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> SearchResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/browserbase/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return SearchResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> SearchResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/browserbase/sdk-python#with_streaming_response
+ """
+ return SearchResourceWithStreamingResponse(self)
+
+ def web(
+ self,
+ *,
+ query: str,
+ num_results: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SearchWebResponse:
+ """
+ Perform a web search and return structured results.
+
+ Args:
+ query: The search query string
+
+ num_results: Number of results to return (1-25)
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/v1/search",
+ body=maybe_transform(
+ {
+ "query": query,
+ "num_results": num_results,
+ },
+ search_web_params.SearchWebParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=SearchWebResponse,
+ )
+
+
+class AsyncSearchResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncSearchResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/browserbase/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncSearchResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncSearchResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/browserbase/sdk-python#with_streaming_response
+ """
+ return AsyncSearchResourceWithStreamingResponse(self)
+
+ async def web(
+ self,
+ *,
+ query: str,
+ num_results: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SearchWebResponse:
+ """
+ Perform a web search and return structured results.
+
+ Args:
+ query: The search query string
+
+ num_results: Number of results to return (1-25)
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/v1/search",
+ body=await async_maybe_transform(
+ {
+ "query": query,
+ "num_results": num_results,
+ },
+ search_web_params.SearchWebParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=SearchWebResponse,
+ )
+
+
+class SearchResourceWithRawResponse:
+ def __init__(self, search: SearchResource) -> None:
+ self._search = search
+
+ self.web = to_raw_response_wrapper(
+ search.web,
+ )
+
+
+class AsyncSearchResourceWithRawResponse:
+ def __init__(self, search: AsyncSearchResource) -> None:
+ self._search = search
+
+ self.web = async_to_raw_response_wrapper(
+ search.web,
+ )
+
+
+class SearchResourceWithStreamingResponse:
+ def __init__(self, search: SearchResource) -> None:
+ self._search = search
+
+ self.web = to_streamed_response_wrapper(
+ search.web,
+ )
+
+
+class AsyncSearchResourceWithStreamingResponse:
+ def __init__(self, search: AsyncSearchResource) -> None:
+ self._search = search
+
+ self.web = async_to_streamed_response_wrapper(
+ search.web,
+ )
diff --git a/src/browserbase/types/__init__.py b/src/browserbase/types/__init__.py
index 5265990..0a9a3b8 100644
--- a/src/browserbase/types/__init__.py
+++ b/src/browserbase/types/__init__.py
@@ -7,7 +7,9 @@
from .session import Session as Session
from .extension import Extension as Extension
from .project_usage import ProjectUsage as ProjectUsage
+from .search_web_params import SearchWebParams as SearchWebParams
from .session_live_urls import SessionLiveURLs as SessionLiveURLs
+from .search_web_response import SearchWebResponse as SearchWebResponse
from .session_list_params import SessionListParams as SessionListParams
from .context_create_params import ContextCreateParams as ContextCreateParams
from .project_list_response import ProjectListResponse as ProjectListResponse
diff --git a/src/browserbase/types/fetch_api_create_response.py b/src/browserbase/types/fetch_api_create_response.py
index cfafbba..f97f563 100644
--- a/src/browserbase/types/fetch_api_create_response.py
+++ b/src/browserbase/types/fetch_api_create_response.py
@@ -10,8 +10,6 @@
class FetchAPICreateResponse(BaseModel):
- """Response body for fetch"""
-
id: str
"""Unique identifier for the fetch request"""
diff --git a/src/browserbase/types/search_web_params.py b/src/browserbase/types/search_web_params.py
new file mode 100644
index 0000000..68926b5
--- /dev/null
+++ b/src/browserbase/types/search_web_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, Annotated, TypedDict
+
+from .._utils import PropertyInfo
+
+__all__ = ["SearchWebParams"]
+
+
+class SearchWebParams(TypedDict, total=False):
+ query: Required[str]
+ """The search query string"""
+
+ num_results: Annotated[int, PropertyInfo(alias="numResults")]
+ """Number of results to return (1-25)"""
diff --git a/src/browserbase/types/search_web_response.py b/src/browserbase/types/search_web_response.py
new file mode 100644
index 0000000..0243d22
--- /dev/null
+++ b/src/browserbase/types/search_web_response.py
@@ -0,0 +1,46 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["SearchWebResponse", "Result"]
+
+
+class Result(BaseModel):
+ id: str
+ """Unique identifier for the result"""
+
+ title: str
+ """The title of the search result"""
+
+ url: str
+ """The URL of the search result"""
+
+ author: Optional[str] = None
+ """Author of the content if available"""
+
+ favicon: Optional[str] = None
+ """Favicon URL"""
+
+ image: Optional[str] = None
+ """Image URL if available"""
+
+ published_date: Optional[datetime] = FieldInfo(alias="publishedDate", default=None)
+ """Publication date in ISO 8601 format"""
+
+
+class SearchWebResponse(BaseModel):
+ """Response body for web search"""
+
+ query: str
+ """The search query that was executed"""
+
+ request_id: str = FieldInfo(alias="requestId")
+ """Unique identifier for the request"""
+
+ results: List[Result]
+ """List of search results"""
diff --git a/tests/api_resources/test_search.py b/tests/api_resources/test_search.py
new file mode 100644
index 0000000..fa00b77
--- /dev/null
+++ b/tests/api_resources/test_search.py
@@ -0,0 +1,102 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from browserbase import Browserbase, AsyncBrowserbase
+from tests.utils import assert_matches_type
+from browserbase.types import SearchWebResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestSearch:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_web(self, client: Browserbase) -> None:
+ search = client.search.web(
+ query="x",
+ )
+ assert_matches_type(SearchWebResponse, search, path=["response"])
+
+ @parametrize
+ def test_method_web_with_all_params(self, client: Browserbase) -> None:
+ search = client.search.web(
+ query="x",
+ num_results=1,
+ )
+ assert_matches_type(SearchWebResponse, search, path=["response"])
+
+ @parametrize
+ def test_raw_response_web(self, client: Browserbase) -> None:
+ response = client.search.with_raw_response.web(
+ query="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ search = response.parse()
+ assert_matches_type(SearchWebResponse, search, path=["response"])
+
+ @parametrize
+ def test_streaming_response_web(self, client: Browserbase) -> None:
+ with client.search.with_streaming_response.web(
+ query="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ search = response.parse()
+ assert_matches_type(SearchWebResponse, search, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncSearch:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_web(self, async_client: AsyncBrowserbase) -> None:
+ search = await async_client.search.web(
+ query="x",
+ )
+ assert_matches_type(SearchWebResponse, search, path=["response"])
+
+ @parametrize
+ async def test_method_web_with_all_params(self, async_client: AsyncBrowserbase) -> None:
+ search = await async_client.search.web(
+ query="x",
+ num_results=1,
+ )
+ assert_matches_type(SearchWebResponse, search, path=["response"])
+
+ @parametrize
+ async def test_raw_response_web(self, async_client: AsyncBrowserbase) -> None:
+ response = await async_client.search.with_raw_response.web(
+ query="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ search = await response.parse()
+ assert_matches_type(SearchWebResponse, search, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_web(self, async_client: AsyncBrowserbase) -> None:
+ async with async_client.search.with_streaming_response.web(
+ query="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ search = await response.parse()
+ assert_matches_type(SearchWebResponse, search, path=["response"])
+
+ assert cast(Any, response.is_closed) is True