Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/metabase/metabase/llms.txt

Use this file to discover all available pages before exploring further.

The Embedding API allows you to embed Metabase content (dashboards and questions) into external applications with secure, signed URLs.

Embedding overview

Metabase supports two types of embedding:
  • Public embedding - No authentication required, uses UUID-based URLs
  • Signed embedding - Secure, token-based embedding with parameter locking
This page covers programmatic embedding. Public embedding is covered in the Public API documentation.

Enable embedding

Before you can embed content, enable embedding in your Metabase settings:
  1. Go to Admin > Settings > Embedding
  2. Enable “Embedded analytics”
  3. Generate or set your embedding secret key
Keep your embedding secret key secure. It’s used to sign embedding tokens and should never be exposed in client-side code.

Enable embedding on content

To make a dashboard or question embeddable, update its enable_embedding property.

Enable embedding on a card

PUT /api/card/{id}
curl -X PUT \
  https://your-metabase.com/api/card/1 \
  -H 'Content-Type: application/json' \
  -H 'X-Metabase-Session: SESSION_TOKEN' \
  -d '{
    "enable_embedding": true,
    "embedding_params": {
      "category": "enabled",
      "date_range": "locked"
    }
  }'

Enable embedding on a dashboard

PUT /api/dashboard/{id}
curl -X PUT \
  https://your-metabase.com/api/dashboard/1 \
  -H 'Content-Type: application/json' \
  -H 'X-Metabase-Session: SESSION_TOKEN' \
  -d '{
    "enable_embedding": true,
    "embedding_params": {
      "user_id": "locked",
      "date_range": "enabled"
    }
  }'

Embedding parameters

enable_embedding
boolean
required
Enable or disable embedding for this content
embedding_params
object
Map of parameter names to their embedding status:
  • "disabled" - Parameter cannot be set
  • "enabled" - Parameter can be passed in the embedding URL
  • "locked" - Parameter must be set server-side and cannot be changed

Generate embedding token

To embed content securely, you need to generate a signed JWT token on your server.

Token payload

const payload = {
  resource: { dashboard: 1 },  // or { question: 1 }
  params: {
    user_id: 123,              // locked parameter values
    date_range: "last-30-days"
  },
  exp: Math.round(Date.now() / 1000) + (10 * 60) // 10 minute expiration
};

Sign the token

const jwt = require('jsonwebtoken');

const METABASE_SECRET_KEY = process.env.METABASE_SECRET_KEY;

const payload = {
  resource: { dashboard: 1 },
  params: {
    user_id: 123
  },
  exp: Math.round(Date.now() / 1000) + (10 * 60)
};

const token = jwt.sign(payload, METABASE_SECRET_KEY);

Embed URL structure

Dashboard embedding

https://your-metabase.com/embed/dashboard/TOKEN#bordered=true&titled=true

Question embedding

https://your-metabase.com/embed/question/TOKEN#bordered=true&titled=true

URL parameters

Customize the embedded appearance with URL hash parameters:
bordered
boolean
Show border around the embed (default: true)
titled
boolean
Show title (default: true)
theme
string
Color theme: null (default), “night”, or “transparent”
hide_parameters
string
Comma-separated list of parameters to hide
hide_download_button
boolean
Hide download button (default: false)

Example implementation

Server-side token generation

const express = require('express');
const jwt = require('jsonwebtoken');

const app = express();
const METABASE_SECRET_KEY = process.env.METABASE_SECRET_KEY;

app.get('/embed/dashboard/:dashboardId', (req, res) => {
  const userId = req.user.id; // from your auth system
  
  const payload = {
    resource: { dashboard: parseInt(req.params.dashboardId) },
    params: {
      user_id: userId  // locked parameter
    },
    exp: Math.round(Date.now() / 1000) + (10 * 60)
  };
  
  const token = jwt.sign(payload, METABASE_SECRET_KEY);
  
  res.json({
    url: `https://metabase.example.com/embed/dashboard/${token}#bordered=true&titled=true`
  });
});

Client-side iframe

<!DOCTYPE html>
<html>
<head>
    <title>Embedded Dashboard</title>
    <style>
        #embed-container {
            width: 100%;
            height: 800px;
            border: none;
        }
    </style>
</head>
<body>
    <iframe
        id="embed-container"
        src=""
        frameborder="0"
        allowtransparency
    ></iframe>

    <script>
        // Fetch the signed embedding URL from your server
        fetch('/embed/dashboard/1')
            .then(response => response.json())
            .then(data => {
                document.getElementById('embed-container').src = data.url;
            });
    </script>
</body>
</html>

List embeddable content

Get embeddable cards

GET /api/card/embeddable
curl -X GET \
  https://your-metabase.com/api/card/embeddable \
  -H 'X-Metabase-Session: SESSION_TOKEN'
Returns all cards where enable_embedding is true.

Get embeddable dashboards

GET /api/dashboard/embeddable
curl -X GET \
  https://your-metabase.com/api/dashboard/embeddable \
  -H 'X-Metabase-Session: SESSION_TOKEN'

Embedding types

Static embedding

Embed with fixed parameters that cannot be changed:
const payload = {
  resource: { dashboard: 1 },
  params: {
    user_id: 123  // locked - cannot be changed by end user
  }
};

Dynamic filtering

Allow users to change certain parameters:
const payload = {
  resource: { dashboard: 1 },
  params: {
    user_id: 123,           // locked
    date_range: "last-30-days"  // enabled - can be changed via URL
  }
};
URL: https://metabase.com/embed/dashboard/TOKEN#bordered=true&date_range=last-7-days

Best practices

Embedding security tips:
  • Always generate tokens server-side
  • Never expose your embedding secret key
  • Use short token expiration times (5-10 minutes)
  • Implement proper user authentication before generating tokens
  • Use locked parameters for user-specific data filtering
  • Regularly rotate your embedding secret key

Token expiration

Tokens include an exp (expiration) claim:
const exp = Math.round(Date.now() / 1000) + (10 * 60); // 10 minutes
When a token expires, the embed will stop working. Implement token refresh logic in your application.

Responsive embedding

Make embeds responsive with CSS:
.embed-container {
    position: relative;
    padding-bottom: 56.25%; /* 16:9 aspect ratio */
    height: 0;
    overflow: hidden;
}

.embed-container iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

Error handling

400 Bad Request
error
Invalid token or malformed request
401 Unauthorized
error
Invalid or expired token
403 Forbidden
error
Embedding not enabled for this resource
404 Not Found
error
Dashboard or question not found