Skip to content

Production patterns

These patterns apply once you move from prototyping to a production integration. They cover the decisions most teams encounter after the first few hundred requests.

Choose the right endpoint

StageEndpointAuth
Local development / prototypingapi.microlink.ioNone needed
Staging / CIpro.microlink.iox-api-key header
Productionpro.microlink.iox-api-key header
The free endpoint has a soft limit of 50 requests per day. The Pro endpoint uses your plan quota. Sending x-api-key to the free endpoint fails with EPRO.

Handle rate limits gracefully

When you hit your quota, Microlink returns HTTP 429 with error code ERATE. Your integration should:
  1. Check x-rate-limit-remaining on every response.
  2. When the value approaches zero, slow down or queue requests.
  3. On a 429 response, wait until x-rate-limit-reset (UTC epoch seconds) before retrying.
const { status, data, headers } = response

if (status === 429) {
  const resetAt = Number(headers['x-rate-limit-reset']) * 1000
  const waitMs = Math.max(0, resetAt - Date.now())
  await new Promise(resolve => setTimeout(resolve, waitMs))
}
The free endpoint resets daily. Pro plan resets depend on your billing cycle. See the rate limit docs.

Use stale-while-revalidate by default
PRO

For most production workflows, the best cache setup is:

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

CLI Microlink API example

microlink https://example.com&ttl=1d&staleTtl=0

cURL Microlink API example

curl -G "https://api.microlink.io" \
  -d "url=https://example.com" \
  -d "ttl=1d" \
  -d "staleTtl=0"

JavaScript Microlink API example

import mql from '@microlink/mql'

const { data } = await mql('https://example.com', {
  ttl: "1d",
  staleTtl: 0
})

Python Microlink API example

import requests

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

querystring = {
    "url": "https://example.com",
    "ttl": "1d",
    "staleTtl": "0"
}

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://example.com",
  ttl: "1d",
  staleTtl: "0"
}

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://example.com",
    "ttl" => "1d",
    "staleTtl" => "0"
];

$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)
    }
    q := u.Query()
    q.Set("url", "https://example.com")
    q.Set("ttl", "1d")
    q.Set("staleTtl", "0")
    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))
}
Serve the cached response instantly, refresh in the background. This maximizes cache hits and minimizes user-facing latency.
Adjust ttl based on how often the target content changes. See caching patterns for the full strategy.

Protect credentials in client-side code

If you consume Microlink from a frontend (website, SPA, mobile app):
  1. Never embed your API key in client-side code, HTML attributes, or public embed URLs.
  2. Use a proxy to keep the key on your server:
  3. Restrict allowed domains in your proxy configuration so only your own sites can consume your quota.

Use meta: false when you do not need metadata

Many workflows only need one output (a screenshot, a PDF, or custom extracted fields). Adding meta: false skips the normalized metadata step, which is usually the single biggest speedup:

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

CLI Microlink API example

microlink https://example.com&screenshot

cURL Microlink API example

curl -G "https://api.microlink.io" \
  -d "url=https://example.com" \
  -d "screenshot=true" \
  -d "meta=false"

JavaScript Microlink API example

import mql from '@microlink/mql'

const { data } = await mql('https://example.com', {
  screenshot: true,
  meta: false
})

Python Microlink API example

import requests

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

querystring = {
    "url": "https://example.com",
    "screenshot": "true",
    "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://example.com",
  screenshot: "true",
  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://example.com",
    "screenshot" => "true",
    "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)
    }
    q := u.Query()
    q.Set("url", "https://example.com")
    q.Set("screenshot", "true")
    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))
}
The x-fetch-mode response header will show 'skipped' when metadata detection is bypassed.

Retry with exponential backoff

Transient errors happen. The API supports a retry parameter that performs server-side retries with exponential backoff:

The following examples show how to use the Microlink API with CLI, cURL, JavaScript, Python, Ruby, PHP & Golang, targeting 'https://example.com' URL with 'retry' API parameter:

CLI Microlink API example

microlink https://example.com&retry=3

cURL Microlink API example

curl -G "https://api.microlink.io" \
  -d "url=https://example.com" \
  -d "retry=3"

JavaScript Microlink API example

import mql from '@microlink/mql'

const { data } = await mql('https://example.com', {
  retry: 3
})

Python Microlink API example

import requests

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

querystring = {
    "url": "https://example.com",
    "retry": "3"
}

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://example.com",
  retry: "3"
}

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://example.com",
    "retry" => "3"
];

$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)
    }
    q := u.Query()
    q.Set("url", "https://example.com")
    q.Set("retry", "3")
    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))
}
The default value is 2. Increase to 3 for targets that occasionally fail.
For client-side retries, respect the same backoff pattern and avoid retrying on 4xx errors that indicate a configuration problem (like EINVALURL or EAUTH).

Monitor response headers

Build observability into your integration by logging these headers:
HeaderWhy it matters
x-cache-statusTracks cache hit rate — low hit rates mean wasted work
x-response-timeTracks p50/p95 latency — spikes signal a target-site or rendering issue
x-pricing-planConfirms requests hit the right endpoint
x-fetch-modeShows whether requests used fetch, prerender, or proxy
x-rate-limit-remainingEnables proactive throttling before you hit the limit

Compress responses

If you call the API directly (not through MQL or the SDK), set the Accept-Encoding header to br or gzip:
curl -H 'Accept-Encoding: br' 'https://api.microlink.io?url=https://example.com'
MQL and the SDK enable compression by default. See the compression docs.