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
| Stage | Endpoint | Auth |
|---|---|---|
| Local development / prototyping | api.microlink.io | None needed |
| Staging / CI | pro.microlink.io | x-api-key header |
| Production | pro.microlink.io | x-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.See the endpoint docs and authentication docs.
Handle rate limits gracefully
When you hit your quota, Microlink returns HTTP 429 with error code
ERATE. Your integration should:- Check
x-rate-limit-remainingon every response. - When the value approaches zero, slow down or queue requests.
- 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=0cURL 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.bodyPHP 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))
}import mql from '@microlink/mql'
const { data } = await mql('https://example.com', {
ttl: "1d",
staleTtl: 0
})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):
- Never embed your API key in client-side code, HTML attributes, or public embed URLs.
- Use a proxy to keep the key on your server:
- @microlink/proxy— self-hosted Node.js proxy.
- @microlink/edge-proxy— edge-deployed (Cloudflare Workers, Vercel Edge).
- Restrict allowed domains in your proxy configuration so only your own sites can consume your quota.
See the authentication docs and private pages patterns.
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&screenshotcURL 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.bodyPHP 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))
}import mql from '@microlink/mql'
const { data } = await mql('https://example.com', {
screenshot: true,
meta: false
})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=3cURL 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.bodyPHP 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))
}import mql from '@microlink/mql'
const { data } = await mql('https://example.com', {
retry: 3
})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:
| Header | Why it matters |
|---|---|
x-cache-status | Tracks cache hit rate — low hit rates mean wasted work |
x-response-time | Tracks p50/p95 latency — spikes signal a target-site or rendering issue |
x-pricing-plan | Confirms requests hit the right endpoint |
x-fetch-mode | Shows whether requests used fetch, prerender, or proxy |
x-rate-limit-remaining | Enables 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.