Run Puppeteer as a service, with no Chrome to install or scale
Puppeteer as a service means you keep writing Puppeteer and stop running Chrome. Scrapers that need clicks, QA checks, data pulls from interactive pages and one-off automations all need a browser only for the seconds the script runs. With Browser Functions you send the function with a URL and get its return value back.
Headless Chrome is the heaviest dependency in a serverless stack
A Chromium build is too large for most function bundles, needs system fonts and libraries the runtime does not ship, and cold-starts slowly. On a server it leaks memory, leaves zombie processes behind after a crash and has to be upgraded in step with Puppeteer.
Serverless Puppeteer workarounds trade one problem for another: trimmed Chromium builds pinned to old versions, layers that break on the next runtime upgrade, or a browser pool you now size, monitor and restart. None of it is the script you actually wanted to run.
The function method moves the browser behind the request. When your function references page, Microlink starts a headless browser, navigates to the URL and calls your function with the full Puppeteer Page. It returns whatever your function returns, plus profiling, and the browser is destroyed afterwards.
How to run Puppeteer as a service with microlink.function
Write the function as you would locally, but receive page instead of launching a browser. The navigation to the URL has already happened when your code starts.
import createClient from 'microlink.io'
const microlink = createClient({
apiKey: process.env.MICROLINK_API_KEY
})
const { isFulfilled, value } = await microlink.function(
'https://news.ycombinator.com',
({ page }) =>
page.$$eval('.titleline > a', links =>
links.slice(0, 5).map(a => ({ title: a.textContent, url: a.href }))
)
)The function receives page after navigation, along with url, headers and response. value is the array your function returned, and isFulfilled tells you it completed without throwing.
import createClient from 'microlink.io'
const microlink = createClient({
apiKey: process.env.MICROLINK_API_KEY
})
const { value } = await microlink.function(
'https://example.com/pricing',
({ page, selector }) => page.$$eval(selector, rows => rows.map(row => row.innerText)),
{ waitForSelector: '.plan', selector: '.plan' }
)API options such as waitForSelector, click or scripts prepare the page before your code runs. Any option that is not an API parameter, like selector here, arrives as a named argument.
curl 'https://api.microlink.io/?url=https%3A%2F%2Fexample.com&function=%28%7B+page+%7D%29+%3D%3E+page.title%28%29&meta=false'Without the SDK, send the function as a string in the function parameter. The result is under data.function with isFulfilled, value, profiling and logging.
- function The JavaScript to run. The SDK serializes and compresses it for you.
- waitForSelector Waits for an element before the function is called.
- scripts Injects a library such as jQuery into the page before your code runs.
- meta The SDK sends false by default so the request only pays for the function.
- timeout The request ceiling: 30 seconds on free, 60 seconds on Pro.
Plan limits are explicit: on the free plan a function gets 15 seconds, 64 MB of heap, 1024 bytes of compressed code, one in-flight run per IP and same-origin requests only. Pro raises that to 60 seconds, 128 MB, unlimited code size and concurrency, and unrestricted outgoing requests. The profiling guide shows where each run spends its time.
Why hosted headless Chrome beats a browser you maintain
The browser is infrastructure. The function is your product. Splitting them lets you ship the second without owning the first.
Try a function against a live page in the editor before wiring it into your code, and read the Browser Functions feature for the full runtime.
The function troubleshooting guide maps each error, including EINVALFUNCTION for syntax errors, to its fix.
When not to: private and loopback targets such as localhost are refused with EFORBIDDENURL, so this is not a way to test a dev server, and flows that need state across many requests or more than 60 seconds belong on a browser you run. See request isolation.
FAQ
Can I run my existing Puppeteer script as a service?
How long can a serverless Puppeteer function run?
Can the hosted headless Chrome reach localhost or a private network?
What happens when my Puppeteer function throws an error?
Can a Puppeteer function use npm packages?
Solve the next problem with the same API
JavaScript with npm packages
Pagination and Load more buttons
Custom fields from JavaScript apps
Scraping behind a login
Screenshots of JavaScript-rendered pages
PDFs of JavaScript-rendered pages
Ready to run Puppeteer without Chrome?
Send a function, get a value. Start on the free tier and move your first script off your own browser today.