Embed: Caching and performance
Embeds are read-mostly. The same URL renders the same card thousands of times. Lean into that — cache aggressively and serve from the edge.
This page covers embed-specific performance tactics. For the universal caching controls (
ttl, staleTtl, force, and how to read cache headers), see caching patterns.Embed-specific speedups
The single biggest win for embed-only requests is matching the response shape to your renderer. Three patterns:
-
Static markup with direct
embed— for<img>and<meta og:image>, return the asset itself. The CDN serves bytes directly with no JSON parsing in your stack.<img src="https://api.microlink.io?url=https://stripe.com&embed=image.url" /> -
Filter the JSON when you only need a few fields —
filter=title,description,image.urlshrinks the payload and keeps your renderer's hot path tight.The following examples show how to use the Microlink API with CLI, cURL, JavaScript, Python, Ruby, PHP & Golang, targeting 'https://stripe.com' URL with 'filter' API parameter:
CLI Microlink API example
microlink https://stripe.com&filter=title,description,image.url,logo.urlcURL Microlink API example
curl -G "https://api.microlink.io" \ -d "url=https://stripe.com" \ -d "filter=title,description,image.url,logo.url"JavaScript Microlink API example
import createClient from 'microlink.io' const microlink = createClient() const data = await microlink.metadata('https://stripe.com')Python Microlink API example
import requests url = "https://api.microlink.io/" querystring = { "url": "https://stripe.com", "filter": "title,description,image.url,logo.url" } 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://stripe.com", filter: "title,description,image.url,logo.url" } 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://stripe.com", "filter" => "title,description,image.url,logo.url" ]; $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://stripe.com") q.Set("filter", "title,description,image.url,logo.url") 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 createClient from 'microlink.io' const microlink = createClient() const data = await microlink.metadata('https://stripe.com') -
Disable metadata extraction when you only want a screenshot or iframe —
meta=falseskips the metadata stage entirely.The following examples show how to use the Microlink API with CLI, cURL, JavaScript, Python, Ruby, PHP & Golang, targeting 'https://stripe.com' URL with 'screenshot', 'meta' & 'embed' API parameters:
CLI Microlink API example
microlink https://stripe.com&screenshot&embed=screenshot.urlcURL Microlink API example
curl -G "https://api.microlink.io" \ -d "url=https://stripe.com" \ -d "screenshot=true" \ -d "meta=false" \ -d "embed=screenshot.url"JavaScript Microlink API example
import createClient from 'microlink.io' const microlink = createClient() const data = await microlink.screenshot('https://stripe.com')Python Microlink API example
import requests url = "https://api.microlink.io/" querystring = { "url": "https://stripe.com", "screenshot": "true", "meta": "false", "embed": "screenshot.url" } 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://stripe.com", screenshot: "true", meta: "false", embed: "screenshot.url" } 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://stripe.com", "screenshot" => "true", "meta" => "false", "embed" => "screenshot.url" ]; $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://stripe.com") q.Set("screenshot", "true") q.Set("meta", "false") q.Set("embed", "screenshot.url") 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 createClient from 'microlink.io' const microlink = createClient() const data = await microlink.screenshot('https://stripe.com')Useful for OG images and dynamic banners — no metadata work, just the capture.
Cache strategy
For the controls that apply to every workflow —
ttl, staleTtl, force, and how to verify caching through response headers — see caching patterns.A recommended production setup for embeds:
The following examples show how to use the Microlink API with CLI, cURL, JavaScript, Python, Ruby, PHP & Golang, targeting 'https://stripe.com' URL with 'ttl', 'staleTtl' & 'apiKey' API parameters:
CLI Microlink API example
microlink https://stripe.com&ttl=1d&staleTtl=0 --api-key YOUR_API_TOKENcURL Microlink API example
curl -G "https://pro.microlink.io" \
-H "x-api-key: YOUR_API_TOKEN" \
-d "url=https://stripe.com" \
-d "ttl=1d" \
-d "staleTtl=0"JavaScript Microlink API example
import createClient from 'microlink.io'
const microlink = createClient({ apiKey: "YOUR_API_TOKEN" })
const data = await microlink.metadata('https://stripe.com', {
ttl: "1d",
staleTtl: 0
})Python Microlink API example
import requests
url = "https://pro.microlink.io/"
querystring = {
"url": "https://stripe.com",
"ttl": "1d",
"staleTtl": "0"
}
headers = {
"x-api-key": "YOUR_API_TOKEN"
}
response = requests.get(url, params=querystring, headers=headers)
print(response.json())Ruby Microlink API example
require 'uri'
require 'net/http'
base_url = "https://pro.microlink.io/"
params = {
url: "https://stripe.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)
request['x-api-key'] = "YOUR_API_TOKEN"
response = http.request(request)
puts response.bodyPHP Microlink API example
<?php
$baseUrl = "https://pro.microlink.io/";
$params = [
"url" => "https://stripe.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",
CURLOPT_HTTPHEADER => [
"x-api-key: YOUR_API_TOKEN"
]
]);
$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://pro.microlink.io"
u, err := url.Parse(baseURL)
if err != nil {
panic(err)
}
q := u.Query()
q.Set("url", "https://stripe.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)
}
req.Header.Set("x-api-key", "YOUR_API_TOKEN")
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 createClient from 'microlink.io'
const microlink = createClient({ apiKey: "YOUR_API_TOKEN" })
const data = await microlink.metadata('https://stripe.com', {
ttl: "1d",
staleTtl: 0
})Cache for a day, serve stale instantly while refreshing in the background. Requires a
PRO
plan.For high-traffic embed surfaces — comment widgets, RSS readers, AI search results — pair
staleTtl: 0 with a long ttl so users always hit cache and only the background refresh touches the origin.Hot-linking and the embed URL
The
embed=... URL is itself a CDN-cacheable asset. Browsers and downstream proxies (Slack, Discord, email clients) cache it by URL. Two consequences:- Stable URLs cache best. Avoid appending random query parameters or session IDs.
force=truedefeats this. Useforceonly when you intentionally want a regeneration — never as a default.
If you need the URL to change when source content changes (cache busting), append a content hash you control:
<img src="https://api.microlink.io?url=https://your-site.com/post&screenshot&embed=screenshot.url&v=42" />Bumping
v invalidates downstream caches without touching Microlink's behavior.Embed SDK lazy-loading
The Embed SDK uses
IntersectionObserver and only fires its API call when each preview enters the viewport. Pages with hundreds of embedded links still hit a fast first paint. To preload above-the-fold cards, tighten the lazy options:<Microlink url='...' lazy={{ rootMargin: '400px' }} />The
lazy prop is covered on the link preview component page.When you don't need fresh data
Static sites and SSR builds often have the metadata at build time. Skip the runtime call:
<Microlink
url='https://example.com/post'
fetchData={false}
setData={prefetchedData}
/>That removes the API call entirely. Combined with build-time crawling, you ship zero-API-call embeds. See the link preview component page for
setData.Verify caching is working
Check the response headers when you suspect a slow embed:
| Header | What it tells you |
|---|---|
x-cache-status | HIT (cache), MISS (fresh), BYPASS (forced) |
x-cache-ttl | Effective cache lifetime in milliseconds |
x-response-time | A short value (under ~50ms) usually means a cache hit |
For the full pattern, see verify caching behavior.
Next step
Learn how to embed authenticated dashboards and pages protected by antibot systems in private pages and proxy.