Skip to content

The Puppeteer alternative with no Chrome to run

Keep the Puppeteer code and drop the browser fleet. Send a function that receives a Puppeteer page, or ask for a screenshot or PDF by parameter, and Microlink runs it in a fresh Chrome on its side.

The following examples show how to use the Microlink API with CLI, cURL, JavaScript, Python, Ruby, PHP & Golang, targeting 'https://news.ycombinator.com' URL with 'function' & 'meta' API parameters:

CLI Microlink API example

microlink https://news.ycombinator.com&function='({ page }) =>
  page.$$eval('"'"'.titleline > a'"'"', links =>
    links.slice(0, 5).map(a => ({ title: a.textContent, url: a.href }))
  )'

cURL Microlink API example

curl -G "https://api.microlink.io" \
  -d "url=https://news.ycombinator.com" \
  -d "function=(%7B%20page%20%7D)%20%3D%3E%0A%20%20page.%24%24eval('.titleline%20%3E%20a'%2C%20links%20%3D%3E%0A%20%20%20%20links.slice(0%2C%205).map(a%20%3D%3E%20(%7B%20title%3A%20a.textContent%2C%20url%3A%20a.href%20%7D))%0A%20%20)" \
  -d "meta=false"

JavaScript Microlink API example

import createClient from 'microlink.io'

const microlink = createClient()

const { 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 }))
    )
)

Python Microlink API example

import requests

url = "https://api.microlink.io/"

querystring = {
    "url": "https://news.ycombinator.com",
    "function": '''({ page }) =>
  page.$$eval('.titleline > a', links =>
    links.slice(0, 5).map(a => ({ title: a.textContent, url: a.href }))
  )''',
    "meta": "false"
}

response = requests.get(url, params=querystring)

print(response.json())

Ruby Microlink API example

require 'uri'
require 'net/http'

base_url = "https://api.microlink.io/"

params = {
  url: "https://news.ycombinator.com",
  function: "({ page }) =>
  page.$$eval('.titleline > a', links =>
    links.slice(0, 5).map(a => ({ title: a.textContent, url: a.href }))
  )",
  meta: "false"
}

uri = URI(base_url)
uri.query = URI.encode_www_form(params)

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Get.new(uri)
response = http.request(request)

puts response.body

PHP Microlink API example

<?php

$baseUrl = "https://api.microlink.io/";

$params = [
    "url" => "https://news.ycombinator.com",
    "function" => "({ page }) =>
  page.$$eval('.titleline > a', links =>
    links.slice(0, 5).map(a => ({ title: a.textContent, url: a.href }))
  )",
    "meta" => "false"
];

$query = http_build_query($params);
$url = $baseUrl . '?' . $query;

$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "GET"
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
    echo "cURL Error #: " . $err;
} else {
    echo $response;
}

Golang Microlink API example

package main

import (
    "fmt"
    "net/http"
    "net/url"
    "io"
)

func main() {
    baseURL := "https://api.microlink.io"

    u, err := url.Parse(baseURL)
    if err != nil {
        panic(err)
    }
    fn := `({ page }) =>
  page.$$eval('.titleline > a', links =>
    links.slice(0, 5).map(a => ({ title: a.textContent, url: a.href }))
  )`

    q := u.Query()
    q.Set("url", "https://news.ycombinator.com")
    q.Set("function", fn)
    q.Set("meta", "false")
    u.RawQuery = q.Encode()

    req, err := http.NewRequest("GET", u.String(), nil)
    if err != nil {
        panic(err)
    }

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }

    fmt.Println(string(body))
}

What you own when you run Puppeteer.

npm install puppeteer and a ten-line script work on a laptop. Production adds a binary, a runtime and a process tree to look after. Every number below comes from the Puppeteer, AWS or Chromium docs, or from the @sparticuz/chromium repository.
The jobRunning Puppeteer yourselfMicrolink
The browser binaryChrome for Testing downloads on install: about 170 MB on macOS, 282 MB on Linux, 280 MB on Windows.Nothing to download. Your code makes an HTTP call.
Version upgradesEach Puppeteer release pairs with one Chrome build (v25.17.0 with 157.0.8103.50), and Chrome ships a new major version every two weeks.Chrome and Puppeteer upgraded on our side.
Serverless packagingLambda caps a zip deployment at 250 MB unzipped, layers included, and the Linux browser alone is larger. Either ship a container image (10 GB cap) and pay a larger cold start, or swap in the 130.62 MiB @sparticuz/chromium build.No layer, no package budget to fit.
System libraries and fontsChrome for Testing ships without its shared library dependencies. The Lambda runtime has no fonts, and the common trimmed build adds Open Sans only, no emoji.Fonts and libraries installed once, for every call.
Zombie processesPuppeteer’s own troubleshooting guide points to dumb-init or docker --init to reap Chrome processes left behind.A fresh browser per call, destroyed when the response is sent.
Concurrency and queueingYou size the pool: how many browsers per box, how many pages per browser, what waits in the queue.Paid plans are capped by quota, never by rate. Free keys allow 2 calls in flight.
Cold startsEvery new container or function launches Chrome before the first page loads.Your code launches no browser. Cache hits skip the browser entirely.
Proxies and antibotYou buy a proxy pool, rotate it, and detect blocks yourself.Proxy resolution included on Pro, and blocks named by provider.
Caching and retriesYou build a cache in front of the browser and a retry loop around it.ttl from 1 minute to 31 days, and 2 backoff retries by default.
Download sizes from the Puppeteer installation guide; version pairing from supported browsers; release cadence from the Chromium release cycle doc; the 250 MB zip and 10 GB container image limits from AWS Lambda quotas; the trimmed build size and font list from @sparticuz/chromium; libraries and zombie processes from Puppeteer troubleshooting. The Microlink column links to ttl, retry, proxy and request isolation. Last verified: November 2026.

Send the page code Skip the browser fleet

2.8
secs
P95 screenshot
1.6
secs
P95 PDF
99.9
%
SLA Guaranteed

Start free, scale when ready

One request per call, whether it runs your function, takes a screenshot or prints a PDF. The rate holds at $1.07 per thousand calls whichever tier you pick, and a cache hit costs nothing.

Pro

For production workloads.
$49/month
≈ $1.07 per 1,000 requests
46,000 requests / month
Cancel anytime · No setup fees

Business & Enterprise

Bought the way companies buy.
Enterprise adds

Build or buy, row by row.

Both run the same engine: Chrome, driven over the DevTools Protocol by Puppeteer. Microlink builds on browserless, its open-source driver on top of Puppeteer. The difference is who operates the browser. Puppeteer facts come from pptr.dev and the Puppeteer repository, and the last four rows are where running it yourself wins.
CapabilityMicrolinkSelf-hosted Puppeteer
Run your own Puppeteer code
function receives page, a full Puppeteer Page already navigated to the URL, plus response and headers. Paid plans allow 60 seconds and 128 MB per run.
✓✓
Chrome install and upgrades
Puppeteer pairs each release with one Chrome for Testing build, and Chrome ships a major version every two weeks.
ManagedYou pin and upgrade
Install footprint
Lambda caps a zip deployment at 250 MB unzipped, layers included, and a container image at 10 GB.
0 MB~282 MB on Linux
Fresh browser per call
No cookies or storage carry over between Microlink calls. In Puppeteer, a new browser context shares no cookies or cache with the others.
✓createBrowserContext() per job
Screenshot, PDF, metadata, markdown
page.screenshot() and page.pdf() exist in Puppeteer. Metadata and markdown are code you write and maintain.
One parameter eachYou write each
Response caching
Default ttl is 24 hours, and a cache hit is not counted against the plan.
1 minute to 31 daysYour code
Retries
Exponential backoff when the browser fails internally.
2 by defaultYour code
Ads and cookie banners
Puppeteer blocks requests through page.setRequestInterception() and rules you maintain.
Blocked by defaultWrite interception rules
Proxy for blocked targets
proxy.location pins the exit country; proxy.url routes through a server you supply.
Included on ProBring your own
Antibot detection
Microlink names the provider that blocked the request.
✓Your code
Free tier
Without signing up you get 5 calls daily; a free key raises that to 25, no card asked. Puppeteer costs nothing to install, while the machine behind it bills by the hour.
25/dayFree, Apache-2.0
Session length
A browser you launch stays open as long as your process does.
60 s per callUnbounded
Private network and localhost
Microlink refuses private and loopback targets with EFORBIDDENURL.
✕✓
Firefox
Puppeteer drives stable Firefox since v23.0.0. Microlink runs Chrome only.
✕✓
Launch flags and extensions
puppeteer.launch() takes any Chrome flag. Microlink launches the browser for you.
✕✓
Last verified: November 2026. Check pptr.dev for the latest Puppeteer and Chrome versions.

When running Puppeteer yourself is the better pick.

An API is the wrong tool for some Puppeteer jobs. Six of them, stated plainly.
  • Long sessions that keep state

    A login, then fifty clicks, then a download, all in one browser that stays open for an hour. Microlink gives each call a fresh browser and a 60-second ceiling on paid plans, so state across calls is yours to carry.
  • End-to-end tests

    Tests want the browser next to the code under test, deterministic and offline, running in CI. Puppeteer, or a test runner built on it, is the right tool there.
  • Intranet and localhost targets

    Microlink only resolves public addresses and refuses private and loopback ones with EFORBIDDENURL. A dev server, a staging box behind a VPN or an internal dashboard needs a browser inside that network.
  • Very high, steady volume

    At millions of pages a month on a flat, predictable load, a dedicated box can cost less per page than $1.07 per 1,000 requests. Count the hours spent keeping it up before you decide: self-hosting is cheaper only if it still wins with them included.
  • Latency-critical paths with a warm pool

    Microlink launches a fresh browser for every call, so a screenshot takes the P95 shown on this page. A Puppeteer browser you keep running answers browser.newPage() in-process with no launch at all, which is far faster when every millisecond sits in front of a user.
  • Full control of the browser

    Custom launch flags, extensions, a specific Chrome build or Firefox. puppeteer.launch() takes all of it; Microlink chooses the browser and its flags for you.

Product Information

What moves to the API, what stays in your code, and where the limits are.

Mostly as is. Remove puppeteer.launch() and page.goto(), take page from the function arguments and return the result. Everything after navigation, from page.click() to page.$$eval(), works unchanged because page is a full Puppeteer Page. The function can require() npm packages, and the result comes back under data.function. See function and writing functions.

Is there a Puppeteer screenshot API without writing code?

Yes. Set screenshot: true on a request and Microlink returns a hosted image URL, with full page, element, device and viewport as parameters. pdf: true does the same for page.pdf(). No function is needed for either. See the Screenshot API and the PDF API.

Which browser runs my code?

Chrome. Microlink drives it through browserless, its open-source driver on top of Puppeteer, and keeps Chrome as the engine because the same rendering produces the screenshots, PDFs and DOM the API returns. Firefox is not available. Read why we still run Chrome.

What are the limits of a function call?

On paid plans a function gets 60 seconds, 128 MB of memory, unlimited code size and unrestricted outgoing requests. On the free plan it gets 15 seconds, 64 MB, 1024 bytes of code, 2 concurrent requests and same-origin requests only. Hitting a limit returns a named error such as TimeoutError instead of failing the whole request. See function troubleshooting.

Why not run Puppeteer on AWS Lambda?

You can, with work. Lambda caps a zip deployment at 250 MB unzipped including layers (AWS quotas), and the Chrome for Testing build Puppeteer downloads is about 282 MB on Linux (Puppeteer docs). One path is a container image, which Lambda accepts up to 10 GB, at the price of a larger cold start. The other is a trimmed build such as @sparticuz/chromium, with fonts added through a layer because the runtime ships none. Either way every cold start pays a browser launch.

How does pricing compare to running my own servers?

Every self-serve tier works out to $1.07 per thousand calls: $49 buys 46,000 a month and $450 buys 420,000, with cache hits billed at zero. A server bills a fixed amount each month, and on top of it come the hours spent upgrading Chrome, fixing fonts and reaping processes. Low or spiky traffic usually favors the API; very high, steady traffic can favor your own box, once those hours are counted.

Can I try it without a credit card?

Yes. 5 requests a day need no key at all, and the free API key renews 25 requests every day with no card. Function, screenshot, PDF, metadata and markdown are all included on it.

Start building

Paste the body of the Puppeteer script you run today into a function call and compare the result.
Get your free API key
25 requests/day · Every Pro feature · No credit card
Trusted with 100M+ requests every month

Puppeteer, Chrome and Chromium are trademarks of Google LLC. Microlink is not affiliated with or endorsed by Google. Details about Puppeteer, Chrome and AWS Lambda come from their public documentation and may have changed since they were checked.