DocsGuides

Guides

Embed graphs

2 min readReviewed July 2026

GraphJSON charts can live outside the dashboard. Choose a static iframe for a fixed configuration or generate an iframe URL from your server when filters must change dynamically.

Choose an embed mode#

Mode Configuration Graph data Best for
Static iframe URL Fixed when exported Loads live Public metrics, internal portals, fixed reports
Visualization API Built by your server Loads live Per-user views and dynamic product analytics
Data API Built by your server Returned as JSON Custom chart components

All three use the same collection, range, aggregation, filters, and customization concepts.

Static iframe#

Configure a chart in the visualizer and select Export → As static iframe.

<iframe
  src="https://graphjson.com/embed?p=..."
  title="Completed checkouts over the last 30 days"
  width="100%"
  height="360"
  loading="lazy"
  style="border: 0; border-radius: 12px;"
></iframe>

The URL stores the chart configuration, not a frozen image. The iframe queries fresh data when it loads.

Use a meaningful title for accessibility and reserve a height so the page does not shift while the chart loads.

Dynamic iframe#

Generate dynamic URLs on your server:

export async function createAccountChart(accountId) {
  const payload = {
    api_key: process.env.GRAPHJSON_API_KEY,
    collection: "product_events",
    IANA_time_zone: "America/Los_Angeles",
    graph_type: "Single Line",
    start: "30 days ago",
    end: "now",
    filters: [
      ["event", "=", "report_exported"],
      ["account_id", "=", accountId]
    ],
    metric: null,
    aggregation: "Count",
    compare: "30 days ago",
    granularity: "Day",
    customizations: {
      title: "Reports exported",
      showYAxis: true,
      showDots: false
    }
  };

  const response = await fetch(
    "https://api.graphjson.com/api/visualize/iframe",
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload)
    }
  );

  const body = await response.json();

  if (!response.ok) {
    throw new Error(body.error || `GraphJSON returned ${response.status}`);
  }

  return body.url;
}

Render the returned URL in the browser:

<iframe
  src={chartUrl}
  title="Reports exported"
  className="analytics-chart"
/>

Enforce authorization#

Derive accountId from the authenticated server session. Do not trust a browser-supplied account ID without confirming the user can access it.

Security: Construct the GraphJSON payload on your server. If you let the browser choose an arbitrary collection or filter and merely attach the API key, a user may query data outside their authorization boundary.

A safe handler:

  1. authenticates the application user
  2. resolves their allowed account
  3. chooses a known collection and chart definition
  4. adds the allowed account filter
  5. calls GraphJSON with the server-side key
  6. returns only the generated iframe URL

Cache thoughtfully#

Generating an iframe URL does not need to happen on every browser render when the configuration is unchanged. Cache by a safe key such as:

chart_definition + account_id + reporting_timezone

Do not share one user’s cached URL with another user. Remember that relative time ranges continue to move when the iframe loads, so a cached URL can still display fresh data.

Treat the URL as sensitive#

The embed URL is a bearer capability. Anyone who obtains it can load that chart.

  • do not include unfiltered sensitive data
  • do not log complete embed URLs in public telemetry
  • avoid placing them in public source code unless the chart is intended to be public
  • replace exposed URLs when the underlying view is sensitive

Use the data API for native UI#

Choose the data API when an iframe cannot match your design system, interaction, or accessibility requirements. Your server can request the same aggregate and return a reduced, authorized result to your component.

Before releasing an embed to customers, follow Production embedded analytics for tenant isolation, caching, loading states, accessibility, and operational tests.

Need a hand?

Tell us what you’re building and we’ll point you in the right direction.

Contact support