sample.pdf becomes sample-1719265797164.pdf).
View or run the example template here
Triggering downloads
First, trigger a download in your browser automation:- Create a browser session and get the session ID
- Connect to the session using your preferred framework
- Configure your library’s downloads location
- Perform the download action in your automation script
- Node.js
- Python
import { chromium } from "playwright-core";
import { Browserbase } from "@browserbasehq/sdk";
(async () => {
const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY! });
const session = await bb.sessions.create();
const browser = await chromium.connectOverCDP(session.connectUrl);
const defaultContext = browser.contexts()[0];
const page = defaultContext.pages()[0];
// Required to avoid playwright overriding location
const client = await defaultContext.newCDPSession(page);
await client.send("Browser.setDownloadBehavior", {
behavior: "allow",
downloadPath: "downloads",
eventsEnabled: true,
});
await page.goto("https://browser-tests-alpha.vercel.app/api/download-test");
const [download] = await Promise.all([
page.waitForEvent("download"),
page.locator("#download").click(),
]);
let downloadError = await download.failure();
if (downloadError !== null) {
console.log("Error happened on download:", downloadError);
throw new Error(downloadError);
}
// Store the session ID to retrieve downloads later
console.log("Download completed. Session ID:", session.id);
await page.close();
await browser.close();
})().catch((error) => console.error(error.message));
import puppeteer from "puppeteer-core";
import { Browserbase } from "@browserbasehq/sdk";
(async () => {
const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY! });
const session = await bb.sessions.create();
const browser = await puppeteer.connect({
browserWSEndpoint: session.connectUrl,
});
const pages = await browser.pages();
const page = pages[0];
const client = await page.createCDPSession();
await client.send("Browser.setDownloadBehavior", {
behavior: "allow",
downloadPath: "downloads",
eventsEnabled: true,
});
await page.goto("https://browser-tests-alpha.vercel.app/api/download-test");
const [downloadPath] = await Promise.all([
// Puppeteer doesn't have a download event, so we need to use the CDP session to listen for the download progress
new Promise<string>(resolve => {
client.on('Browser.downloadProgress', event => {
if (event.state === 'completed') {
resolve(event.guid);
}
});
}),
page.locator("#download").click(),
]);
// Store the session ID to retrieve downloads later
console.log("Download completed. Session ID:", session.id);
await page.close();
await browser.close();
})().catch((error) => console.error(error.message));
import http from "http";
import { Builder, By } from "selenium-webdriver";
import { Browserbase } from "@browserbasehq/sdk";
(async () => {
const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY! });
const session = await bb.sessions.create();
// Create a custom connection with the session details
const customHttpAgent = new http.Agent({});
(customHttpAgent as any).addRequest = (req: any, options: any) => {
req.setHeader("x-bb-signing-key", session.signingKey);
(http.Agent.prototype as any).addRequest.call(customHttpAgent, req, options);
};
const driver = new Builder()
.forBrowser("chrome")
.usingHttpAgent(customHttpAgent)
.usingServer(
session.seleniumRemoteUrl
)
.build();
// Head to the test downloads page
await driver.get("https://browser-tests-alpha.vercel.app/api/download-test");
// Click the download button
console.log("Downloading file to the remote browser...");
await driver.findElement(By.id("download")).click();
// Store the session ID to retrieve downloads later
console.log("Download completed. Session ID:", session.id);
// Clean up
await driver.quit();
})().catch((error) => console.error(error.message));
from playwright.sync_api import sync_playwright
from browserbase import Browserbase
import os
bb = Browserbase(api_key=os.environ["BROWSERBASE_API_KEY"])
session = bb.sessions.create()
print(f"Session ID: {session.id}")
with sync_playwright() as playwright: # Use the session ID to connect to the browser
browser = playwright.chromium.connect_over_cdp(session.connect_url)
# Required to avoid playwright overriding the location
cdp_session = browser.new_browser_cdp_session()
cdp_session.send(
"Browser.setDownloadBehavior",
{
"behavior": "allow",
"downloadPath": "downloads",
"eventsEnabled": True,
},
)
context = browser.contexts[0]
page = context.pages[0]
# Head to the test downloads page
page.goto("https://browser-tests-alpha.vercel.app/api/download-test")
# Click the download button
print("Downloading file to the remote browser...")
page.get_by_role("link", name="Download File").click()
# Clean up
page.close()
browser.close()
print("Download completed. Use the session ID to retrieve the files.")
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.remote_connection import RemoteConnection
from browserbase import Browserbase
import os
bb = Browserbase(api_key=os.environ["BROWSERBASE_API_KEY"])
session = bb.sessions.create()
print(f"Session ID: {session.id}")
# Create a custom connection with the session details
class CustomRemoteConnection(RemoteConnection):
def get_remote_connection_headers(self, parsed_url, keep_alive=False):
headers = super().get_remote_connection_headers(parsed_url, keep_alive)
headers["x-bb-api-key"] = os.environ["BROWSERBASE_API_KEY"]
headers["session-id"] = session.id
return headers
custom_conn = CustomRemoteConnection(session.selenium_remote_url)
driver = webdriver.Remote(
command_executor=custom_conn,
options=webdriver.ChromeOptions()
)
# Head to the test downloads page
driver.get("https://browser-tests-alpha.vercel.app/api/download-test")
# Click the download button
print("Downloading file to the remote browser...")
driver.find_element(By.ID, "download").click()
# Clean up
driver.quit()
print("Download completed. Use the session ID to retrieve the files.")
Critical:
setDownloadBehavior ConfigurationWhen using Playwright or Puppeteer, you must call Browser.setDownloadBehavior via CDP to ensure downloads are synced to Browserbase’s storage. Pay special attention to the downloadPath parameter. It must be set to "downloads" exactly as shown in the examples above.Common misconfiguration issues:- Using an absolute path (e.g.,
/tmp/downloads) instead of"downloads" - Omitting the
setDownloadBehaviorcall entirely - Setting
behaviorto something other than"allow"
Retrieving downloaded files
After triggering downloads, retrieve them using the Downloads API. The API provides granular access to individual downloaded files. You can list, filter, retrieve, and delete downloads. Filenames don’t include the timestamp suffix Browserbase adds during storage.Files sync in real time, but large downloads may not be immediately available
through the
/downloads endpoint. The code below includes retry logic to
handle this.List downloads
List all downloads for a session with optional filtering by filename, MIME type, file size, and creation time.- Node.js
- Python
Node
import { writeFileSync } from "node:fs";
const API_KEY = process.env.BROWSERBASE_API_KEY!;
async function saveDownloadsOnDisk(sessionId: string, retryForSeconds: number) {
const endTime = Date.now() + retryForSeconds * 1000;
while (Date.now() < endTime) {
try {
// List individual downloads for the session
const listResponse = await fetch(
`https://api.browserbase.com/v1/downloads?sessionId=${sessionId}`,
{ headers: { "x-bb-api-key": API_KEY } }
);
const { downloads, total } = await listResponse.json();
if (total > 0) {
console.log(`Found ${total} download(s)`);
for (const download of downloads) {
// Download each file individually
const fileResponse = await fetch(
`https://api.browserbase.com/v1/downloads/${download.id}`,
{
headers: {
"x-bb-api-key": API_KEY,
Accept: "application/octet-stream",
},
}
);
const buffer = Buffer.from(await fileResponse.arrayBuffer());
writeFileSync(download.filename, buffer);
console.log(`Saved: ${download.filename} (${download.size} bytes)`);
}
return;
}
} catch (e) {
console.error("Error fetching downloads:", e);
throw e;
}
// Wait 2 seconds before retrying
await new Promise((resolve) => setTimeout(resolve, 2000));
}
throw new Error("No downloads found within the retry period");
}
(async () => {
// Use the session ID from your browser automation to retrieve downloads
const sessionId = "your-session-id";
await saveDownloadsOnDisk(sessionId, 20); // wait up to 20s
console.log("Downloads complete");
})().catch((error) => {
console.error("Download failed:", error);
});
Python
import os
import time
import requests
API_KEY = os.environ["BROWSERBASE_API_KEY"]
def save_downloads_on_disk(session_id: str, retry_seconds: int = 20):
"""
List and download individual files from a session.
Retries for the specified number of seconds if no downloads are found.
:param session_id: The session ID from your browser automation
:param retry_seconds: How long to retry if no downloads are found
"""
end_time = time.time() + retry_seconds
while time.time() < end_time:
try:
# List individual downloads for the session
list_response = requests.get(
"https://api.browserbase.com/v1/downloads",
params={"sessionId": session_id},
headers={"x-bb-api-key": API_KEY},
)
data = list_response.json()
if data["total"] > 0:
print(f"Found {data['total']} download(s)")
for download in data["downloads"]:
# Download each file individually
file_response = requests.get(
f"https://api.browserbase.com/v1/downloads/{download['id']}",
headers={
"x-bb-api-key": API_KEY,
"Accept": "application/octet-stream",
},
)
with open(download["filename"], "wb") as f:
f.write(file_response.content)
print(f"Saved: {download['filename']} ({download['size']} bytes)")
return
except Exception as e:
print(f"Error fetching downloads: {e}")
raise
time.sleep(2) # Wait 2 seconds before retrying
raise TimeoutError("No downloads found within the retry period")
# Use the session ID from your browser automation to retrieve downloads
session_id = "your-session-id"
try:
save_downloads_on_disk(session_id)
print("Downloads complete")
except Exception as e:
print(f"Failed to retrieve downloads: {e}")
Filtering options
| Parameter | Type | Description |
|---|---|---|
sessionId | string | Required. The session ID to list downloads for. |
filename | string | Filter by exact filename match. |
mimeType | string | Filter by MIME type (e.g., application/pdf). |
minSize | number | Minimum file size in bytes. |
maxSize | number | Maximum file size in bytes. |
createdAfter | string | Filter downloads created after this timestamp (ISO 8601). |
createdBefore | string | Filter downloads created before this timestamp (ISO 8601). |
limit | number | Maximum results to return (1-100, default: 20). |
offset | number | Number of results to skip for pagination. |
- Node.js
- Python
Node
const API_KEY = process.env.BROWSERBASE_API_KEY!;
const sessionId = "your-session-id";
// Find PDF files larger than 1KB created in the last hour
const params = new URLSearchParams({
sessionId,
mimeType: "application/pdf",
minSize: "1024",
createdAfter: new Date(Date.now() - 60 * 60 * 1000).toISOString(),
limit: "10",
});
const response = await fetch(
`https://api.browserbase.com/v1/downloads?${params}`,
{
headers: { "x-bb-api-key": API_KEY },
}
);
const { downloads } = await response.json();
console.log(`Found ${downloads.length} matching PDFs`);
Python
import os
from datetime import datetime, timedelta
import requests
API_KEY = os.environ["BROWSERBASE_API_KEY"]
session_id = "your-session-id"
# Find PDF files larger than 1KB created in the last hour
response = requests.get(
"https://api.browserbase.com/v1/downloads",
params={
"sessionId": session_id,
"mimeType": "application/pdf",
"minSize": 1024,
"createdAfter": (datetime.now() - timedelta(hours=1)).isoformat(),
"limit": 10,
},
headers={"x-bb-api-key": API_KEY}
)
downloads = response.json()["downloads"]
print(f"Found {len(downloads)} matching PDFs")
Get a download
Retrieve metadata or file content for a specific download. UseAccept: application/json for metadata, or Accept: application/octet-stream to download the file (default if no Accept header is provided).
- Node.js
- Python
Node
import { writeFileSync } from "node:fs";
const API_KEY = process.env.BROWSERBASE_API_KEY!;
const downloadId = "download-uuid";
// Get metadata
const metadataResponse = await fetch(
`https://api.browserbase.com/v1/downloads/${downloadId}`,
{
headers: {
"x-bb-api-key": API_KEY,
Accept: "application/json",
},
}
);
const metadata = await metadataResponse.json();
console.log(`File: ${metadata.filename} (${metadata.size} bytes)`);
// Download file content
const fileResponse = await fetch(
`https://api.browserbase.com/v1/downloads/${downloadId}`,
{
headers: {
"x-bb-api-key": API_KEY,
Accept: "application/octet-stream",
},
}
);
const buffer = Buffer.from(await fileResponse.arrayBuffer());
writeFileSync(metadata.filename, buffer);
Python
import os
import requests
API_KEY = os.environ["BROWSERBASE_API_KEY"]
download_id = "download-uuid"
# Get metadata
metadata_response = requests.get(
f"https://api.browserbase.com/v1/downloads/{download_id}",
headers={
"x-bb-api-key": API_KEY,
"Accept": "application/json"
}
)
metadata = metadata_response.json()
print(f"File: {metadata['filename']} ({metadata['size']} bytes)")
# Download file content
file_response = requests.get(
f"https://api.browserbase.com/v1/downloads/{download_id}",
headers={
"x-bb-api-key": API_KEY,
"Accept": "application/octet-stream"
}
)
with open(metadata["filename"], "wb") as f:
f.write(file_response.content)
Delete a download
Remove a download from storage. Returns204 No Content on success.
- Node.js
- Python
Node
const API_KEY = process.env.BROWSERBASE_API_KEY!;
const downloadId = "download-uuid";
const response = await fetch(
`https://api.browserbase.com/v1/downloads/${downloadId}`,
{
method: "DELETE",
headers: { "x-bb-api-key": API_KEY },
}
);
if (response.status === 204) {
console.log("Download deleted");
}
Python
import os
import requests
API_KEY = os.environ["BROWSERBASE_API_KEY"]
download_id = "download-uuid"
response = requests.delete(
f"https://api.browserbase.com/v1/downloads/{download_id}",
headers={"x-bb-api-key": API_KEY}
)
if response.status_code == 204:
print("Download deleted")
Download object
Each download contains the following fields:| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for the download. |
sessionId | string | The session ID this download belongs to. |
filename | string | The filename of the downloaded file. |
mimeType | string | The MIME type of the file. |
size | number | File size in bytes. |
checksum | string | SHA256 checksum of the file. |
createdAt | string | Timestamp when the file was downloaded (ISO 8601). |
List downloads
List and filter downloads
Get download
Get metadata or file content
Delete download
Remove a download