How to create and send charts in email

Email is a ubiquitous format, but it comes with limitations. Mail clients like Gmail and Outlook remove Javascript, SVG, and other alternatives. This means in order to include a chart or graph in email, you must render it as an image.

This tutorial explains how to use the open-source QuickChart project to display a simple bar chart in the body of an email.

Normally, rendering dynamic charts at scale is resource-intensive, requiring a server render farm, headless browsers, and other configuration.

Production infrastructure for chart rendering

We will remove this complexity by using a service that renders graphs for us. A specially constructed URL sent to the QuickChart API will return a chart image. Because it’s just a matter of building a URL, it’s easy to do in your existing programming environment.

By changing the URL, you can make the chart dynamic. The API is built on Chart.js, the most popular frontend charts library, which means it’s very easy to customize your chart.

A simple chart in an email

Render the chart image

The main QuickChart endpoint is Use this as the base URL, then add some query parameters:

  • chart: The Chart.js object (required). Check out the Chart.js docs for many different chart types and customization options and use the sandbox editor to test out your configs.
  • width: Width of chart in pixels (optional)
  • height: Height of chart in pixels (optional)
  • devicePixelRatio: Ratio between physical pixels and logical pixels. Defaults to 2.0 for retina support (optional)
  • View more parameters in the documentation

Let’s suppose you built your Chart.js config and it looks like this:

type: 'bar',
data: {
labels: [2012, 2013, 2014, 2015, 2016],
datasets: [{
label: 'Users',
data: [120, 60, 50, 180, 120]

As you can see, we’re building a graph of users from 2012 through 2016. You can preview this graph by pasting the config into the testing sandbox.

Take the Chart.js config and put it into the main endpoint URL:{type:'bar',data:{labels:[2012,2013,2014,2015,2016],datasets:[{label:'Users',data:[120,60,50,180,120]}]}}

This URL will render the chart when your browser loads it. To include it in an email, all we need to do is put the URL in an image tag:

<img src="{type:'bar',data:{labels:[2012,2013,2014,2015,2016],datasets:[{label:'Users',data:[120,60,50,180,120]}]}}" />

This displays the chart image, like so:

You can safely embed this chart directly in your email. It doesn’t require any Javascript, and any email client like Gmail or Outlook will display your chart.

Email a chart in any programming language

Because a chart is just defined by its URL, you may construct a QuickChart URL in any programming language. Follow the instructions above to build your chart configuration, then put it in an <img> tag in your email.

This lightweight approach avoids third-party dependencies. That said, QuickChart has published libraries for a few languages. See the most up-to-date list here. We’ve also included many examples below.

Once you’ve generated the chart, you can use any existing email service (such as Mailgun or Sendgrid) to send an email.

For more advanced chart use cases, you may use your language’s HTTP library in order to generate short URLs for your chart.

Email charts with Python

Here’s how to use Python to generate and email charts. First, build your Chart.js config. In this example, we’ll display a simple bar graph:

chart_config = {
'type': 'bar',
'data': {
'labels': ['Week 1', 'Week 2', 'Week 3', 'Week 4'],
'datasets': [{
'label': 'Retweets',
'data': [12, 5, 40, 5]
}, {
'label': 'Likes',
'data': [80, 42, 215, 30]

Now, let’s build our email message as a string. In order to build the image chart URL, we’ll convert the chart config to JSON and URL encode it.

import json
from urllib.parse import quote

encoded_config = quote(json.dumps(chart_config))
chart_url = f'{encoded_config}'

email_message = f"""Hello, this is my email body containing a chart image. Please see the chart below:
<img src="
{chart_url}" />

Now that we have created the email body, the next step is to decide how we want to send the email. You can do this using any method that’s already available to you in Python, whether it’s using your own SMTP server, Gmail, or an email-sending service like Mailgun or Sendgrid.

To finish it off, let’s just use the Mailgun API:

import requests

request_url = f'{DOMAIN}/messages'
request =, auth=('api', 'your_api_key_here'), data={
'from': '[email protected]',
'to': '[email protected]',
'subject': 'Updated Chart Report with Mailgun',
'html': email_message,

print(f'Status: {request.status_code}')
print(f'Body: {request.text}')

Or using Sendgrid’s Python library:

from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail

message = Mail(
from_email='[email protected]',
to_emails='[email protected]',
subject='Updated Chart Report with Sendgrid',
sg = SendGridAPIClient('your_api_key_here')
response = sg.send(message)
except Exception as e:

Email charts with Javascript/Node.js

Sending charts using Javascript or node.js is straightforward. You can either build the URL as described above, or npm install quickchart-js.

Use the library like so. First, generate the URL for your chart image and build your email message:

const QuickChart = require('quickchart-js');

const myChart = new QuickChart();
type: 'bar',
data: { labels: ['Hello world', 'Foo bar'], datasets: [{ label: 'Foo', data: [1, 2] }] },

const chartImageUrl = myChart.getUrl();

const message = `Hello, please see the chart below:
<img src="
${chartImageUrl}" />

Then, send the email with Mailgun or some other preferred email library:

const mailgunGenerator = require('mailgun-js');
const mailgun = mailgunGenerator({
apiKey: 'abc123',
domain: '',

const data = {
from: '[email protected]',
to: '[email protected]',
subject: 'Updated Chart Report',
html: message,

mailgun.messages().send(data, (err, body) => {

Here’s the Sendgrid equivalent:

const sgMail = require('@sendgrid/mail');
const msg = {
to: '[email protected]',
from: '[email protected]',
subject: 'Updated Chart Report',
html: message,

Email charts with Java

In order to email a chart with Java, first create a string containing our Chart.js config.

You can build this JSON string dynamically if you’d like - use normal string manipulation and concatenation to substitute your data. Here’s a simple example of a Chart.js config with some hardcoded values:

String chartConfig = """
"type": "bar",
"data": {
"labels": [2012, 2013, 2014, 2015, 2016],
"datasets": [{
"label": "Users",
"data": [120, 60, 50, 180, 120]

Suppose we wanted to replace the data with dynamic values from an array. Here’s one approach:

String chartConfigTemplate = """
"type": "bar",
"data": {
"labels": [2012, 2013, 2014, 2015, 2016],
"datasets": [{
"label": "Users",
"data": [ %DATA_VALUES% ]

List<Integer> values = new ArrayList<Integer>();

String chartConfig =
chartConfigTemplate.replace("%DATA_VALUES%", dataVals.toString());

You can also build the JSON completely dynamically using org.json or a similar library.

Now that we have our chart config, pack this into the QuickChart API URL. We use

String chartUrl =
"" +
URLEncoder.encode(chartConfig, StandardCharsets.UTF_8);

String message =
"Hello, please see the chart below:<br><br><img src=\"" + chartUrl + "\">";

That’s it for generating the chart. Now all we need to do is send the chart image in the body of the email:

import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;

public class MGSample {
public static JsonNode sendSimpleMessage() throws UnirestException {

// ... Generate the chart image

// Send the email
HttpResponse<JsonNode> request ="" + YOUR_DOMAIN_NAME + "/messages")
.basicAuth("api", API_KEY)
.field("from", "Bob Smith <[email protected]>")
.field("to", "[email protected]")
.field("subject", "Chart Report")
.field("html", message)
return request.getBody();

See here for the equivalent Java Sendgrid example.

Email charts with C#

To email a chart in C#, first we create our Chart.js config. Basic chart configs are JSON, so you can use any method of creating JSON objects in C# that you prefer. In this case, we’ll just put the config directly in our source code:

String chartConfig = @"{
""type"": ""bar"",
""data"": {
""labels"": [2012, 2013, 2014, 2015, 2016],
""datasets"": [{
""label"": ""Users"",
""data"": [120, 60, 50, 180, 120]

Next, put this chart config object in the QuickChart API URL and build our message:

String chartUrl = "" + Uri.EscapeDataString(chartConfig);

String message = $"Hello, please see the chart below:<br><br><img src=\"{chartUrl}\">"

Now that we’ve constructed our email body, all we need to do is send the email. This part is easy if you’re using a third-party email API. Here’s an example using Sendgrid C#:

var apiKey = "your_api_key_here";
var client = new SendGridClient(apiKey);
var msg = new SendGridMessage()
From = new EmailAddress("[email protected]", "My Name"),
Subject = "Chart Report Update",
HtmlContent = message
msg.AddTo(new EmailAddress("[email protected]", "Recipient Name"));
var response = await client.SendEmailAsync(msg).ConfigureAwait(false);

Email charts with PHP

To get started with sending chart emails in PHP, build a simple JSON object that defines your chart. Here’s a simple example:

$chartConfig = '{
"type": "bar",
"data": {
"labels": [2012, 2013, 2014, 2015, 2016],
"datasets": [{
"label": "Users",
"data": [120, 60, 50, 180, 120]

This is a static example, but let’s suppose you wanted to fill your data object automatically using the contents of an array. It might look something like this:

$data = array(120, 60, 50, 180, 120);

$chartConfig = '{
"type": "bar",
"data": {
"labels": [2012, 2013, 2014, 2015, 2016],
"datasets": [{
"label": "Users",
"data": ['
. implode(',', $data) . ']

You can even build the whole thing as a nested PHP array and get the string using json_encode:

$chartConfigArr = array(
'type' => 'bar',
'data' => array(
'labels' => array(2012, 2013, 2014, 2015, 2016),
'datasets' => array(
'label' => 'Users',
'data' => array(120, 60, 50, 180, 120),
$chartConfig = json_encode($chartConfigArr);

Once you have a chart configuration that you’re happy with, we are ready to render it as an image. This creates a chart with width of 500 pixels and height of 300 pixels:

$chartUrl = '' . urlencode($chartConfig);

We just need to take this URL and include it as an image tag in our email. Here’s an example using the PHP Sendgrid library:

$email = new \SendGrid\Mail\Mail();
$email->setFrom("[email protected]", "From User");
$email->setSubject("Chart Report Update");
$email->addTo("[email protected]", "Recipient User");
"text/html", "Please see the chart below:<br><br><img src=\"$chartUrl\">"
$sendgrid = new \SendGrid('your_api_key_here');
try {
$response = $sendgrid->send($email);
print $response->statusCode() . "\n";
print $response->body() . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";

Advanced features

The process of building a chart image via URL is quite straightforward, but there are many ways to customize your chart. For starters, you can use the Chart.js configuration to make many other types of charts: line graphs, pie charts, mixed charts, and more.

If you’re creating more complex charts, consider generating a short URL for your chart so you don’t have to pack everything into the image tag.

You can also use a variety of Chart.js plugins to add more customizations annotating your chart images.

For more advanced usage and many other chart types and customization, view the documentation.

There are lots of ways to make your chart stand out in an email

Need help?

If you have questions or need help with your chart rendering, please reach out.

Ian Webster

About the author

Ian Webster is a software engineer and former Googler based in San Mateo, California. He has helped Google, NASA, and governments around the world improve their data pipelines and visualizations. In 2018, Ian created QuickChart, a collection of open-source APIs that support data visualization efforts.

Email · LinkedIn · Twitter