Tools & Practices

How to automatically create dozens of charts using the Datawrapper API

Go to the story

We’ve created dozens of charts that update automatically every day without any intervention on our end, thanks to a few lines of Python code and the Datawrapper API.

Producing a large number of visualizations in a short period of time is often difficult and tedious, but also unavoidable, when presenting data in journalism.

To elaborate on the story and present a more global view of the data that make up the report, we may need to create separate charts or maps for each detail, be it temporal, geographical, and so on. The process becomes more complicated if it must be repeated at regular intervals whenever the data is updated.

One way to avoid such a time-consuming process is to use the Datawrapper API, which allows us to create charts with just a few lines of Python code.

We used this approach to visualize fuel price data by prefecture from the beginning of 2026 in iMEdD’s article on fuel prices. Since Greece is divided into dozens of geographical units and the analysis had to be carried out for four different fuel types, manually entering the data on a daily basis was not a viable solution.

The first step

The first step in using the Datawrapper API is creating a key without which you cannot proceed further.

The process is easy. While logged in to your account, go to the Datawrapper main menu (dashboard) and hover your mouse over your account name in the top right corner of the screen. Then, a dropdown menu will appear with the option “Settings & Account”. Clicking this option will take you to your account’s options interface.

In the top left corner of the screen, you will see the “API Tokens” option. Clicking on it will take you to the “API Access Tokens” interface. The “Create Access Token” button is located there. The access token is your key. Save it somewhere safe right away because you won’t be able to see it again after it first appears.

Chart with a few lines of code

Creating charts with Python is not difficult if we use an LLM AI model. If you don’t have a code editor installed on your computer, don’t worry. You can write in Google Colab, a Google tool that runs in your browser.

The steps to create a simple line chart (line graph):

  • Open a new notebook in Google Colab
  • Install the Datawrapper library in your runtime by typing in the following code: !pip install datawrapper
  • Install any other libraries you may need (import pandas as pd, import os, etc.)
  • Data source. This could be a URL, but it could also be a dataframe created in Google Colab from our own data or an Excel file, and so on.
  • Set the line chart and its variables with dw.LineChart()
  • Last step is to create the chart with create()

Example:

 
# Import necessary libraries 

import pandas as pd 

import datawrapper as dw 

import os 

 

# Add the token to the environment 

API_KEY = “insert_your_key_here” 

os.environ["DATAWRAPPER_ACCESS_TOKEN"] = API_KEY 

 

# Load sample data about temperatures 

df = pd.DataFrame({ 

    "Year": list(range(2000, 2011)), 

    "LandAverageTemperature": [14.3, 14.5, 14.6, 14.8, 15.0, 15.1, 15.3, 15.4, 15.6, 15.7, 15.9] 

}) 

 

chart = dw.LineChart( 

    # Chart title 

    title="Global land temperature in July, 2000-2011", 

    # Data source attribution 

    source_name="Berkeley Earth", 

    source_url="http://berkeleyearth.org/data/", 

    # Get the data from the dataframe 

    data=df, 

    # Set the suffix on our values 

    transformations=dw.Transform( 

        column_format=[ 

            dw.ColumnFormat( 

                column="LandAverageTemperature", 

                number_append=" °C", 

            ) 

        ] 

    ), 

    # Set the Y-axis range 

    custom_range_y=[8, 21], 

    # Format Y-axis grid labels with no decimal places 

    y_grid_format="0", 

    # Tooltip format 

    tooltip_number_format="00.00", 

    tooltip_x_format="YYYY", 

    # Configure the main temperature line's color 

    color_category={ 

        "LandAverageTemperature": "#1d81a2", 

    }, 

    lines=[ 

        # Style the main line 

        dw.Line( 

            column="LandAverageTemperature", 

            width=dw.LineWidth.THIN, 

            interpolation=dw.LineInterpolation.CURVED, 

        ) 

    ] 

) 

# Last step. Create the chart 

chart.create() 

The first step happens with dw.LineChart(), which gathers all the variables we want our chart to have in one variable (in this case the “chart”. Here we place the variables we want to set for the chart, such as the title and the data source, but also the style of the line, the content of the tooltip, etc.

The last thing to do once we have defined the variables in the chart is to type the command .create().

A chart image generated using the Datawrapper API and displayed via a “hover” effect over the Attica region on the map above. The image shows the price trend for Unleaded 95 gasoline in the Attica region since the beginning of 2026.

How we made images refreshed from dozens of charts

For the charts that visualize historical fuel prices by region, we used GitHub and the Python programming language to automate a process that runs daily.

Using a .csv file with fuel prices by prefecture as a data source, we created charts for each region and each fuel type. Each created chart has a unique code (“id”) that makes it easy to identify. In our case, the purpose was to update the data. Therefore, the code was divided into two parts:

1) creating the charts and
2) updating them.

Initial creation of charts

In the first part of the project, we created a Python script that generates charts for each unique region in the “prefecture” column of the .csv file, as well as for each fuel type. Next, we identify each fuel type with a specific color code. The chart ids, as well as the region name and fuel types are stored in a .json file to be used by the next Python script, in the next part of the project.

Several variables have been placed inside the dw.LineChart() to fulfill the specific requirements of our project.

In our case, for example, we did not provide a title. That is why the “title=” line is missing. At the same time, we chose to display only February 28on the x-axis (custom_ticks_x = [“2026-02-28”]) because we want to visually distinguish the price development before and after the start of the Middle East conflict.

Part of the script for the creation of charts

 
# Data source URL
CSV_URL = "https://raw.githubusercontent.com/iMEdD-Lab/fuel_prices_data/refs/heads/main/prefectures/update_master/master_pref_upd.csv"

# Map each fuel type to a color
FUEL_COLORS = {
    "Αμόλυβδη 95": "#08A021",
    "Αμόλυβδη 100": "#FFCB6F",
    "Diesel Κίνησης": "#15607a",
    "Autogas": "#F54927"
}

CHART_CONFIG_FILE = "ids/chart_config.json"
Path(CHART_CONFIG_FILE).parent.mkdir(parents=True, exist_ok=True)


# LOAD CSV
df = pd.read_csv(CSV_URL)
df['date'] = pd.to_datetime(df['date'], errors='coerce')
df = df[df['date'] >= '2026-01-01']

# Ensure all fuel columns are numeric
for fuel_type in FUEL_COLORS.keys():
    if fuel_type in df.columns:
        df[fuel_type] = pd.to_numeric(df[fuel_type], errors='coerce')


# LOAD EXISTING CONFIG JSON IF EXISTS
config_list = []
if Path(CHART_CONFIG_FILE).exists():
    with open(CHART_CONFIG_FILE, "r", encoding="utf-8") as f:
        config_list = json.load(f)

# LOOP THROUGH PREFECTURES AND FUEL TYPES
prefectures = df['prefecture'].dropna().unique()

for pref in prefectures:
    df_pref = df[df['prefecture'] == pref]

    for fuel_type, color in FUEL_COLORS.items():
        if fuel_type not in df_pref.columns:
            continue

        df_chart = df_pref[['date', fuel_type]].dropna()
        if df_chart.empty:
            print(f"No data for {fuel_type} in {pref}, skipping...")
            continue

        # Compute dynamic lower Y-axis for this fuel type in this prefecture
        min_val = df_chart[fuel_type].min()
        lower_y = math.floor(min_val / 0.15) * 0.15  # round down to nearest 0.15

        
        # CREATE LINE CHART
       
        chart = dw.LineChart(
            source_name="Υπουργείο Ανάπτυξης",
            source_url="https://github.com/pmethodios/fuel_prices",
            data=df_chart,
            x_column="date",
            y_grid_format="0.00",
            custom_range_y=[lower_y, None],
            custom_ticks_y=[1.0, 1.25, 1.5, 1.75, 2.0],  
            tooltip_x_format="DD-MM-YYYY",
            show_tooltips=True,
            dark_mode_invert=True,
            label_colors=True,
            color_category={fuel_type: color},
            x_grid="off",
            x_grid_format="DD/MM",
            custom_ticks_x=["2026-02-28"]
        )

       
        # ADD WIDE LINE
        chart.lines.append(
            dw.Line(
                column=fuel_type,
                width=dw.LineWidth.THICK,
                interpolation=dw.LineInterpolation.CURVED,
                show_first_value_label=True,
                show_last_value_label=True,
                show_symbols=True,
                line_symbol="circle"
            )
        )

    
        # CREATE CHART ON DATAWRAPPER
        result = chart.create()
        chart_id = result.chart_id

  
        # SAVE JSON CONFIG
        config_list.append({
            "chart_id": chart_id,
            "prefecture": pref,
            "fuel_type": fuel_type
        })


# SAVE UPDATED CONFIG LIST
with open(CHART_CONFIG_FILE, "w", encoding="utf-8") as f:
    json.dump(config_list, f, ensure_ascii=False, indent=2)


Updating charts and saving images

In another Python script we placed commands that make our project “live” by updating the data almost daily.

We started with the same process of defining the key for the API. In this file, however, in this step we use as an additional data source: the .json file containing the ids of the charts we want to update.

The data for all charts is updated through a “for loop” that runs through each id and filters the data by region name and fuel type.

 
# Load the data from the .csv url
df = pd.read_csv(CSV_URL)
df['date'] = pd.to_datetime(df['date'], errors='coerce')

# Ensure numeric fuel columns
for fuel_type in FUEL_COLORS.keys():
    if fuel_type in df.columns:
        df[fuel_type] = pd.to_numeric(df[fuel_type], errors='coerce')


# UPDATE EACH CHART
for chart_info in chart_mappings:
    chart_id = chart_info["chart_id"]
    prefecture = chart_info["prefecture"]
    fuel_type = chart_info["fuel_type"]

    # Filter CSV for this prefecture and fuel type, from Jan 1, 2026
    df_chart = df[(df["prefecture"] == prefecture) & (df["date"] >= "2026-01-01")]
    df_chart = df_chart[["date", fuel_type]].dropna()

    # Skip if no data
    if df_chart.empty:
        print(f"No data for {prefecture} - {fuel_type}, skipping...")
        continue

   
    # ADD PREFECTURE AS A COLUMN
    df_chart['prefecture'] = prefecture


    # CALCULATE PER-FUEL MIN VALUE FOR Y-AXIS
    min_val = df_chart[fuel_type].min()

    # Second closest lower tenth
    lower_y = math.floor(min_val * 10) / 10 - 0.1
    lower_y = max(lower_y, 0)
    

    # ASSIGN COLOR
    color_category = {fuel_type: FUEL_COLORS.get(fuel_type, "#000000")}  # default black if missing


    # UPDATE CHART
    chart = dw.LineChart(
        chart_id=chart_id,
        data=df_chart,
        x_column="date",
        color_category=color_category,
        y_grid_format="0.00",
        custom_range_y=[lower_y, None],
        tooltip_x_format="DD-MM-YYYY",
        show_tooltips=True
    )

    chart.lines.clear()
    chart.lines.append(
        dw.Line(
            column=fuel_type,
            width=dw.LineWidth.MEDIUM,
            interpolation=dw.LineInterpolation.CURVED,
            show_first_value_label=True,
            show_last_value_label=True,
            show_symbols=True,
            line_symbol="circle"
        )
    )

    chart.update()

Images

The last step after updating the charts is the creation of images. With the goal of visualizing the prices for each region and each fuel type from the start of 2026 when the user passes the mouse over each area of the map, we needed to convert the charts into images (.png files).

This approach was chosen because it is not possible to integrate interactive charts into tooltips in Datawrapper. Thus, we chose to create .png image files for each region and fuel type.

This process is done with the command “.export_png()”.

 
    #Export image from chart
    png_data = chart.export_png()
    # Save the data to path
    with open(local_png_path, "wb") as f:
        f.write(png_data)

In our case we store all these images in the temp_files folder, and the files are named according to the name of the county and the type of fuel they analyze. In this way, each image file is contained in a unique url, which then is visualized in the tooltip of the maps.

The full documentation on creating a line chart with Datawrapper API can be found in this link.

Creative Commons license logo