Open Access status in time

# libraries and imports
import sys
import requests
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('../matplotlib.mplstyle')

In this study, we will get data from OpenAlex to extract data on how the Open Access status of publications has evolved in the past 10 years.

# first, we define a handy function that will help us perform queries to the OpenAlex APIs.
def send_query(url, params, print_query=False):

        # perform the request using the specified URL and parameters
        r = requests.get(url, params)

        if print_query:
            print(r.request.url)

        # check the status code of the request
        if r.status_code != 200:

            if r.status_code == 414:
                raise ValueError("""API error: {}""".format(r.reason))
            else:
                import ast
                err = ast.literal_eval(r.text)
                raise ValueError("""API error:\n\tERROR  :  {error}\n\tMESSAGE:  {message}""".format(**err))
        else:

            # if there is no error return the JSON object for the results
            return r.json()
# then, we obtain the data, starting from MIN_YEAR and ending at MAX_YEAR, both included
MIN_YEAR = 2015
MAX_YEAR = 2025
OA_API_URL = 'https://api.openalex.org'
oa_status_year = {}
for year in range(MIN_YEAR, MAX_YEAR+1):
    # build the URL for the request
    URL = f'{OA_API_URL}/works'
    tmp = send_query(
        URL,
        params={'filter':f'type:article,publication_year:{year}', 'group_by':'oa_status'})['group_by']
    oa_status_year[year] = pd.DataFrame(tmp).set_index('key')['count'].rename('npapers')
oa_status_year = pd.DataFrame(oa_status_year)
oa_status_year.sum()
2015    7207568
2016    7246654
2017    6853936
2018    6810353
2019    7087018
2020    7227151
2021    6644701
2022    6043447
2023    6322328
2024    6655393
2025    7691611
dtype: int64
# prepare data
plot_df = (oa_status_year/1e6).T
order_status = plot_df.sum(axis=0).sort_values(ascending=False).index
plot_df = plot_df[order_status]

# init figure
fig, ax = plt.subplots(figsize=(7,3.5))

# color mapping
colors = {
    'closed':'red',
    'gold':'xkcd:gold',
    'bronze':'xkcd:bronze',
    'green':'green',
    'hybrid':'xkcd:light blue',
    'diamond':'xkcd:emerald'
}

for col in plot_df.columns:
    plot_df[col].plot(
        color=colors[col],
        linewidth=3,
        label=col.capitalize(),
        ax=ax
    )

ax.set_xlabel('Year')
ax.set_ylabel('Number of publications (M)')

# legend
leg = ax.legend(title='', ncols=6, loc='lower left', bbox_to_anchor=(0,1), handlelength=0.3, columnspacing=0.8)
for legobj in leg.legend_handles:
    legobj.set_linewidth(7.0)

ax.set_title('Evolution of number of publications per year by Open Access status', loc='left', pad=30, fontsize=12, fontweight='bold')
ax.grid(axis='y', linestyle='--', linewidth=0.5)
ax.text(2014.5, -1., "Source: OpenAlex\nData visualization by Ruggero Cortini", color='xkcd:dark grey',fontsize=8)

fig.savefig('n_year.png', dpi=600, bbox_inches='tight')