Skip to content

Embed: iframe parameter

The iframe parameter asks Microlink to discover the provider's official interactive embed for the target URL — a real YouTube player, a Spotify track, a Tweet widget, a Vimeo video. When discovery succeeds, the response includes a new iframe field with HTML and any required scripts.
This is the right path when the goal is the experience the source provides: playable media, interactive widgets, or anything that is not just a thumbnail and title. If you want a wrapper component that handles the injection for you, see SDK with media='iframe'. If you want a styled card built from JSON, see metadata API + custom HTML.

What the iframe field returns

The following examples show how to use the Microlink API with CLI, cURL, JavaScript, Python, Ruby, PHP & Golang, targeting 'https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw' URL with 'iframe' API parameter:

CLI Microlink API example

microlink https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw&iframe

cURL Microlink API example

curl -G "https://api.microlink.io" \
  -d "url=https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw" \
  -d "iframe=true"

JavaScript Microlink API example

import mql from '@microlink/mql'

const { data } = await mql('https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw', {
  iframe: true
})

Python Microlink API example

import requests

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

querystring = {
    "url": "https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw",
    "iframe": "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://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw",
  iframe: "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.body

PHP Microlink API example

<?php

$baseUrl = "https://api.microlink.io/";

$params = [
    "url" => "https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw",
    "iframe" => "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://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw")
    q.Set("iframe", "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))
}
The discovered iframe is rendered above. The response payload contains the markup that produced it.
The iframe object has two subfields:
{
  "iframe": {
    "html": "<iframe width=\"100%\" height=\"152\" src=\"https://open.spotify.com/embed/track/3BovdzfaX4jb5KFQwoPfAw?utm_source=oembed\" ...></iframe>",
    "scripts": []
  }
}
  • html — the markup to inject into the page.
  • scripts — an array of { src, async, charset } entries that some providers need. Empty for most video and audio providers; populated for Twitter, Instagram, and other widget-based embeds.

Inject the iframe HTML

The simplest pattern: fetch the data server-side, pass iframe.html to the client, and inject it.

Vanilla JavaScript

import mql from '@microlink/mql'

const { data } = await mql(url, { iframe: true })

if (data.iframe) {
  document.getElementById('embed').innerHTML = data.iframe.html

  data.iframe.scripts.forEach(({ src, async, charset }) => {
    if (document.querySelector(`script[src="${src}"]`)) return
    const script = document.createElement('script')
    script.src = src
    if (async) script.async = true
    if (charset) script.charset = charset
    document.head.appendChild(script)
  })
}
The script step is what makes Twitter, Instagram, and similar widgets actually render — without it you get unstyled blockquotes.

React

import mql from '@microlink/mql'

function ProviderEmbed ({ url }) {
  const [iframe, setIframe] = useState(null)

  useEffect(() => {
    let cancelled = false
    mql(url, { iframe: true }).then(({ data }) => {
      if (cancelled) return
      setIframe(data.iframe)
      data.iframe?.scripts?.forEach(({ src, async, charset }) => {
        if (document.querySelector(`script[src="${src}"]`)) return
        const s = document.createElement('script')
        s.src = src
        if (async) s.async = true
        if (charset) s.charset = charset
        document.head.appendChild(s)
      })
    })
    return () => { cancelled = true }
  }, [url])

  if (!iframe) return null
  return <div dangerouslySetInnerHTML={{ __html: iframe.html }} />
}
For React, the SDK already handles this — see SDK and pass media="iframe".

Server-side rendering

If you SSR the page, write the HTML and the script tags directly into the response — no client fetch needed:
const { data } = await mql(url, { iframe: true })

res.send(`
  <article>
    <h1>${data.title}</h1>
    ${data.iframe.html}
    ${data.iframe.scripts.map(s => `<script async src="${s.src}"></script>`).join('')}
  </article>
`)

When to use iframe vs SDK vs custom HTML

If you wantUse
The provider's real interactive player or widgetiframe parameter (this page)
A drop-in component with fetching, lazy-loading, and themingSDK with media="iframe"
A static rich card, fully styled by youmetadata API + custom HTML
The SDK and the iframe parameter are not alternatives — the SDK consumes the same iframe field internally when you set media="iframe". Pick the SDK if you want the wrapping component to also handle the loading state, lazy-loading, and CSS theming. Pick the raw iframe parameter if you want the markup and nothing else.

Customize the iframe with oEmbed options

The iframe parameter accepts an object that forwards consumer options to the oEmbed endpoint:

The following examples show how to use the Microlink API with CLI, cURL, JavaScript, Python, Ruby, PHP & Golang, targeting 'https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw' URL with 'iframe' API parameter:

CLI Microlink API example

microlink https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw&iframe.maxWidth=350

cURL Microlink API example

curl -G "https://api.microlink.io" \
  -d "url=https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw" \
  -d "iframe.maxWidth=350"

JavaScript Microlink API example

import mql from '@microlink/mql'

const { data } = await mql('https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw', {
  iframe: {
    maxWidth: 350
  }
})

Python Microlink API example

import requests

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

querystring = {
    "url": "https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw",
    "iframe.maxWidth": "350"
}

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://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw",
  iframe.maxWidth: "350"
}

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://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw",
    "iframe.maxWidth" => "350"
];

$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://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw")
    q.Set("iframe.maxWidth", "350")
    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))
}
Each provider chooses which options it honors. maxWidth and maxHeight are the most widely supported.
See the for the full list of consumer parameters.

When iframe discovery fails

Not every URL has an oEmbed endpoint. If discovery fails, the iframe field is absent from the response — the rest of the metadata is still returned.
Plan your code for both shapes:
const { data } = await mql(url, { iframe: true })

if (data.iframe) {
  container.innerHTML = data.iframe.html
} else {
  // fall back to a custom card built from data.title / data.image / data.description
  container.innerHTML = renderCard(data)
}
The full provider list lives in the iframe parameter reference — 280+ providers including YouTube, Spotify, Twitter/X, Vimeo, Instagram, TikTok, Figma, CodeSandbox, CodePen, Reddit, Pinterest, Behance, Dribbble, TED, SoundCloud, and Mixcloud.

Pair iframe with metadata in one call

You usually want both — the iframe for playback, the metadata for the surrounding card frame:

The following examples show how to use the Microlink API with CLI, cURL, JavaScript, Python, Ruby, PHP & Golang, targeting 'https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw' URL with 'iframe' & 'palette' API parameters:

CLI Microlink API example

microlink https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw&iframe&palette

cURL Microlink API example

curl -G "https://api.microlink.io" \
  -d "url=https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw" \
  -d "iframe=true" \
  -d "palette=true"

JavaScript Microlink API example

import mql from '@microlink/mql'

const { data } = await mql('https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw', {
  iframe: true,
  palette: true
})

Python Microlink API example

import requests

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

querystring = {
    "url": "https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw",
    "iframe": "true",
    "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://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw",
  iframe: "true",
  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.body

PHP Microlink API example

<?php

$baseUrl = "https://api.microlink.io/";

$params = [
    "url" => "https://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw",
    "iframe" => "true",
    "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://open.spotify.com/track/3BovdzfaX4jb5KFQwoPfAw")
    q.Set("iframe", "true")
    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))
}
One request returns the player, the title and description, the publisher logo, and brand colors — enough to render a full provider-styled card.
This is also why the SDK works the way it does: it requests iframe, audio, video, image, logo together and picks the best media available per URL.

Security and sandboxing

Injecting third-party HTML and scripts is intentional here — that is how the player renders. To reduce blast radius:
  • Apply a strict that allowlists only the providers you embed.
  • Add sandbox to the iframe element if you want to restrict navigation or popups.
  • Strip query parameters that could leak referrer data when privacy matters (use the nocookie variants where the provider supports them, e.g. youtube-nocookie.com).
The HTML returned by Microlink mirrors what the provider hands out via oEmbed, so the security posture is the same as if you embedded the provider directly.

Next step

For a wrapper component that handles the iframe injection plus loading/lazy/theming for you, see SDK.