Download API
Run custom Puppeteer code and return files that Chrome downloads during execution. The response includes appropriate Content-Type and Content-Disposition headers. You can load external libraries via import and use ESM modules that run inside the browser context.
Endpoint
- Method:
POST - Path:
/download - Auth:
tokenquery parameter (?token=) - Content-Type:
application/javascriptorapplication/json - Response:
*/*(based on downloaded file)
See the OpenAPI reference for complete details.
Quickstart
The /download API accepts requests in two content types: application/javascript (raw code) and application/json (code + context data).
Sending code as Javascript
This example navigates to a page with CSV sample files, clicks the first .csv download link, and waits for the file to download.
JS Code
export default async ({ page }) => {
await page.goto("https://filesamples.com/formats/csv", {
waitUntil: "networkidle2",
});
await page.click('a[href*=".csv"]');
await new Promise((r) => setTimeout(r, 5000));
};
- cURL
- Javascript
- Python
curl -s -X POST "https://production-sfo.browserless.io/download?token=YOUR_API_TOKEN_HERE" -H "Content-Type: application/javascript" -d 'export default async({page})=>{await page.goto("https://filesamples.com/formats/csv",{waitUntil:"networkidle2"});await page.click("a[href*=\".csv\"]");await new Promise(r=>setTimeout(r,5000));}' -o sample.csv
const TOKEN = "YOUR_API_TOKEN_HERE";
const url = `https://production-sfo.browserless.io/download?token=${TOKEN}`;
const code = `export default async ({ page }) => {
await page.goto("https://filesamples.com/formats/csv", {
waitUntil: "networkidle2",
});
await page.click('a[href*=".csv"]');
await new Promise((r) => setTimeout(r, 5000));
};`;
const response = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/javascript" },
body: code,
});
const blob = await response.blob();
console.log("Downloaded", blob.size, "bytes");
import requests
TOKEN = "YOUR_API_TOKEN_HERE"
url = f"https://production-sfo.browserless.io/download?token={TOKEN}"
code = """export default async ({ page }) => {
await page.goto("https://filesamples.com/formats/csv", {
waitUntil: "networkidle2",
});
await page.click('a[href*=".csv"]');
await new Promise((r) => setTimeout(r, 5000));
};"""
response = requests.post(
url,
headers={"Content-Type": "application/javascript"},
data=code,
)
with open("sample.csv", "wb") as f:
f.write(response.content)
print("Saved sample.csv")
Sending code as JSON
You can send a JSON payload with the following fields:
code: String, required — your JavaScript code as a stringcontext: Object, optional — variables to pass to your function
This example uses context to pass the target URL, navigates to the page, and clicks the first .csv download link.
JS Code
export default async ({ page, context }) => {
await page.goto(context.url, { waitUntil: "networkidle2" });
await page.click('a[href*=".csv"]');
await new Promise((r) => setTimeout(r, 5000));
};
- cURL
- Javascript
- Python
curl -s -X POST "https://production-sfo.browserless.io/download?token=YOUR_API_TOKEN_HERE" -H "Content-Type: application/json" -d '{"code":"export default async({page,context})=>{await page.goto(context.url,{waitUntil:\"networkidle2\"});await page.click(\"a[href*=\\\".csv\\\"]\");await new Promise(r=>setTimeout(r,5000));}","context":{"url":"https://filesamples.com/formats/csv"}}' -o sample.csv
const TOKEN = "YOUR_API_TOKEN_HERE";
const url = `https://production-sfo.browserless.io/download?token=${TOKEN}`;
const payload = {
code: `export default async ({ page, context }) => {
await page.goto(context.url, { waitUntil: "networkidle2" });
await page.click('a[href*=".csv"]');
await new Promise((r) => setTimeout(r, 5000));
};`,
context: { url: "https://filesamples.com/formats/csv" },
};
const response = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
const blob = await response.blob();
console.log("Downloaded", blob.size, "bytes");
import requests
import json
TOKEN = "YOUR_API_TOKEN_HERE"
url = f"https://production-sfo.browserless.io/download?token={TOKEN}"
payload = {
"code": """export default async ({ page, context }) => {
await page.goto(context.url, { waitUntil: "networkidle2" });
await page.click('a[href*=".csv"]');
await new Promise((r) => setTimeout(r, 5000));
};""",
"context": {"url": "https://filesamples.com/formats/csv"},
}
response = requests.post(
url,
headers={"Content-Type": "application/json"},
data=json.dumps(payload),
)
with open("sample.csv", "wb") as f:
f.write(response.content)
print("Saved sample.csv")
Importing libraries
Since the /download API uses ESM modules, you can use import syntax over HTTP to load modules. For instance, let's try loading the Faker module.
JS Code
import { faker } from "https://esm.sh/@faker-js/faker";
export default async function ({ page }) {
const rndName = faker.person.fullName();
const rndEmail = faker.internet.email();
await page.evaluate(
(name, email) => {
const jsonStr = JSON.stringify({ name, email });
const jsonContent = `data:application/json,${jsonStr}`;
const encodedUri = encodeURI(jsonContent);
const link = document.createElement("a");
link.setAttribute("href", encodedUri);
link.setAttribute("download", "data.json");
document.body.appendChild(link);
return link.click();
},
rndName,
rndEmail
);
}
cURL
curl -X POST "https://production-sfo.browserless.io/download?token=YOUR_API_TOKEN_HERE" -H 'Content-Type: application/javascript' -d 'import{faker}from"https://esm.sh/@faker-js/faker";export default async function({page}){const n=faker.person.fullName();const e=faker.internet.email();await page.evaluate((name,email)=>{const j=JSON.stringify({name,email});const c=`data:application/json,${j}`;const u=encodeURI(c);const l=document.createElement("a");l.setAttribute("href",u);l.setAttribute("download","data.json");document.body.appendChild(l);return l.click();},n,e);}'
Response
{
"name": "Jasmine Littel",
"email": "Giovanna26@hotmail.com"
}
Generate and download a file
If there isn't a file on the site that you need to download, you can create the content you want to download and then trigger a download by creating a link element on the page.
This example navigates to Hacker News, scrapes the top 10 story titles and URLs, creates a JSON blob, and triggers a download.
JS Code
export default async ({ page }) => {
await page.goto("https://news.ycombinator.com/", { waitUntil: "networkidle2" });
const stories = await page.$$eval(".athing", (rows) =>
rows.slice(0, 10).map((r) => ({
title: r.querySelector(".titleline > a")?.textContent || "",
url: r.querySelector(".titleline > a")?.href || "",
}))
);
await page.evaluate((d) => {
const a = document.createElement("a");
a.href = URL.createObjectURL(
new Blob([JSON.stringify(d, null, 2)], { type: "application/json" })
);
a.download = "hn.json";
document.body.appendChild(a);
a.click();
}, stories);
await new Promise((r) => setTimeout(r, 2000));
};
cURL
curl -s -X POST "https://production-sfo.browserless.io/download?token=YOUR_API_TOKEN_HERE" -H "Content-Type: application/javascript" -d 'export default async({page})=>{await page.goto("https://news.ycombinator.com/",{waitUntil:"networkidle2"});const s=await page.$$eval(".athing",r=>r.slice(0,10).map(r=>({title:r.querySelector(".titleline > a")?.textContent||"",url:r.querySelector(".titleline > a")?.href||""})));await page.evaluate(d=>{const a=document.createElement("a");a.href=URL.createObjectURL(new Blob([JSON.stringify(d,null,2)],{type:"application/json"}));a.download="hn.json";document.body.appendChild(a);a.click()},s);await new Promise(r=>setTimeout(r,2000));}' -o hn.json
Response
[
{
"title": "Show HN: A open-source tool for building AI agents",
"url": "https://example.com/article1"
},
{
"title": "Why Rust is the future of systems programming",
"url": "https://example.com/article2"
}
]
You can also send the code as a JSON payload. This example scrapes book data from Books to Scrape, builds a CSV string, and uses context to pass the page number.
JS Code
export default async ({ page, context }) => {
await page.goto(
"https://books.toscrape.com/catalogue/page-" + context.pageNumber + ".html",
{ waitUntil: "networkidle2" }
);
const books = await page.$$eval(".product_pod", (nodes) =>
nodes.map((e) => ({
title: e.querySelector("h3 a").getAttribute("title"),
price: e.querySelector(".price_color").textContent,
}))
);
const csv = ["Title,Price", ...books.map((x) => x.title + "," + x.price)].join(
"\n"
);
await page.evaluate((d) => {
const a = document.createElement("a");
a.href = URL.createObjectURL(new Blob([d], { type: "text/csv" }));
a.download = "books.csv";
document.body.appendChild(a);
a.click();
}, csv);
await new Promise((r) => setTimeout(r, 2000));
};
cURL
curl -s -X POST "https://production-sfo.browserless.io/download?token=YOUR_API_TOKEN_HERE" -H "Content-Type: application/json" -d '{"code":"export default async({page,context})=>{await page.goto(\"https://books.toscrape.com/catalogue/page-\"+context.pageNumber+\".html\",{waitUntil:\"networkidle2\"});const b=await page.$$eval(\".product_pod\",n=>n.map(e=>({title:e.querySelector(\"h3 a\").getAttribute(\"title\"),price:e.querySelector(\".price_color\").textContent})));const csv=[\"Title,Price\",...b.map(x=>x.title+\",\"+x.price)].join(\"\\n\");await page.evaluate(d=>{const a=document.createElement(\"a\");a.href=URL.createObjectURL(new Blob([d],{type:\"text/csv\"}));a.download=\"books.csv\";document.body.appendChild(a);a.click()},csv);await new Promise(r=>setTimeout(r,2000));}","context":{"pageNumber":1}}' -o books.csv
Response
Title,Price
A Light in the Attic,£51.77
Tipping the Velvet,£53.74
Soumission,£50.10