diff --git a/.gitignore b/.gitignore index ac91bb11..46152338 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,4 @@ dist codegen.log Brewfile.lock.json screenshot.png -openapi.v1.yaml **/.DS_Store diff --git a/README.md b/README.md index 41f865cf..62726e0f 100644 --- a/README.md +++ b/README.md @@ -28,23 +28,45 @@ The full API of this library can be found in [api.md](api.md). ```python import os +from playwright.sync_api import Playwright, sync_playwright from browserbase import Browserbase -client = Browserbase( +BROWSERBASE_API_KEY = os.environ.get("BROWSERBASE_API_KEY") +BROWSERBASE_PROJECT_ID = os.environ.get("BROWSERBASE_PROJECT_ID") + +bb = Browserbase( # This is the default and can be omitted - api_key=os.environ.get("BROWSERBASE_API_KEY"), + api_key=BROWSERBASE_API_KEY, ) -context = client.contexts.create( - project_id="projectId", -) -print(context.id) -``` +def run(playwright: Playwright) -> None: + # Create a session on Browserbase + session = bb.sessions.create(project_id=BROWSERBASE_PROJECT_ID) + + # Connect to the remote session + chromium = playwright.chromium + browser = chromium.connect_over_cdp(session.connect_url) + context = browser.contexts[0] + page = context.pages[0] -While you can provide an `api_key` keyword argument, -we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) -to add `BROWSERBASE_API_KEY="My API Key"` to your `.env` file -so that your API Key is not stored in source control. + # Execute Playwright actions on the remote browser tab + page.goto("https://news.ycombinator.com/") + page_title = page.title() + assert ( + page_title == "Hacker News" + ), f"Page title is not 'Hacker News', it is '{page_title}'" + page.screenshot(path="screenshot.png") + + page.close() + browser.close() + print("Done!") + + +if __name__ == "__main__": + with sync_playwright() as playwright: + run(playwright) + +``` ## Examples @@ -63,33 +85,6 @@ rye run example playwright_basic # replace with the example you want to run > [!NOTE] > Make sure you have a `.env` file that matches the [.env.example](.env.example) file in the root of this repository. -## Async usage - -Simply import `AsyncBrowserbase` instead of `Browserbase` and use `await` with each API call: - -```python -import os -import asyncio -from browserbase import AsyncBrowserbase - -client = AsyncBrowserbase( - # This is the default and can be omitted - api_key=os.environ.get("BROWSERBASE_API_KEY"), -) - - -async def main() -> None: - context = await client.contexts.create( - project_id="projectId", - ) - print(context.id) - - -asyncio.run(main()) -``` - -Functionality between the synchronous and asynchronous clients is otherwise identical. - ## Using types 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: diff --git a/examples/e2e/test_playwright.py b/examples/e2e/test_playwright.py index a9b266e3..a08de603 100644 --- a/examples/e2e/test_playwright.py +++ b/examples/e2e/test_playwright.py @@ -1,12 +1,16 @@ +import os +from typing import Generator + import pytest -from playwright.sync_api import Playwright, sync_playwright from dotenv import load_dotenv -import os +from playwright.sync_api import Playwright, sync_playwright from browserbase import Browserbase from .. import ( BROWSERBASE_API_KEY, playwright_basic, + playwright_proxy, + playwright_upload, playwright_captcha, playwright_contexts, playwright_downloads, @@ -15,28 +19,58 @@ bb = Browserbase(api_key=BROWSERBASE_API_KEY) load_dotenv() -SKIP_CAPTCHA_SOLVING = os.getenv("SKIP_CAPTCHA_SOLVING", "true").lower() == "true" +CI = os.getenv("CI", "false").lower() == "true" @pytest.fixture(scope="session") -def playwright(): +def playwright() -> Generator[Playwright, None, None]: with sync_playwright() as p: yield p - -def test_playwright_basic(playwright: Playwright): +def test_playwright_basic(playwright: Playwright) -> None: playwright_basic.run(playwright) -def test_playwright_captcha(playwright: Playwright): - if SKIP_CAPTCHA_SOLVING: - pytest.skip("Skipping captcha solving") +@pytest.mark.skipif(True, reason="Flaky and fails often") +def test_playwright_captcha(playwright: Playwright) -> None: playwright_captcha.run(playwright) -def test_playwright_contexts(playwright: Playwright): +def test_playwright_contexts(playwright: Playwright) -> None: playwright_contexts.run(playwright) -def test_playwright_downloads(playwright: Playwright): +def test_playwright_downloads(playwright: Playwright) -> None: playwright_downloads.run(playwright) + + +def test_playwright_proxy_enable_via_create_session(playwright: Playwright) -> None: + playwright_proxy.run_enable_via_create_session(playwright) + + +def test_playwright_proxy_enable_via_querystring(playwright: Playwright) -> None: + playwright_proxy.run_enable_via_querystring_with_created_session(playwright) + + +@pytest.mark.skipif(CI, reason="Flaky and fails on CI") +def test_playwright_proxy_geolocation_country(playwright: Playwright) -> None: + playwright_proxy.run_geolocation_country(playwright) + + +@pytest.mark.skipif(CI, reason="Flaky and fails on CI") +def test_playwright_proxy_geolocation_state(playwright: Playwright) -> None: + playwright_proxy.run_geolocation_state(playwright) + + +@pytest.mark.skipif(CI, reason="Flaky and fails on CI") +def test_playwright_proxy_geolocation_american_city(playwright: Playwright) -> None: + playwright_proxy.run_geolocation_american_city(playwright) + + +@pytest.mark.skipif(CI, reason="Flaky and fails on CI") +def test_playwright_proxy_geolocation_non_american_city(playwright: Playwright) -> None: + playwright_proxy.run_geolocation_non_american_city(playwright) + + +def test_playwright_upload(playwright: Playwright) -> None: + playwright_upload.run(playwright) diff --git a/examples/packages/logo.png b/examples/packages/logo.png new file mode 100644 index 00000000..583360a1 Binary files /dev/null and b/examples/packages/logo.png differ diff --git a/examples/playwright_basic.py b/examples/playwright_basic.py index e909f9db..1ba97530 100644 --- a/examples/playwright_basic.py +++ b/examples/playwright_basic.py @@ -1,25 +1,29 @@ +import os + from playwright.sync_api import Playwright, sync_playwright -from examples import ( - BROWSERBASE_API_KEY, - BROWSERBASE_PROJECT_ID, - BROWSERBASE_CONNECT_URL, - bb, +from browserbase import Browserbase + +BROWSERBASE_API_KEY = os.environ.get("BROWSERBASE_API_KEY", "") +if not BROWSERBASE_API_KEY: + raise ValueError("BROWSERBASE_API_KEY is not set") +BROWSERBASE_PROJECT_ID = os.environ.get("BROWSERBASE_PROJECT_ID", "") +if not BROWSERBASE_PROJECT_ID: + raise ValueError("BROWSERBASE_PROJECT_ID is not set") + +bb = Browserbase( + # This is the default and can be omitted + api_key=BROWSERBASE_API_KEY, ) -def run(playwright: Playwright): +def run(playwright: Playwright) -> None: # Create a session on Browserbase session = bb.sessions.create(project_id=BROWSERBASE_PROJECT_ID) - assert session.id is not None - assert session.status == "RUNNING", f"Session status is {session.status}" # Connect to the remote session - connect_url = ( - f"{BROWSERBASE_CONNECT_URL}?sessionId={session.id}&apiKey={BROWSERBASE_API_KEY}" - ) chromium = playwright.chromium - browser = chromium.connect_over_cdp(connect_url) + browser = chromium.connect_over_cdp(session.connect_url) context = browser.contexts[0] page = context.pages[0] diff --git a/examples/playwright_captcha.py b/examples/playwright_captcha.py index 1691ba76..da768447 100644 --- a/examples/playwright_captcha.py +++ b/examples/playwright_captcha.py @@ -1,4 +1,4 @@ -from playwright.sync_api import Playwright, sync_playwright, ConsoleMessage +from playwright.sync_api import Playwright, ConsoleMessage, sync_playwright from examples import ( @@ -12,7 +12,7 @@ OVERRIDE_TIMEOUT = 60000 # 60 seconds, adjust as needed -def run(playwright: Playwright): +def run(playwright: Playwright) -> None: # Create a session on Browserbase session = bb.sessions.create(project_id=BROWSERBASE_PROJECT_ID) assert session.id is not None @@ -32,7 +32,7 @@ def run(playwright: Playwright): # Browserbase logs messages to the console to indicate when captcha solving has started and finished # We can track these messages to know when the captcha solving has started and finished - def handle_console(msg: ConsoleMessage): + def handle_console(msg: ConsoleMessage) -> None: nonlocal captcha_solving_started, captcha_solving_finished if msg.text == "browserbase-solving-started": captcha_solving_started = True diff --git a/examples/playwright_contexts.py b/examples/playwright_contexts.py index f6a65c1e..6a10846d 100644 --- a/examples/playwright_contexts.py +++ b/examples/playwright_contexts.py @@ -1,17 +1,20 @@ -from playwright.sync_api import Playwright, sync_playwright, Browser, Cookie -from browserbase.types.session_create_params import ( - BrowserSettings, - BrowserSettingsContext, -) import time from typing import Optional +from playwright.sync_api import Cookie, Browser, Playwright, sync_playwright + + from examples import ( BROWSERBASE_API_KEY, BROWSERBASE_PROJECT_ID, BROWSERBASE_CONNECT_URL, bb, ) +from browserbase.types.session_create_params import ( + BrowserSettings, + BrowserSettingsContext, +) + CONTEXT_TEST_URL = "https://www.browserbase.com" SECOND = 1000 @@ -27,7 +30,7 @@ def find_cookie(browser: Browser, name: str) -> Optional[Cookie]: return next((cookie for cookie in cookies if cookie.get("name") == name), None) -def run(playwright: Playwright): +def run(playwright: Playwright) -> None: context_id = None session_id = None test_cookie_name = None diff --git a/examples/playwright_downloads.py b/examples/playwright_downloads.py index 3dc76539..3e1e3401 100644 --- a/examples/playwright_downloads.py +++ b/examples/playwright_downloads.py @@ -1,7 +1,10 @@ +import io import re import zipfile -import io + from playwright.sync_api import Playwright, sync_playwright + + from examples import ( BROWSERBASE_API_KEY, BROWSERBASE_PROJECT_ID, @@ -17,7 +20,7 @@ def get_download(session_id: str) -> bytes: return response.read() -def run(playwright: Playwright): +def run(playwright: Playwright) -> None: # Create a session on Browserbase session = bb.sessions.create(project_id=BROWSERBASE_PROJECT_ID) assert session.id is not None diff --git a/examples/playwright_extensions.py b/examples/playwright_extensions.py index 944ac55d..d12f47a0 100644 --- a/examples/playwright_extensions.py +++ b/examples/playwright_extensions.py @@ -1,17 +1,18 @@ import os -import zipfile import time +import zipfile +from io import BytesIO from pathlib import Path -from playwright.sync_api import sync_playwright, Playwright -from browserbase.types.extension import Extension -from browserbase.types.session import Session + +from playwright.sync_api import Page, Playwright, sync_playwright + from examples import ( BROWSERBASE_API_KEY, BROWSERBASE_PROJECT_ID, BROWSERBASE_CONNECT_URL, bb, ) -from io import BytesIO +from browserbase.types import Extension, SessionCreateResponse PATH_TO_EXTENSION = ( Path.cwd() / "examples" / "packages" / "extensions" / "browserbase-test" @@ -50,7 +51,7 @@ def zip_extension(path: Path = PATH_TO_EXTENSION, save_local: bool = False) -> B return memory_zip -def create_extension(): +def create_extension() -> str: zip_data = zip_extension(save_local=True) extension: Extension = bb.extensions.create( file=("extension.zip", zip_data.getvalue()) @@ -62,11 +63,27 @@ def get_extension(id: str) -> Extension: return bb.extensions.retrieve(id) -def delete_extension(id: str): +def delete_extension(id: str) -> None: bb.extensions.delete(id) -def run(playwright: Playwright): +def check_for_message(page: Page, message: str) -> None: + # Wait for the extension to load and log a message + console_messages: list[str] = [] + page.on("console", lambda msg: console_messages.append(msg.text)) + page.goto("https://www.browserbase.com/") + + start = time.time() + while time.time() - start < 10: + if message in console_messages: + break + assert ( + message in console_messages + ), f"Expected message not found in console logs. Messages: {console_messages}" + + +def run(playwright: Playwright) -> None: + expected_message = "browserbase test extension image loaded" extension_id = None # Create extension @@ -78,7 +95,7 @@ def run(playwright: Playwright): print(f"Retrieved extension: {extension}") # Use extension - session: Session = bb.sessions.create( + session: SessionCreateResponse = bb.sessions.create( project_id=BROWSERBASE_PROJECT_ID, extension_id=extension.id, ) @@ -88,26 +105,12 @@ def run(playwright: Playwright): ) context = browser.contexts[0] page = context.pages[0] - - console_messages: list[str] = [] - page.on("console", lambda msg: console_messages.append(msg.text)) - - page.goto("https://www.browserbase.com/") - - # Wait for the extension to load and log a message - start = time.time() - while time.time() - start < 10: - if "browserbase test extension image loaded" in console_messages: - break - assert ( - "browserbase test extension image loaded" in console_messages - ), f"Expected message not found in console logs. Messages: {console_messages}" - + check_for_message(page, expected_message) page.close() browser.close() # Use extension with proxies - session_with_proxy: Session = bb.sessions.create( + session_with_proxy: SessionCreateResponse = bb.sessions.create( project_id=BROWSERBASE_PROJECT_ID, extension_id=extension_id, proxies=True, @@ -124,15 +127,7 @@ def run(playwright: Playwright): page.goto("https://www.browserbase.com/") - # Wait for the extension to load and log a message (longer timeout for proxies) - start = time.time() - while time.time() - start < 10: - if "browserbase test extension image loaded" in console_messages: - break - assert ( - "browserbase test extension image loaded" in console_messages - ), f"Expected message not found in console logs. Messages: {console_messages}" - + check_for_message(page, expected_message) page.close() browser.close() diff --git a/examples/playwright_proxy.py b/examples/playwright_proxy.py new file mode 100644 index 00000000..4ea6677b --- /dev/null +++ b/examples/playwright_proxy.py @@ -0,0 +1,198 @@ +import time + +from playwright.sync_api import Page, Playwright, sync_playwright + +from examples import ( + BROWSERBASE_API_KEY, + BROWSERBASE_PROJECT_ID, + BROWSERBASE_CONNECT_URL, + bb, +) + +GRACEFUL_SHUTDOWN_TIMEOUT = 30000 # Assuming 30 seconds, adjust as needed + + +def check_proxy_bytes(session_id: str) -> None: + bb.sessions.update( + id=session_id, project_id=BROWSERBASE_PROJECT_ID, status="REQUEST_RELEASE" + ) + time.sleep(GRACEFUL_SHUTDOWN_TIMEOUT / 1000) + updated_session = bb.sessions.retrieve(id=session_id) + assert ( + updated_session.proxy_bytes is not None and updated_session.proxy_bytes > 0 + ), f"Proxy bytes: {updated_session.proxy_bytes}" + + +def run_enable_via_create_session(playwright: Playwright) -> None: + session = bb.sessions.create(project_id=BROWSERBASE_PROJECT_ID, proxies=True) + + browser = playwright.chromium.connect_over_cdp( + f"{BROWSERBASE_CONNECT_URL}?apiKey={BROWSERBASE_API_KEY}&sessionId={session.id}" + ) + + context = browser.contexts[0] + page = context.pages[0] + page.goto("https://www.google.com") + page_title = page.title() + + page.close() + browser.close() + + assert page_title == "Google" + check_proxy_bytes(session.id) + + +def run_enable_via_querystring_with_created_session(playwright: Playwright) -> None: + session = bb.sessions.create(project_id=BROWSERBASE_PROJECT_ID, proxies=True) + + browser = playwright.chromium.connect_over_cdp( + f"{BROWSERBASE_CONNECT_URL}?apiKey={BROWSERBASE_API_KEY}&sessionId={session.id}&enableProxy=true" + ) + + context = browser.contexts[0] + page = context.pages[0] + page.goto("https://www.google.com/") + page_title = page.title() + + page.close() + browser.close() + + assert page_title == "Google" + check_proxy_bytes(session.id) + + +def extract_from_table(page: Page, cell: str) -> str: + page.goto("https://www.showmyip.com/") + page.wait_for_selector("table.iptab") + + td = page.locator(f"table.iptab tr:has-text('{cell}') td:last-child") + + text = td.text_content() + if not text: + raise Exception(f"Failed to extract {cell}") + return text.strip() + + +def run_geolocation_country(playwright: Playwright) -> None: + session = bb.sessions.create( + project_id=BROWSERBASE_PROJECT_ID, + proxies=[ + { + "geolocation": {"country": "CA"}, + "type": "browserbase", + } + ], + ) + + browser = playwright.chromium.connect_over_cdp( + f"{BROWSERBASE_CONNECT_URL}?apiKey={BROWSERBASE_API_KEY}&sessionId={session.id}" + ) + + context = browser.contexts[0] + page = context.pages[0] + + country = extract_from_table(page, "Country") + + page.close() + browser.close() + + assert country == "Canada" + + +def run_geolocation_state(playwright: Playwright) -> None: + session = bb.sessions.create( + project_id=BROWSERBASE_PROJECT_ID, + proxies=[ + { + "geolocation": { + "country": "US", + "state": "NY", + }, + "type": "browserbase", + } + ], + ) + + browser = playwright.chromium.connect_over_cdp( + f"{BROWSERBASE_CONNECT_URL}?apiKey={BROWSERBASE_API_KEY}&sessionId={session.id}" + ) + + context = browser.contexts[0] + page = context.pages[0] + + state = extract_from_table(page, "Region") + + page.close() + browser.close() + + assert state == "New York" + + +def run_geolocation_american_city(playwright: Playwright) -> None: + session = bb.sessions.create( + project_id=BROWSERBASE_PROJECT_ID, + proxies=[ + { + "geolocation": { + "city": "Los Angeles", + "country": "US", + "state": "CA", + }, + "type": "browserbase", + } + ], + ) + + browser = playwright.chromium.connect_over_cdp( + f"{BROWSERBASE_CONNECT_URL}?apiKey={BROWSERBASE_API_KEY}&sessionId={session.id}" + ) + + context = browser.contexts[0] + page = context.pages[0] + + city = extract_from_table(page, "City") + + page.close() + browser.close() + + assert city == "Los Angeles" + + +def run_geolocation_non_american_city(playwright: Playwright) -> None: + session = bb.sessions.create( + project_id=BROWSERBASE_PROJECT_ID, + proxies=[ + { + "geolocation": { + "city": "London", + "country": "GB", + }, + "type": "browserbase", + } + ], + ) + + browser = playwright.chromium.connect_over_cdp( + f"{BROWSERBASE_CONNECT_URL}?apiKey={BROWSERBASE_API_KEY}&sessionId={session.id}" + ) + + context = browser.contexts[0] + page = context.pages[0] + + city = extract_from_table(page, "City") + + page.close() + browser.close() + + assert city == "London" + + +if __name__ == "__main__": + with sync_playwright() as playwright: + # You can run any of these tests by uncommenting them + run_enable_via_create_session(playwright) + # run_enable_via_querystring_with_created_session(playwright) + # run_geolocation_country(playwright) + # run_geolocation_state(playwright) + # run_geolocation_american_city(playwright) + # run_geolocation_non_american_city(playwright) diff --git a/examples/playwright_upload.py b/examples/playwright_upload.py new file mode 100644 index 00000000..c1a2237c --- /dev/null +++ b/examples/playwright_upload.py @@ -0,0 +1,63 @@ +from pathlib import Path + +from playwright.sync_api import Playwright, sync_playwright + +from examples import ( + BROWSERBASE_API_KEY, + BROWSERBASE_PROJECT_ID, + BROWSERBASE_CONNECT_URL, + bb, +) + +PATH_TO_UPLOAD = Path.cwd() / "examples" / "packages" / "logo.png" + + +def run(playwright: Playwright) -> None: + # Create a session + session = bb.sessions.create(project_id=BROWSERBASE_PROJECT_ID) + + # Construct the URL + url = ( + f"{BROWSERBASE_CONNECT_URL}?apiKey={BROWSERBASE_API_KEY}&sessionId={session.id}" + ) + + # Connect to the browser + browser = playwright.chromium.connect_over_cdp(url) + context = browser.contexts[0] + page = context.pages[0] + + try: + # Navigate to the upload test page + page.goto("https://browser-tests-alpha.vercel.app/api/upload-test") + + # Locate the file input element + file_input = page.locator("#fileUpload") + file_input.set_input_files(str(PATH_TO_UPLOAD)) + + # Get the uploaded file name + file_name_span = page.locator("#fileName") + file_name = file_name_span.inner_text() + + # Get the uploaded file size + file_size_span = page.locator("#fileSize") + file_size = int(file_size_span.inner_text()) + + # Assert the file name and size + assert ( + file_name == "logo.png" + ), f"Expected file name to be 'logo.png', but got '{file_name}'" + assert ( + file_size > 0 + ), f"Expected file size to be greater than 0, but got {file_size}" + + print("File upload test passed successfully!") + + finally: + # Clean up + page.close() + browser.close() + + +if __name__ == "__main__": + with sync_playwright() as playwright: + run(playwright) diff --git a/openapi.v1.yaml b/openapi.v1.yaml new file mode 100644 index 00000000..7bb296d0 --- /dev/null +++ b/openapi.v1.yaml @@ -0,0 +1,1142 @@ +openapi: 3.0.0 +info: + title: Browserbase API + description: Browserbase API for 3rd party developers + version: v1 +tags: [] +paths: + /v1/contexts: + post: + operationId: Contexts_create + summary: Create a Context + parameters: [] + responses: + "201": + description: The request has succeeded and a new resource has been created as a result. + content: + application/json: + schema: + $ref: "#/components/schemas/CreateContextResponse" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateContext" + /v1/contexts/{id}: + get: + operationId: Contexts_get + summary: Context + parameters: + - name: id + in: path + required: true + schema: + $ref: "#/components/schemas/uuid" + responses: + "200": + description: The request has succeeded. + content: + application/json: + schema: + $ref: "#/components/schemas/Context" + put: + operationId: Contexts_update + summary: Update Context + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "200": + description: The request has succeeded. + content: + application/json: + schema: + $ref: "#/components/schemas/CreateContextResponse" + /v1/extensions: + post: + operationId: Extensions_upload + summary: Upload an Extension + parameters: [] + responses: + "200": + description: The request has succeeded. + content: + application/json: + schema: + $ref: "#/components/schemas/Extension" + "415": + description: Client error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + file: + type: string + format: binary + required: + - file + /v1/extensions/{id}: + get: + operationId: Extensions_get + summary: Extension + parameters: + - name: id + in: path + required: true + schema: + $ref: "#/components/schemas/uuid" + responses: + "200": + description: The request has succeeded. + content: + application/json: + schema: + $ref: "#/components/schemas/Extension" + delete: + operationId: Extensions_delete + summary: Delete Extension + parameters: + - name: id + in: path + required: true + schema: + $ref: "#/components/schemas/uuid" + responses: + "204": + description: "There is no content to send for this request, but the headers may be useful. " + /v1/projects: + get: + operationId: Projects_list + summary: List all projects + parameters: [] + responses: + "200": + description: The request has succeeded. + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Project" + /v1/projects/{id}: + get: + operationId: Projects_get + summary: Project + parameters: + - name: id + in: path + required: true + schema: + $ref: "#/components/schemas/uuid" + responses: + "200": + description: The request has succeeded. + content: + application/json: + schema: + $ref: "#/components/schemas/Project" + /v1/projects/{id}/usage: + get: + operationId: Projects_usage + summary: Project Usage + parameters: + - name: id + in: path + required: true + schema: + $ref: "#/components/schemas/uuid" + responses: + "200": + description: The request has succeeded. + content: + application/json: + schema: + $ref: "#/components/schemas/ProjectUsage" + /v1/sessions: + get: + operationId: Sessions_list + summary: List Sessions + parameters: + - name: status + in: query + required: false + schema: + $ref: "#/components/schemas/SessionStatus" + explode: false + responses: + "200": + description: The request has succeeded. + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Session" + post: + operationId: Sessions_create + summary: Create a Session + parameters: [] + responses: + "201": + description: The request has succeeded and a new resource has been created as a result. + content: + application/json: + schema: + type: object + properties: + projectId: + allOf: + - $ref: "#/components/schemas/uuid" + description: The Project ID linked to the Session. + startedAt: + type: string + format: date-time + endedAt: + type: string + format: date-time + expiresAt: + type: string + format: date-time + status: + $ref: "#/components/schemas/SessionStatus" + proxyBytes: + type: integer + description: Bytes used via the [Proxy](/features/stealth-mode#proxies-and-residential-ips) + avgCpuUsage: + type: integer + description: CPU used by the Session + memoryUsage: + type: integer + description: Memory used by the Session + keepAlive: + type: boolean + description: Indicates if the Session was created to be kept alive upon disconnections + contextId: + allOf: + - $ref: "#/components/schemas/uuid" + description: Optional. The Context linked to the Session. + region: + allOf: + - $ref: "#/components/schemas/Region" + description: The region where the Session is running. + id: + $ref: "#/components/schemas/uuid" + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + connectUrl: + type: string + format: uri + description: WebSocket URL to connect to the Session. + seleniumRemoteUrl: + type: string + format: uri + description: HTTP URL to connect to the Session. + signingKey: + type: string + description: Signing key to use when connecting to the Session via HTTP. + required: + - projectId + - startedAt + - expiresAt + - status + - proxyBytes + - keepAlive + - region + - id + - createdAt + - updatedAt + - connectUrl + - seleniumRemoteUrl + - signingKey + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateSession" + x-codeSamples: + - lang: cURL + source: |- + curl --request POST \ + --url https://www.browserbase.com/v1/sessions \ + --header 'Content-Type: application/json' \ + --header 'X-BB-API-Key: ' \ + --data '{"projectId": ""}' + - lang: JavaScript + source: |- + fetch('https://www.browserbase.com/v1/sessions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-BB-API-Key': '' + }, + body: JSON.stringify({ + "projectId": "", + }) + }) + - lang: Python + source: |- + import requests + + url = "https://www.browserbase.com/v1/sessions" + + payload = { + "projectId": "", + } + headers = { + "X-BB-API-Key": "", + "Content-Type": "application/json" + } + + response = requests.request("POST", url, json=payload, headers=headers) + + print(response.text) + - lang: PHP + source: |- + "https://www.browserbase.com/v1/sessions", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => "", + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 30, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => "POST", + CURLOPT_POSTFIELDS => "{"projectId": ""}", + CURLOPT_HTTPHEADER => [ + "Content-Type: application/json", + "X-BB-API-Key: " + ], + ]); + + $response = curl_exec($curl); + $err = curl_error($curl); + + curl_close($curl); + + if ($err) { + echo "cURL Error #:" . $err; + } else { + echo $response; + } + - lang: Go + source: |- + package main + + import ( + "fmt" + "strings" + "net/http" + "io/ioutil" + ) + + func main() { + + url := "https://www.browserbase.com/v1/sessions" + + payload := strings.NewReader("{"projectId": ""}") + + req, _ := http.NewRequest("POST", url, payload) + + req.Header.Add("X-BB-API-Key", "") + req.Header.Add("Content-Type", "application/json") + + res, _ := http.DefaultClient.Do(req) + + defer res.Body.Close() + body, _ := ioutil.ReadAll(res.Body) + + fmt.Println(res) + fmt.Println(string(body)) + + } + - lang: Java + source: |- + HttpResponse response = Unirest.post("https://www.browserbase.com/v1/sessions") + .header("X-BB-API-Key", "") + .header("Content-Type", "application/json") + .body("{"projectId": ""}") + .asString(); + /v1/sessions/{id}: + get: + operationId: Sessions_get + summary: Session + parameters: + - name: id + in: path + required: true + schema: + $ref: "#/components/schemas/uuid" + responses: + "200": + description: The request has succeeded. + content: + application/json: + schema: + $ref: "#/components/schemas/Session" + post: + operationId: Sessions_update + summary: Update Session + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "200": + description: The request has succeeded. + content: + application/json: + schema: + $ref: "#/components/schemas/Session" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/SessionUpdate" + /v1/sessions/{id}/debug: + get: + operationId: Sessions_getDebug + summary: Session Live URLs + parameters: + - name: id + in: path + required: true + schema: + $ref: "#/components/schemas/uuid" + responses: + "200": + description: The request has succeeded. + content: + application/json: + schema: + $ref: "#/components/schemas/SessionLiveUrls" + /v1/sessions/{id}/downloads: + get: + operationId: Sessions_getDownloads + summary: Session Downloads + parameters: + - name: id + in: path + required: true + schema: + $ref: "#/components/schemas/uuid" + responses: + "200": + description: The request has succeeded. + content: + application/zip: + schema: + type: string + format: binary + /v1/sessions/{id}/logs: + get: + operationId: Sessions_getLogs + summary: Session Logs + parameters: + - name: id + in: path + required: true + schema: + $ref: "#/components/schemas/uuid" + responses: + "200": + description: The request has succeeded. + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/SessionLog" + "422": + description: Client error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /v1/sessions/{id}/recording: + get: + operationId: Sessions_getRecording + summary: Session Recording + parameters: + - name: id + in: path + required: true + schema: + $ref: "#/components/schemas/uuid" + responses: + "200": + description: The request has succeeded. + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/SessionRecording" + "422": + description: Client error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /v1/sessions/{id}/uploads: + post: + operationId: Sessions_uploadFile + summary: Create Session Uploads + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "200": + description: The request has succeeded. + content: + application/json: + schema: + type: object + properties: + message: + type: string + required: + - message + "415": + description: Client error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + file: + type: string + format: binary + required: + - file +security: + - BrowserbaseAuth: [] +components: + schemas: + BrowserbaseAuth: + type: object + required: + - type + - in + - name + properties: + type: + type: string + enum: + - apiKey + description: API key authentication + in: + type: string + enum: + - header + description: location of the API key + name: + type: string + enum: + - X-BB-API-Key + description: name of the API key + description: Your [Browserbase API Key](https://www.browserbase.com/settings). + BrowserbaseProxyConfig: + type: object + required: + - type + properties: + type: + type: string + enum: + - browserbase + description: Type of proxy. Always use 'browserbase' for the Browserbase managed proxy network. + geolocation: + allOf: + - $ref: "#/components/schemas/GeolocationConfig" + description: Geographic location for the proxy. Optional. + domainPattern: + type: string + description: Domain pattern for which this proxy should be used. If omitted, defaults to all domains. Optional. + Context: + type: object + required: + - id + - createdAt + - updatedAt + - projectId + properties: + id: + $ref: "#/components/schemas/uuid" + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + projectId: + allOf: + - $ref: "#/components/schemas/uuid" + description: The Project ID linked to the uploaded Context. + ContextSetting: + type: object + required: + - id + properties: + id: + allOf: + - $ref: "#/components/schemas/uuid" + description: The Context ID. + persist: + type: boolean + description: Whether or not to persist the context after browsing. Defaults to `false`. + CreateContext: + type: object + required: + - projectId + properties: + projectId: + allOf: + - $ref: "#/components/schemas/uuid" + description: The Project ID. Can be found in [Settings](https://www.browserbase.com/settings). + CreateContextResponse: + type: object + required: + - id + - uploadUrl + - publicKey + - cipherAlgorithm + - initializationVectorSize + properties: + id: + $ref: "#/components/schemas/uuid" + uploadUrl: + type: string + minLength: 1 + description: An upload URL to upload a custom user-data-directory. + publicKey: + type: string + description: The public key to encrypt the user-data-directory. + cipherAlgorithm: + type: string + description: The cipher algorithm used to encrypt the user-data-directory. AES-256-CBC is currently the only supported algorithm. + initializationVectorSize: + type: integer + format: uint8 + description: The initialization vector size used to encrypt the user-data-directory. [Read more about how to use it](/features/contexts). + CreateSession: + type: object + required: + - projectId + properties: + projectId: + allOf: + - $ref: "#/components/schemas/uuid" + description: The Project ID. Can be found in [Settings](https://www.browserbase.com/settings). + extensionId: + allOf: + - $ref: "#/components/schemas/uuid" + description: The uploaded Extension ID. See [Upload Extension](/reference/api/upload-an-extension). + browserSettings: + $ref: "#/components/schemas/SessionBrowserSettings" + timeout: + type: integer + minimum: 60 + maximum: 21600 + description: Duration in seconds after which the session will automatically end. Defaults to the Project's `defaultTimeout`. + keepAlive: + type: boolean + description: Set to true to keep the session alive even after disconnections. This is available on the Startup plan only. + proxies: + description: Proxy configuration. Can be true for default proxy, or an array of proxy configurations. + region: + allOf: + - $ref: "#/components/schemas/Region" + description: The region where the Session should run. + CreateSessionConnectDetails: + type: object + required: + - connectUrl + - seleniumRemoteUrl + - signingKey + properties: + connectUrl: + type: string + format: uri + description: WebSocket URL to connect to the Session. + seleniumRemoteUrl: + type: string + format: uri + description: HTTP URL to connect to the Session. + signingKey: + type: string + description: Signing key to use when connecting to the Session via HTTP. + Entity: + type: object + required: + - id + - createdAt + - updatedAt + properties: + id: + $ref: "#/components/schemas/uuid" + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + message: + type: string + Extension: + type: object + required: + - id + - createdAt + - updatedAt + - fileName + - projectId + properties: + id: + $ref: "#/components/schemas/uuid" + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + fileName: + type: string + minLength: 1 + projectId: + allOf: + - $ref: "#/components/schemas/uuid" + description: The Project ID linked to the uploaded Extension. + ExternalProxyConfig: + type: object + required: + - type + - server + properties: + type: + type: string + enum: + - external + description: Type of proxy. Always 'external' for this config. + server: + type: string + description: Server URL for external proxy. Required. + domainPattern: + type: string + description: Domain pattern for which this proxy should be used. If omitted, defaults to all domains. Optional. + username: + type: string + description: Username for external proxy authentication. Optional. + password: + type: string + description: Password for external proxy authentication. Optional. + Fingerprint: + type: object + properties: + httpVersion: + type: number + enum: + - 1 + - 2 + browsers: + type: array + items: + type: string + enum: + - chrome + - edge + - firefox + - safari + devices: + type: array + items: + type: string + enum: + - desktop + - mobile + locales: + type: array + items: + type: string + description: Full list of locales is available [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language). + operatingSystems: + type: array + items: + type: string + enum: + - android + - ios + - linux + - macos + - windows + description: 'Note: `operatingSystems` set to `ios` or `android` requires `devices` to include `"mobile"`.' + screen: + $ref: "#/components/schemas/FingerprintScreen" + FingerprintScreen: + type: object + properties: + maxHeight: + type: integer + maxWidth: + type: integer + minHeight: + type: integer + minWidth: + type: integer + GeolocationConfig: + type: object + required: + - country + properties: + city: + type: string + description: Name of the city. Use spaces for multi-word city names. Optional. + state: + type: string + minLength: 2 + maxLength: 2 + description: US state code (2 characters). Must also specify US as the country. Optional. + country: + type: string + minLength: 2 + maxLength: 2 + description: Country code in ISO 3166-1 alpha-2 format + description: Configuration for geolocation + Project: + type: object + required: + - id + - createdAt + - updatedAt + - name + - ownerId + - defaultTimeout + properties: + id: + $ref: "#/components/schemas/uuid" + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + name: + type: string + minLength: 1 + ownerId: + type: string + defaultTimeout: + type: integer + minimum: 60 + maximum: 21600 + ProjectUsage: + type: object + required: + - browserMinutes + - proxyBytes + properties: + browserMinutes: + type: integer + minimum: 0 + proxyBytes: + type: integer + minimum: 0 + Region: + type: string + enum: + - us-west-2 + - us-east-1 + - eu-central-1 + - ap-southeast-1 + Session: + type: object + required: + - id + - createdAt + - updatedAt + - projectId + - startedAt + - expiresAt + - status + - proxyBytes + - keepAlive + - region + properties: + id: + $ref: "#/components/schemas/uuid" + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + projectId: + allOf: + - $ref: "#/components/schemas/uuid" + description: The Project ID linked to the Session. + startedAt: + type: string + format: date-time + endedAt: + type: string + format: date-time + expiresAt: + type: string + format: date-time + status: + $ref: "#/components/schemas/SessionStatus" + proxyBytes: + type: integer + description: Bytes used via the [Proxy](/features/stealth-mode#proxies-and-residential-ips) + avgCpuUsage: + type: integer + description: CPU used by the Session + memoryUsage: + type: integer + description: Memory used by the Session + keepAlive: + type: boolean + description: Indicates if the Session was created to be kept alive upon disconnections + contextId: + allOf: + - $ref: "#/components/schemas/uuid" + description: Optional. The Context linked to the Session. + region: + allOf: + - $ref: "#/components/schemas/Region" + description: The region where the Session is running. + SessionBrowserSettings: + type: object + properties: + context: + $ref: "#/components/schemas/ContextSetting" + extensionId: + allOf: + - $ref: "#/components/schemas/uuid" + description: The uploaded Extension ID. See [Upload Extension](/reference/api/upload-an-extension). + fingerprint: + allOf: + - $ref: "#/components/schemas/Fingerprint" + description: See usage examples [in the Stealth Mode page](/features/stealth-mode#fingerprinting). + viewport: + $ref: "#/components/schemas/SessionBrowserSettingsViewport" + blockAds: + type: boolean + description: Enable or disable ad blocking in the browser. Defaults to `false`. + solveCaptchas: + type: boolean + description: Enable or disable captcha solving in the browser. Defaults to `true`. + recordSession: + type: boolean + description: Enable or disable session recording. Defaults to `true`. + logSession: + type: boolean + description: Enable or disable session logging. Defaults to `true`. + SessionBrowserSettingsViewport: + type: object + properties: + width: + type: integer + height: + type: integer + SessionLiveUrls: + type: object + required: + - debuggerFullscreenUrl + - debuggerUrl + - pages + - wsUrl + properties: + debuggerFullscreenUrl: + type: string + format: uri + debuggerUrl: + type: string + format: uri + pages: + type: array + items: + type: object + properties: + id: + type: string + url: + type: string + format: uri + faviconUrl: + type: string + format: uri + title: + type: string + debuggerUrl: + type: string + format: uri + debuggerFullscreenUrl: + type: string + format: uri + required: + - id + - url + - faviconUrl + - title + - debuggerUrl + - debuggerFullscreenUrl + wsUrl: + type: string + format: uri + SessionLog: + type: object + required: + - eventId + - method + - pageId + - sessionId + - timestamp + properties: + eventId: + type: string + method: + type: string + pageId: + type: integer + request: + $ref: "#/components/schemas/SessionLogRequestBody" + response: + $ref: "#/components/schemas/SessionLogResponseBody" + sessionId: + type: string + timestamp: + type: integer + description: milliseconds that have elapsed since the UNIX epoch + frameId: + type: string + loaderId: + type: string + SessionLogRequestBody: + type: object + required: + - timestamp + - params + - rawBody + properties: + timestamp: + type: integer + description: milliseconds that have elapsed since the UNIX epoch + params: + type: object + additionalProperties: {} + rawBody: + type: string + SessionLogResponseBody: + type: object + required: + - timestamp + - result + - rawBody + properties: + timestamp: + type: integer + description: milliseconds that have elapsed since the UNIX epoch + result: + type: object + additionalProperties: {} + rawBody: + type: string + SessionRecording: + type: object + required: + - id + - data + - sessionId + - timestamp + - type + properties: + id: + type: string + data: + type: object + additionalProperties: {} + description: See [rrweb documentation](https://github.com/rrweb-io/rrweb/blob/master/docs/recipes/dive-into-event.md). + sessionId: + type: string + timestamp: + type: integer + description: milliseconds that have elapsed since the UNIX epoch + type: + type: integer + SessionStatus: + type: string + enum: + - RUNNING + - ERROR + - TIMED_OUT + - COMPLETED + SessionUpdate: + type: object + required: + - projectId + - status + properties: + projectId: + allOf: + - $ref: "#/components/schemas/uuid" + description: The Project ID. Can be found in [Settings](https://www.browserbase.com/settings). + status: + type: string + enum: + - REQUEST_RELEASE + description: Set to `REQUEST_RELEASE` to request that the session complete. Use before session's timeout to avoid additional charges. + Versions: + type: string + enum: + - v1 + uuid: + type: string + securitySchemes: + BrowserbaseAuth: + type: apiKey + in: header + name: X-BB-API-Key + description: Your [Browserbase API Key](https://www.browserbase.com/settings). +servers: + - url: https://www.browserbase.com + description: Public endpoint + variables: {} diff --git a/src/browserbase/resources/sessions/sessions.py b/src/browserbase/resources/sessions/sessions.py index d298c011..74a99bbb 100644 --- a/src/browserbase/resources/sessions/sessions.py +++ b/src/browserbase/resources/sessions/sessions.py @@ -714,4 +714,4 @@ def recording(self) -> AsyncRecordingResourceWithStreamingResponse: @cached_property def uploads(self) -> AsyncUploadsResourceWithStreamingResponse: - return AsyncUploadsResourceWithStreamingResponse(self._sessions.uploads) + return AsyncUploadsResourceWithStreamingResponse(self._sessions.uploads) \ No newline at end of file