Function API
Execute custom Puppeteer code via HTTP requests without installing libraries. Your function receives a page object (Puppeteer page) and optional context data from the JSON body. Return an object with data (Buffer, JSON, or plain text) and type (Content-Type string) to control the response.
Endpoint
- Method:
POST - Path:
/function - Auth:
tokenquery parameter (?token=) - Content-Type:
application/javascriptorapplication/json - Response:
*/*(based on function returntype)
See the OpenAPI reference for complete details.
Quickstart
The /function API accepts requests in two content types: application/javascript (raw code) and application/json (code + context data).
Sending code as Javascript
JS Code
export default async ({ page }) => {
await page.goto("https://books.toscrape.com/", { waitUntil: "networkidle2" });
const books = await page.$$eval(".product_pod", (nodes) =>
nodes.slice(0, 5).map((el) => ({
title: el.querySelector("h3 a").getAttribute("title"),
price: el.querySelector(".price_color").textContent,
}))
);
return {
data: { books },
type: "application/json",
};
};
- cURL
- Javascript
- Python
curl -s -X POST "https://production-sfo.browserless.io/function?token=YOUR_API_TOKEN_HERE" -H "Content-Type: application/javascript" -d 'export default async({page})=>{await page.goto("https://books.toscrape.com/",{waitUntil:"networkidle2"});const b=await page.$$eval(".product_pod",n=>n.slice(0,5).map(e=>({title:e.querySelector("h3 a").getAttribute("title"),price:e.querySelector(".price_color").textContent})));return{data:{books:b},type:"application/json"};}'
const TOKEN = "YOUR_API_TOKEN_HERE";
const url = `https://production-sfo.browserless.io/function?token=${TOKEN}`;
const code = `export default async ({ page }) => {
await page.goto("https://books.toscrape.com/", { waitUntil: "networkidle2" });
const books = await page.$$eval(".product_pod", (nodes) =>
nodes.slice(0, 5).map((el) => ({
title: el.querySelector("h3 a").getAttribute("title"),
price: el.querySelector(".price_color").textContent,
}))
);
return {
data: { books },
type: "application/json",
};
};`;
const response = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/javascript" },
body: code,
});
const result = await response.json();
console.log(result);
import requests
TOKEN = "YOUR_API_TOKEN_HERE"
url = f"https://production-sfo.browserless.io/function?token={TOKEN}"
code = """export default async ({ page }) => {
await page.goto("https://books.toscrape.com/", { waitUntil: "networkidle2" });
const books = await page.$$eval(".product_pod", (nodes) =>
nodes.slice(0, 5).map((el) => ({
title: el.querySelector("h3 a").getAttribute("title"),
price: el.querySelector(".price_color").textContent,
}))
);
return {
data: { books },
type: "application/json",
};
};"""
response = requests.post(
url,
headers={"Content-Type": "application/javascript"},
data=code,
)
result = response.json()
print(result)
Response
{
"data": {
"books": [
{ "title": "A Light in the Attic", "price": "£51.77" },
...
]
},
"type": "application/json"
}
Sending code as JSON
You can send a JSON payload with the following fields:
code: String, required — custom function code.context: Object, optional — value used to pass context values and arguments to thecode
JS Code
export default async ({ page, context }) => {
await page.goto(
`https://quotes.toscrape.com/page/${context.pageNumber}/`,
{ waitUntil: "networkidle2" }
);
const quotes = (
await page.$$eval(".quote", (nodes) =>
nodes.map((el) => ({
text: el.querySelector(".text").textContent,
author: el.querySelector(".author").textContent,
}))
)
).slice(0, context.maxQuotes);
return {
data: { page: context.pageNumber, quotes },
type: "application/json",
};
};
- cURL
- Javascript
- Python
curl -s -X POST "https://production-sfo.browserless.io/function?token=YOUR_API_TOKEN_HERE" -H "Content-Type: application/json" -d '{"code":"export default async({page,context})=>{await page.goto(\"https://quotes.toscrape.com/page/\"+context.pageNumber+\"/\",{waitUntil:\"networkidle2\"});const q=(await page.$$eval(\".quote\",n=>n.map(e=>({text:e.querySelector(\".text\").textContent,author:e.querySelector(\".author\").textContent})))).slice(0,context.maxQuotes);return{data:{page:context.pageNumber,quotes:q},type:\"application/json\"};}","context":{"pageNumber":2,"maxQuotes":3}}'
const TOKEN = "YOUR_API_TOKEN_HERE";
const url = `https://production-sfo.browserless.io/function?token=${TOKEN}`;
const code = `export default async ({ page, context }) => {
await page.goto(
\`https://quotes.toscrape.com/page/\${context.pageNumber}/\`,
{ waitUntil: "networkidle2" }
);
const quotes = (
await page.$$eval(".quote", (nodes) =>
nodes.map((el) => ({
text: el.querySelector(".text").textContent,
author: el.querySelector(".author").textContent,
}))
)
).slice(0, context.maxQuotes);
return {
data: { page: context.pageNumber, quotes },
type: "application/json",
};
};`;
const context = { pageNumber: 2, maxQuotes: 3 };
const response = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ code, context }),
});
const result = await response.json();
console.log(result);
import requests
TOKEN = "YOUR_API_TOKEN_HERE"
url = f"https://production-sfo.browserless.io/function?token={TOKEN}"
code = """export default async ({ page, context }) => {
await page.goto(
`https://quotes.toscrape.com/page/${context.pageNumber}/`,
{ waitUntil: "networkidle2" }
);
const quotes = (
await page.$$eval(".quote", (nodes) =>
nodes.map((el) => ({
text: el.querySelector(".text").textContent,
author: el.querySelector(".author").textContent,
}))
)
).slice(0, context.maxQuotes);
return {
data: { page: context.pageNumber, quotes },
type: "application/json",
};
};"""
context = {"pageNumber": 2, "maxQuotes": 3}
response = requests.post(
url,
headers={"Content-Type": "application/json"},
json={"code": code, "context": context},
)
result = response.json()
print(result)
Response
{
"data": {
"page": 2,
"quotes": [
{
"text": "\u201cThere are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.\u201d",
"author": "Albert Einstein"
},
...
]
},
"type": "application/json"
}
Debug your code
Before sending your code through the /function API, you can test and troubleshoot it using our Live Debugger feature. This powerful tool allows you to write and execute your Puppeteer code in a browser environment, making it easy to iterate and debug before making API calls.
You can access the Live Debugger directly from your account page. Once your code is working as expected, simply copy and paste it as the request body and send it with the application/javascript content-type header to the /function endpoint.
For more details on how to use the Live Debugger and its features, see our Live Debugger documentation.
Importing libraries
Since the /function 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 () {
const Internet = faker.internet;
const rndData = [...Array(5)].map(() => ({
domain: Internet.domainName(),
ip: Internet.ip(),
mac: Internet.mac(),
protocol: Internet.protocol(),
}));
return {
data: { domains: rndData },
type: "application/json",
};
}
cURL
curl -X POST "https://production-sfo.browserless.io/function?token=YOUR_API_TOKEN_HERE" -H 'Content-Type: application/javascript' -d 'import{faker}from"https://esm.sh/@faker-js/faker";export default async function(){const I=faker.internet;const d=[...Array(5)].map(()=>({domain:I.domainName(),ip:I.ip(),mac:I.mac(),protocol:I.protocol()}));return{data:{domains:d},type:"application/json"};};'
Returning files
The /function API can return other file types beyond JSON, such as PDF files. In this example, we'll generate a PDF of Hacker News using the built-in page.pdf() method.
JS Code
export default async ({ page }) => {
await page.goto("https://news.ycombinator.com/", { waitUntil: "networkidle2" });
return await page.pdf({ printBackground: true });
};
cURL
curl -s -X POST "https://production-sfo.browserless.io/function?token=YOUR_API_TOKEN_HERE" -H "Content-Type: application/javascript" -d 'export default async({page})=>{await page.goto("https://news.ycombinator.com/",{waitUntil:"networkidle2"});return await page.pdf({printBackground:true});}' -o hn.pdf
Exception handling
When working with the /function API, you can catch exceptions and handle errors gracefully. This is particularly useful for debugging and providing meaningful error responses when automation fails.
The following example shows how to implement error handling, allowing you to:
- Capture the error message when an automation step fails
- Take a screenshot when an error occurs for visual debugging
- Handle screenshot capture errors gracefully
- Return both successful data and error information in a structured format
JS Code
export default async ({ page }) => {
let error;
let title;
try {
await page.goto("https://books.toscrape.com/", { waitUntil: "networkidle2" });
title = await page.title();
await page.click(".nonexistent");
} catch (err) {
error = { message: err.message };
try {
error.screenshot = await page.screenshot({ encoding: "base64" });
} catch (_) {}
}
return {
data: { title, error },
type: "application/json",
};
};
cURL
curl -s -X POST "https://production-sfo.browserless.io/function?token=YOUR_API_TOKEN_HERE" -H "Content-Type: application/javascript" -d 'export default async({page})=>{let e,t;try{await page.goto("https://books.toscrape.com/",{waitUntil:"networkidle2"});t=await page.title();await page.click(".nonexistent");}catch(r){e={message:r.message};try{e.screenshot=await page.screenshot({encoding:"base64"});}catch(x){};}return{data:{title:t,error:e},type:"application/json"};};'
The /function API, like all REST APIs, automatically closes the browser session after execution completes. If you need to persist sessions or keep browsers alive between requests, consider using BaaS with session management or BrowserQL with the reconnect mutation.