Embed: Metadata API with custom HTML/CSS
When the SDK is too opinionated and the iframe parameter is too provider-specific, you can render previews entirely from your own markup. The metadata API gives you the raw fields — title, description, image, logo, palette — and you decide everything else.
This is the right path when:
- You need previews to match an existing design system exactly.
- You want server-rendered HTML with no client-side JavaScript.
- You're embedding inside an email, RSS feed, or static site generator.
- You need to A/B test layouts without changing your data layer.
Want ready-made layouts to paste into Cursor or Claude Code? See generate custom previews with AI — six recipes (hero card, one-line, tweet-style, telegram-style, notification, chat bubble) built on the same payload this page documents.
The minimum metadata call
A bare metadata request returns everything most previews need:
The following examples show how to use the Microlink API with CLI, cURL, JavaScript, Python, Ruby, PHP & Golang, targeting 'https://stripe.com' URL:
CLI Microlink API example
microlink https://stripe.comcURL Microlink API example
curl -G "https://api.microlink.io" \
-d "url=https://stripe.com"JavaScript Microlink API example
import mql from '@microlink/mql'
const { data } = await mql('https://stripe.com')Python Microlink API example
import requests
url = "https://api.microlink.io/"
querystring = {
"url": "https://stripe.com"
}
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"
}
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"
];
$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")
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://stripe.com')The response payload looks like this:
"data": {
"title": "Stripe | Financial Infrastructure to Grow Your Revenue",
"description": "Stripe is a financial services platform that helps all types of businesses accept payments, build flexible billing models, and manage money movement.",
"url": "https://stripe.com/",
"publisher": "stripe.com",
"lang": "en",
"image": {
"url": "https://images.stripeassets.com/fzn2n1nzq965/XtX984S1GJVsVOXFC7kMu/01988281e867728dfb09aa7793a6e3b9/Stripe.jpg?q=80",
"type": "jpg",
"size": 312818,
"height": 1024,
"width": 2048,
"size_pretty": "313 kB"
},
"date": "2026-05-19T13:38:48.000Z",
"author": "Kurtis Moyer, Lead Product Manager of Payments, Mindbody",
"logo": {
"url": "https://images.stripeassets.com/fzn2n1nzq965/4vVgZi0ZMoEzOhkcv7EVwK/8cce6fdcf2733b2ec8e99548908847ed/favicon.png?w=180&h=180",
"type": "png",
"size": 3143,
"height": 180,
"width": 180,
"size_pretty": "3.14 kB"
}
},The default response includes
title, description, image, logo, publisher, and url.Read those fields directly from
data and pass them into your template. No SDK install, no script tag, no iframe.Render a custom card from JSON
Fetch the metadata server-side, then render whatever HTML you want:
import mql from '@microlink/mql'
export async function renderCard (url) {
const { data } = await mql(url)
return `
<a class="link-card" href="${data.url}" rel="noopener noreferrer">
<img class="link-card__image" src="${data.image.url}" alt="" loading="lazy" />
<div class="link-card__body">
<span class="link-card__publisher">${data.publisher ?? ''}</span>
<h3 class="link-card__title">${data.title}</h3>
<p class="link-card__description">${data.description ?? ''}</p>
</div>
</a>
`
}Pair it with a stylesheet — no framework required:
.link-card {
display: grid;
grid-template-columns: 200px 1fr;
border: 1px solid #e1e8ed;
border-radius: 12px;
overflow: hidden;
text-decoration: none;
color: inherit;
background: #fff;
}
.link-card__image { width: 100%; height: 100%; object-fit: cover; }
.link-card__body { padding: 16px; }
.link-card__publisher { font-size: 11px; text-transform: uppercase; color: #6b7280; }
.link-card__title { margin: 6px 0 4px; font-size: 16px; font-weight: 700; }
.link-card__description {
font-size: 13px;
color: #4b5563;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}The same data can power any layout. See generate custom previews with AI for ready-made recipes (one-line, hero, tweet-style, notification, chat bubble).
Direct embed — no JSON parsing
If you only need a single asset URL (the image, the logo, the screenshot), use
embed to make the API URL behave like that asset:<img
src="https://api.microlink.io?url=https://stripe.com&embed=image.url"
alt="Stripe"
loading="lazy"
/>The same trick works for the logo:
<img src="https://api.microlink.io?url=https://stripe.com&embed=logo.url" alt="" />Or a real screenshot when
og:image is missing or low-quality:<img
src="https://api.microlink.io?url=https://stripe.com&screenshot&meta=false&embed=screenshot.url"
alt="Stripe homepage"
/>See the embed reference for every supported field. The dot notation (
screenshot.url, image.url, logo.url, pdf.url, video.url) follows the response payload.Open Graph and social cards
A common production use case — generate dynamic OG images for any page:
<meta
property="og:image"
content="https://api.microlink.io?url=https://your-site.com/blog/post&screenshot&meta=false&embed=screenshot.url"
/>
<meta
name="twitter:image"
content="https://api.microlink.io?url=https://your-site.com/blog/post&screenshot&meta=false&embed=screenshot.url"
/>Every share on Twitter, Slack, Discord, or LinkedIn now gets a fresh capture of the page. Combined with caching, those images are served from the edge instead of regenerating per share.
Add brand colors with palette
For UI that adapts to each link's brand, request
palette: true:The following examples show how to use the Microlink API with CLI, cURL, JavaScript, Python, Ruby, PHP & Golang, targeting 'https://stripe.com' URL with 'palette' API parameter:
CLI Microlink API example
microlink https://stripe.com&palettecURL Microlink API example
curl -G "https://api.microlink.io" \
-d "url=https://stripe.com" \
-d "palette=true"JavaScript Microlink API example
import mql from '@microlink/mql'
const { data } = await mql('https://stripe.com', {
palette: true
})Python Microlink API example
import requests
url = "https://api.microlink.io/"
querystring = {
"url": "https://stripe.com",
"palette": "true"
}
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",
palette: "true"
}
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",
"palette" => "true"
];
$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("palette", "true")
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://stripe.com', {
palette: true
})Each image and logo gets
palette, background_color, color, and alternative_color fields with WCAG-aware contrast.Use those fields directly:
const { data } = await mql('https://stripe.com', { palette: true })
card.style.background = data.image.background_color
card.style.color = data.image.color
card.querySelector('.accent-bar').style.background = data.image.palette[0]See the palette reference for details.
Filter the JSON response
If you only need a few fields,
filter reduces the payload to exactly those: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 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.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 mql from '@microlink/mql'
const { data } = await mql('https://stripe.com', {
filter: "title,description,image.url,logo.url"
})Useful for high-volume preview pipelines where every byte over the wire counts.
See the filter reference for dot-notation rules.
Keep credentials safe in markup
The
embed URL is publicly visible in HTML, CSS, and <meta> tags. Never put an API key, cookie, or proxy URL into a public embed:- For unauthenticated targets, use the free
https://api.microlink.ioendpoint. - For authenticated targets, render the URL server-side and fetch the asset through your own backend, or front it with @microlink/proxy/@microlink/edge-proxy.
For the full security model, see private pages and proxy.
Choose between metadata API and the other approaches
| If you need | Use |
|---|---|
| Server-rendered HTML, no client-side JS, full control | Metadata API + custom HTML/CSS (this page) |
| Pre-built layouts and prompts to feed your AI coding assistant | Generate custom previews with AI |
| A drop-in component with fetching, lazy-loading, and theming built in | SDK |
| The provider's interactive player (real YouTube embed, Spotify track) | iframe parameter |
Mixing them is fine — the metadata API powers all four.
Next step
If you'd rather have your AI assistant write the markup so it matches your existing design system, see generate custom previews with AI.