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:
- Go to Admin > Settings > Embedding
- Enable “Embedded analytics”
- 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
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
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 or disable embedding for this content
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:
Show border around the embed (default: true)
Show title (default: true)
Color theme: null (default), “night”, or “transparent”
Comma-separated list of parameters to hide
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
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
Invalid token or malformed request
Embedding not enabled for this resource
Dashboard or question not found