Skip to content

Function: Profiling and performance

Every function response includes profiling data with phase-level timing and resource usage.

Profiling

import createClient from 'microlink.io'

const microlink = createClient()

const { profiling } = await microlink.function('https://example.com', ({ page }) => page.title())

console.log(profiling)
// {
//   phases: { install: 0, build: 120, spawn: 45, run: 890, total: 1055 },
//   cpu: 234,
//   memory: { total: 69996544, used: 2359296, heap: 4410880, external: 1742574 },
//   size: 156
// }
FieldDescription
phases.installTime spent installing npm dependencies (0 when none are used)
phases.buildTime spent bundling the function code
phases.spawnTime spent starting the isolated process
phases.runTime spent executing the function
phases.totalWall-clock time from start to finish
cpuPeak CPU time in milliseconds
memory.totalResident memory of the sandbox, Node.js baseline included, in bytes
memory.usedResident memory attributable to your function, in bytes
memory.heapV8 heap in use, in bytes. The only field the memory limit bounds
memory.externalOff-heap Buffer/ArrayBuffer memory, in bytes
sizeBundled code size in bytes
Use profiling to understand where time is spent. If install is high, your dependencies are being installed for the first time — subsequent runs use the cache. If run is high, the function itself is doing heavy work.

Plan limits

The function parameter is available on both free and pro plans:
FreePro
Timeout15 secondsUp to 60 seconds
Memory64 MB128 MB
Code size1024 bytesUnlimited
Concurrency1 in-flight per IPUnlimited
Outgoing requestsSame-origin onlyUnrestricted
A free API key is enough to prototype workflows and run the examples in this guide; headers, proxy, ttl and staleTtl are included. For production workloads that need more volume, use a paid plan.
To authenticate, pass your API key:
import createClient from 'microlink.io'

const microlink = createClient({
  apiKey: process.env.MICROLINK_API_KEY
})

const { value } = await microlink.function('https://example.com', ({ page }) => page.title())
See the authentication and rate limit docs for endpoint and quota details.

When the page is fetched

A function-only request pays for the function and nothing else: normalized metadata is off by default, and the page is resolved only when your code needs it.
your functionwhat the request does
never mentions pagenothing is fetched
calls page.content(), page.extract() or page.metadata()the page is fetched at the moment you call it
drives the browser, such as page.click() or page.evaluate()the page is loaded once and your function runs on it
The fetch happens on the call, not before your function starts.
Destructure page itself rather than its methods. ({ page }) => page.content() is answered from fetched HTML with no browser, while ({ page: { content } }) => content() cannot be read ahead of time and starts a full browser.
Ask for meta explicitly when you want normalized metadata alongside the function, or call page.metadata inside it to get the same data for one page load:
import createClient from 'microlink.io'

const microlink = createClient()

const { value } = await microlink.function(
  'https://example.com',
  async ({ page }) => (await page.metadata()).title
)

console.log(value)
page.metadata() returns the same fields as meta.

Compress large functions

Large function bodies are compressed before they are sent. The SDK handles this automatically — microlink.function() compresses the code with brotli in Node.js and lz-string in browsers, so the call stays the same:
import createClient from 'microlink.io'

const microlink = createClient()

const { value, profiling } = await microlink.function('https://example.com', ({ page }) => page.title())

console.log(profiling.size)
If you call the API directly, prefix the compressed payload with the algorithm alias: lz# for lz-string, br# for brotli, gz# for gzip. See the compression reference for details.

Optimization checklist

  1. Leave metadata off — a function-only request already skips it. Ask for meta: true only when you genuinely want it, since it is usually the largest cost in the request.
  2. Use page.title() and page.$eval() instead of page.evaluate() when possible — they are faster and easier to debug.
  3. Replace fixed waits like page.waitForTimeout(3000) with page.waitForSelector() — they resolve as soon as the element appears.
  4. Check profiling.phases to find the bottleneck — a high install on first run is normal, but a high run means the function itself needs work.
  5. Minimize dependencies — each require() adds install and build time. Use only what you need.

See also