browserless docs

browserless docs

  • Quick Start
  • Docker
  • Libraries
  • FAQ
  • Blog
  • Sign-up

›APIs

Hosted Service

  • Quick Start
  • How it works
  • GraphQL API
  • Best Practices
  • Terminology
  • Using your token

Docker

  • Quick Start
  • Configuration
  • Webhooks
  • Extending

APIs

  • /content
  • /download
  • /function
  • /pdf
  • /screencast
  • /screenshot
  • /scrape
  • /stats
  • /workspace

Libraries

  • Puppeteer
  • Playwright
  • Selenium
  • Capybara
  • .NET
  • Java
  • Python
  • Go (chromedp)

Recipes

  • Dealing with downloads
  • Using a Proxy
  • Watching sessions

Options

  • Launch Options
  • Using API /GET

/download API

Much like the /function API, the /download API allows you to POST code for browserless to execute. The only difference with /download is that, instead of returning the result of your function, we instead return a file that the browser downloads.

If your download request doesn't result in a file being downloaded, browserless will likely time-out the function.

Here's an example of downloading a file created in the browser (a CSV file):

const puppeteer = require('puppeteer');

async function run() {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();

    // Here we generate a CSV file and have the browser download it
    await page.evaluate(() => {
        const rows = [
            ["name1", "city1", "some other info"],
            ["name2", "city2", "more info"]
        ];
        let csvContent = "data:text/csv;charset=utf-8,";
        rows.forEach(function(rowArray){
            let row = rowArray.join(",");
            csvContent += row + "\r\n";
        }); 
        const encodedUri = encodeURI(csvContent);
        const link = document.createElement("a");
        link.setAttribute("href", encodedUri);
        link.setAttribute("download", "data.csv");
        document.body.appendChild(link);

        return link.click();
    });
}

run();

This might come as a surprise, but unfortunately in puppeteer there's no way to know if the file downloaded, or an API to even get it. You'll have to know ahead of time where files are kept, and watch the file-system for it to complete. Instead of wiring up all that code you can send browserless an HTTP request and it takes care of all the underlying file-system calls:

curl -X POST \
  https://chrome.browserless.io/download?token=YOUR-API-TOKEN \
  -H 'Content-Type: application/javascript' \
  -d 'module.exports = async ({ page }) => {
    await page.evaluate(() => {
        const rows = [
            ["name1", "city1", "some other info"],
            ["name2", "city2", "more info"]
        ];
        let csvContent = "data:text/csv;charset=utf-8,";
        rows.forEach(function(rowArray){
            let row = rowArray.join(",");
            csvContent += row + "\r\n";
        }); 
        const encodedUri = encodeURI(csvContent);
        const link = document.createElement("a");
        link.setAttribute("href", encodedUri);
        link.setAttribute("download", "data.csv");
        document.body.appendChild(link);

        return link.click();
    });
};'

This API is sensitive to the downloaded file, and will return an appropriate Content-Type with the response.

Parameterize-ing your /download call

You can also post a application/json body, as opposed to a application/javascript one, if you'd like to re-use your function with new parameters. In this case let's change our CSV file to be dynamic (note that our script is now minified for portability):

curl -X POST \
  https://chrome.browserless.io/download?token=YOUR-API-TOKEN \
  -H 'Content-Type: application/json' \
  -d '{
    "code": "module.exports=async({page:a,context:b})=>{const{rows:c}=b;await a.evaluate(()=>{let d='\''data:text/csv;charset=utf-8,'\'';c.forEach(function(g){let h=g.join('\'','\'');d+=h+'\''\r\n'\''});const e=encodeURI(d),f=document.createElement('\''a'\'');return f.setAttribute('\''href'\'',e),f.setAttribute('\''download'\'','\''data.csv'\''),document.body.appendChild(f),f.click()})};",
    "context": {
        "rows": [
            ["Some", "Cool", "CSV"],
            ["With", "Rows", "And Stuff"]
        ]
    }
}'

This lets you define re-usable functions and invoke them over HTTP in a more flexible way.

← /content/function →
browserless docs
Docs
Quick StartDocker DocsChrome API
Community
SlackTwitter
More
GitHubStar