Skip to content

docs(examples): per-file Downloads API example - #193

Open
jayozer wants to merge 1 commit into
browserbase:mainfrom
jayozer:examples/downloads-api
Open

docs(examples): per-file Downloads API example#193
jayozer wants to merge 1 commit into
browserbase:mainfrom
jayozer:examples/downloads-api

Conversation

@jayozer

@jayozer jayozer commented Aug 28, 2026

Copy link
Copy Markdown

What this adds

examples/downloads_api.py: an end-to-end example of Browserbase's per-file Downloads API, reached through the SDK's custom-request surface.

  1. Creates a session and connects Playwright over CDP.
  2. Arms Browser.setDownloadBehavior with behavior: "allow", downloadPath: "downloads", eventsEnabled: true (the configuration downloads need in order to sync; see Downloads example doesn't work #108).
  3. Triggers the same test download playwright_downloads.py uses.
  4. Polls GET /v1/downloads?sessionId=... until the file shows up in the listing (files sync a few seconds after the browser finishes writing them).
  5. Fetches that one file with GET /v1/downloads/{id} and Accept: application/octet-stream, and writes it to disk.
  6. Checks the bytes against the listing: length matches size, SHA-256 matches checksum.

It also adds a short paragraph to the README's Examples section saying that client.sessions.downloads.list(id) returns a zip archive of every file the session downloaded, and pointing at the new example for per-file access.

Why

sessions.downloads.list() is the SDK's only download call today. It returns one zip per session, and nothing in its description says so. The documented per-file endpoints (/v1/downloads: list, get, delete, with mime and size filters and pagination) are not in the generated SDK; #192 asks for them to be added to the spec. Until then, this example shows how to reach them with client.get() and make_request_options(), the same calls the generated resources use, so callers do not have to drop to raw httpx to get one file.

Notes for review

  • BinaryAPIResponse and make_request_options are imported from browserbase._response and browserbase._base_client; neither is re-exported from the package root. Pyright's reportPrivateUsage is off in this repo so lint is clean, but the example does depend on those paths.
  • The two small models (Download, DownloadListResponse) are written the way the generator would emit them (FieldInfo(alias=...)) and should be replaced by the generated types once the endpoints are in the spec.
  • The per-file API reports the original filename (sandstorm.mp3), while entries in the zip from sessions.downloads.list() carry a timestamp suffix (sandstorm-<ms>.mp3). The example accepts both when it picks the file to verify.
  • Only examples/downloads_api.py and README.md are touched. ./scripts/format and ./scripts/lint pass (ruff, pyright, mypy).
  • Run it with uv run python -m examples.downloads_api from the repo root. ./scripts/example <name> and the uv run python examples/<name>.py shebang put examples/ rather than the repo root on sys.path, so from examples import ... fails for every example in this checkout, not just this one. Left alone here as out of scope.

Refs #192

@jayozer
jayozer requested a review from a team as a code owner August 28, 2026 18:19

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 50f6c4678b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread examples/downloads_api.py
from pydantic import Field as FieldInfo
from playwright.sync_api import Playwright, sync_playwright

from examples import BROWSERBASE_PROJECT_ID, bb

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make the example runnable through the documented runner

The README directs users to ./scripts/example downloads_api, but that script executes uv run python examples/downloads_api.py; in that mode Python puts examples/ rather than the repository root on sys.path, so this absolute from examples ... import searches for a nested examples package and raises ModuleNotFoundError before the example starts. Invoke the example as a module or otherwise ensure the repository root is on the import path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accurate, but pre-existing rather than introduced here. Every file in examples/ uses the same from examples import ... line, and on main today ./scripts/example playwright_downloads fails with the identical ModuleNotFoundError, because uv run python examples/<name>.py puts examples/ rather than the repository root on sys.path. The fix belongs in scripts/example (for example uv run python -m "examples.$1"), which I kept out of this PR to limit it to the example and the README note. The PR description gives the working invocation, uv run python -m examples.downloads_api. If maintainers would rather have the one-line runner fix in this PR, I can add it.

sessions.downloads.list() returns one zip archive per session and its description does not say so. Browserbase also documents a per-file Downloads API under /v1/downloads (list, get, delete) that is not generated into the SDK yet.

Add an example that lists a session's downloads, fetches one file by id through client.get() and make_request_options(), writes it to disk, and checks the bytes against the listing's size and SHA-256 checksum. Note the zip return type in the README's Examples section and point at the new example for per-file access.

Refs browserbase#192
@jayozer
jayozer force-pushed the examples/downloads-api branch from 50f6c46 to 795649d Compare August 28, 2026 19:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant