Skip to main content
← Guides

Charts in Email

End-to-end examples for embedding charts in HTML emails with Resend, Nodemailer, or any SMTP provider. Every example is copy-paste runnable.

How it works

Email clients load images with <img src="URL">. Chart-Output's GET endpoint renders and returns a chart image when a URL is requested — no JavaScript, no iframes. Just an image URL.

Because browsers and email clients cannot send Authorization headers with image requests, pass your API key as a ?key= query parameter.

Always use PNG for email. It has the broadest support across Gmail, Outlook, Apple Mail, and Yahoo. See the format compatibility table.

Simple embed (GET endpoint)

For charts with fewer than ~8 data points, a direct GET URL is the fastest path:

html
<img src="https://chart-output.com/api/v1/render?key=pk_live_YOUR_KEY&type=bar&labels=Jan,Feb,Mar,Apr&data=12000,15000,18000,22000&format=png&width=600&height=300" alt="Monthly revenue" width="600" height="300" style="display:block;max-width:100%;height:auto;" />

Keep URLs under 2000 characters. For complex specs, use POST + returnUrl (see below).

Resend

curl -X POST https://api.resend.com/emails \ -H "Authorization: Bearer re_YOUR_RESEND_KEY" \ -H "Content-Type: application/json" \ -d '{ "from": "reports@acme.com", "to": ["user@example.com"], "subject": "Your monthly report", "html": "<img src=\"https://chart-output.com/api/v1/render?key=pk_live_YOUR_KEY&type=bar&labels=Jan,Feb,Mar,Apr&data=12000,15000,18000,22000&format=png&width=600&height=300\" alt=\"Revenue chart\" width=\"600\" height=\"300\" />" }'

Nodemailer / SMTP

# Build the chart URL and use it in any SMTP tool CHART_URL="https://chart-output.com/api/v1/render?key=pk_live_YOUR_KEY&type=line&labels=Jan,Feb,Mar,Apr&data=100,115,108,130&format=png&width=600&height=300" echo "Embed: <img src=\"$CHART_URL\" />"

POST + returnUrl (complex specs)

For specs that exceed 2000 characters, POST the spec with returnUrl: true. You get back a stable CDN URL to embed in your email:

# POST and capture the URL RESPONSE=$(curl -s -X POST https://chart-output.com/api/v1/render \ -H "Authorization: Bearer pk_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"type":"bar","width":600,"height":300,"format":"png","returnUrl":true,"data":{"labels":["Jan","Feb","Mar"],"datasets":[{"data":[12000,15000,18000]}]}}') URL=$(echo $RESPONSE | python3 -c "import sys,json; print(json.load(sys.stdin)['url'])") echo "Embed URL: $URL"

Full card in email

Use padding: "email" to apply the email-optimized layout padding preset. Render at scale: 2 for retina output, with logical width/height attributes on the <img> tag.

json
{ "type": "line", "brandKitId": "obsidian", "width": 600, "height": 360, "format": "png", "scale": 2, "padding": "email", "backgroundColor": "#0d1117", "borderRadius": 12, "border": { "color": "rgba(255,255,255,0.08)", "width": 1 }, "header": { "eyebrow": "MONTHLY REPORT", "title": "$22,000", "subtitle": "↑ 22% from last month" }, "theme": { "textPrimary": "#f0f6fc", "textSecondary": "#6e7681" }, "data": { "labels": ["Jan", "Feb", "Mar", "Apr"], "datasets": [{ "label": "Revenue", "data": [12000, 15000, 18000, 22000], "borderColor": "#6ee7b7", "backgroundColor": "rgba(110,231,183,0.07)", "fill": true, "tension": 0.4, "borderWidth": 2, "pointRadius": 0 }] }, "footer": { "left": "acme.com", "right": "April 2026", "borderTop": true } }

POST this spec with returnUrl: true to get a CDN URL, then embed it with <img src="URL" width="600" height="360" />.

Troubleshooting

SymptomFix
Image blocked on first openNormal — email clients block images by default. Users click "Display images" once, then see them in all future emails.
Broken image in Outlook desktopCheck that your URL uses HTTPS. Verify the URL works in a browser before sending.
URL too long (> 2000 chars)Use POST + returnUrl for complex specs instead of GET query params.
Distorted chart (labels/data mismatch)Verify labels and data arrays have the same length. Test the URL directly in a browser.
401 error in imageYou cannot use Authorization headers in <img> tags. Use ?key= in the URL instead.