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().

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.
