Skip to content

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:
  1. 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" />
  2. Filter the JSON when you only need a few fieldsfilter=title,description,image.url shrinks 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.url

    cURL 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 mql from '@microlink/mql'
    
    const { data } = await mql('https://stripe.com', {
      filter: "title,description,image.url,logo.url"
    })

    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.body

    PHP 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))
    }
  3. Disable metadata extraction when you only want a screenshot or iframemeta=false skips 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.url

    cURL 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 mql from '@microlink/mql'
    
    const { data } = await mql('https://stripe.com', {
      screenshot: true,
      meta: false,
      embed: "screenshot.url"
    })

    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.body

    PHP 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))
    }
    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' API parameters:

CLI Microlink API example

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

cURL Microlink API example

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

JavaScript Microlink API example

import mql from '@microlink/mql'

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

Python Microlink API example

import requests

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

querystring = {
    "url": "https://stripe.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://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)
response = http.request(request)

puts response.body

PHP Microlink API example

<?php

$baseUrl = "https://api.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"
]);

$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("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))
}
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=true defeats this. Use force only 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.

SDK lazy-loading

The 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' }} />

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 setData reference.

Verify caching is working

Check the response headers when you suspect a slow embed:
HeaderWhat it tells you
x-cache-statusHIT (cache), MISS (fresh), BYPASS (forced)
x-cache-ttlEffective cache lifetime in milliseconds
x-response-timeA 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.