Upload and download files
Send a local file into an <input type="file"> and stream completed downloads back to your machine, without sharing a filesystem with the remote browser.
- A Browserless API token from your account dashboard
Upload a file
Send a file from your local machine to a file input on the remote page. Browserless handles the transfer so the page's upload handlers run as if a user picked the file.
- AI Agent
- Puppeteer
- Playwright
Use the Browserless MCP server to upload files from any MCP-compatible AI agent (Claude Desktop, Cursor, Windsurf, ChatGPT, etc.).
1. Connect the MCP server
Send this prompt to your AI agent to install the Browserless MCP server:
Go to https://github.com/browserless/browserless-mcp/blob/main/install.md
and follow the instructions to install the Browserless MCP server
for my client.
2. Upload a file
Use browserless_agent. Uploading requires interaction: attaching a file to an input and submitting a form.
Use the browserless_agent tool to go to https://example.com/upload,
upload /absolute/path/to/file.ext to the file input, and submit the form.
1. Install dependencies
npm install puppeteer-core
2. Connect and upload
import fs from "fs";
import path from "path";
import puppeteer from "puppeteer-core";
const TOKEN = "YOUR_API_TOKEN_HERE";
const fileToUpload = {
name: "image.png",
content: fs
.readFileSync(path.join(process.cwd(), "image.png"))
.toString("base64"),
mimeType: "image/png",
};
const browser = await puppeteer.connect({
browserWSEndpoint: `wss://production-sfo.browserless.io?token=${TOKEN}`,
});
try {
const page = await browser.newPage();
await page.goto("https://jimpl.com/");
await page.waitForSelector("input[type=file]");
const cdp = await page.createCDPSession();
const result = await cdp.send("Browserless.uploadFile", {
selector: "input[type=file]",
files: [fileToUpload],
});
console.log(result);
} finally {
// Always close to release the session even on error.
await browser.close();
}
3. Check the output
The call returns { ok: true }. On failure, ok is false with an error such as SelectorNotFound, InvalidTarget, or FileTooLarge (the combined decoded size is capped at 100 MB).
1. Install dependencies
npm install playwright-core
2. Connect and upload
import fs from "fs";
import path from "path";
import { chromium } from "playwright-core";
const TOKEN = "YOUR_API_TOKEN_HERE";
const fileToUpload = {
name: "image.png",
content: fs
.readFileSync(path.join(process.cwd(), "image.png"))
.toString("base64"),
mimeType: "image/png",
};
// connectOverCDP uses the raw CDP endpoint, which is where the custom
// Browserless.* methods live — the Playwright protocol server doesn't expose them.
const browser = await chromium.connectOverCDP(
`wss://production-sfo.browserless.io?token=${TOKEN}`,
);
try {
const context = browser.contexts()[0];
const page = context.pages()[0] ?? (await context.newPage());
await page.goto("https://jimpl.com/");
await page.waitForSelector("input[type=file]");
const cdp = await context.newCDPSession(page);
const result = await cdp.send("Browserless.uploadFile", {
selector: "input[type=file]",
files: [fileToUpload],
});
console.log(result);
} finally {
// Always close to release the session even on error.
await browser.close();
}
3. Check the output
The call returns { ok: true }. On failure, ok is false with an error such as SelectorNotFound, InvalidTarget, or FileTooLarge (the combined decoded size is capped at 100 MB).
Download a file
Trigger a download in the remote browser and stream the completed file back to your machine.
- AI Agent
- Puppeteer
- Playwright
Use the Browserless MCP server to download files from any MCP-compatible AI agent (Claude Desktop, Cursor, Windsurf, ChatGPT, etc.).
1. Connect the MCP server
Send this prompt to your AI agent to install the Browserless MCP server:
Go to https://github.com/browserless/browserless-mcp/blob/main/install.md
and follow the instructions to install the Browserless MCP server
for my client.
2. Download a file
Use browserless_agent. Downloading requires interaction: navigating to the page, clicking a download trigger, and saving the result.
Use the browserless_agent tool to go to https://example.com/reports,
click the latest CSV export, and save it to ~/Desktop.
1. Install dependencies
npm install puppeteer-core
2. Connect and capture the download
import fs from "fs";
import path from "path";
import puppeteer from "puppeteer-core";
const TOKEN = "YOUR_API_TOKEN_HERE";
const browser = await puppeteer.connect({
browserWSEndpoint: `wss://production-sfo.browserless.io?token=${TOKEN}`,
});
try {
const page = await browser.newPage();
const cdp = await page.createCDPSession();
// Off by default — download bytes count against data transfer, so opt in.
await cdp.send("Browserless.setDownloadEnabled", { enabled: true });
// Register before the click so the completed download can't be missed.
cdp.on("Browserless.fileDownloaded", ({ filename, data }) => {
const buffer = Buffer.from(data, "base64");
fs.writeFileSync(path.join(process.cwd(), filename), buffer);
console.log(`saved: ${filename} (${buffer.length} bytes)`);
});
await page.goto("https://scraping-sandbox.netlify.app/downloadsamples");
await page.click('a[href="/samples/sample.json"]');
// Give the download time to finish before closing the connection.
await new Promise((res) => setTimeout(res, 3000));
} finally {
// Always close to release the session even on error.
await browser.close();
}
3. Check the output
The Browserless.fileDownloaded event carries filename, mimeType, size, and base64 data. It fires once per completed download.
1. Install dependencies
npm install playwright-core
2. Connect and save the download
import { chromium } from "playwright-core";
const TOKEN = "YOUR_API_TOKEN_HERE";
const browser = await chromium.connect(
`wss://production-sfo.browserless.io/chromium/playwright?token=${TOKEN}`,
);
try {
const page = await browser.newPage();
await page.goto("https://scraping-sandbox.netlify.app/downloadsamples");
// Register before clicking so the download can't be missed.
const downloadPromise = page.waitForEvent("download");
await page.click('a[href="/samples/sample.json"]');
const download = await downloadPromise;
// Saves on the machine running this script, not the remote browser.
await download.saveAs(download.suggestedFilename());
console.log(`saved: ${download.suggestedFilename()}`);
} finally {
// Always close to release the session even on error.
await browser.close();
}
3. Check the output
download.saveAs() writes the file to the machine running the script. Use download.suggestedFilename() for the browser's filename, or pass your own path.
Next steps
- File Transfers feature guide — full parameter and event reference
- Fill and Submit a Form — automate the inputs around a file upload
- Extract and Download Images — pull every image off a page