Travailler avec des API

L’utilisateur souhaite accéder à des données via une API.

ImportantTâche concernée et recommandation
  • En premier lieu, il est recommandé de vérifier s’il existe un package R spécifique à l’API que vous voulez utiliser.
  • S’il n’existe de package spécifique, il est recommandé d’utiliser le package httr2 pour transmettre des requêtes à l’API, qui remplace le package httr désormais superseded
  • httr2 permet nativement de gérer la transformation des retours au format JSON, mais au besoin pour des cas complexes le package jsonlite peut être nécessaire
  • Pour accéder à des données de l’Insee, il est recommandé d’utiliser l’un des packages suivants : doremifasol (fichiers sur insee.fr, API sirene), melodi (catalogue de données Melodi), insee (séries BDM).
  • Si nécessaire, les jetons d’accès (token) doivent être utilisés sous forme de variable d’environnements, inscrits dans un fichier .Renviron qui n’est pas partagé.

Rappels des notions essentielles sur les API

Qu’est-ce qu’une API ?

Une Application Programming Interface (ou API) est une interface de programmation qui permet d’utiliser une application existante pour restituer des données. Le terme d’API peut être paraître intimidant, mais il s’agit simplement d’une façon de mettre à disposition des données : plutôt que de laisser l’utilisateur consulter directement des bases de données (souvent volumineuses et complexes), l’API lui propose de formuler une requête qui est traitée par le serveur hébergeant la base de données, puis de recevoir des données en réponse à sa requête.

D’un point de vue informatique, une API est une porte d’entrée clairement identifiée par laquelle un logiciel offre des services à d’autres logiciels (ou utilisateurs). L’objectif d’une API est de fournir un point d’accès à une fonctionnalité qui soit facile à utiliser et qui masque les détails de la mise en oeuvre. Par exemple, l’API Sirene permet de récupérer la raison sociale d’une entreprise à partir de son identifiant Siren en interrogeant le référentiel disponible sur Internet directement depuis un script R, sans avoir à connaître tous les détails du répertoire Sirene.

À l’Insee comme ailleurs, la connexion entre les bases de données pour les nouveaux projets tend à se réaliser par des API. L’accès à des données par des API devient ainsi de plus en plus commun et est amené à devenir une compétence de base de tout utilisateur de données.

Avantages des API

Les API présentent de multiples avantages :

  • Les API rendent les programmes plus reproductibles. En effet, grâce aux API, il est possible de mettre à jour facilement les données utilisées par un programme si celles-ci évoluent. Cette flexibilité accrue pour l’utilisateur évite au producteur de données d’avoir à réaliser de multiples extractions, et réduit le problème de la coexistence de versions différentes des données.
  • Grâce aux API, l’utilisateur peut extraire facilement une petite partie d’une base de données plus conséquente.
  • Les API permettent de mettre à disposition des données tout en limitant le nombre de personnes ayant accès aux bases de données elles-mêmes.
  • Grâce aux API, il est possible de proposer des services sur mesure pour les utilisateurs (par exemple, un accès spécifique pour les gros utilisateurs).

Utilisation des API

Une API peut souvent être utilisée de deux façons : par une interface Web, et par l’intermédiaire d’un logiciel (R, Python…). Par ailleurs, les API peuvent être proposées avec un niveau de liberté variable pour l’utilisateur :

  • soit en libre accès (l’utilisation n’est pas contrôlée et l’utilisateur peut utiliser le service comme bon lui semble) ;
  • soit via la génération d’un compte et d’un jeton d’accès qui permettent de sécuriser l’utilisation de l’API et de limiter le nombre de requêtes.
WarningSpécificité Insee

Les API mises à disposition des utilisateurs par l’Insee se trouvent dans le catalogue des API.

Consulter l’interface Web d’une API

Les API peuvent proposer une interface Web, mais ce n’est pas toujours le cas. Cette interface permet notamment :

  • de s’inscrire aux différents services ;
  • de visualiser les différentes requêtes proposées par les services ;
  • de lancer l’API depuis cette plateforme ;
  • de proposer une documentation sur les API.

L’utilisation de l’interface Web est utile dans une démarche exploratoire mais trouve rapidement ses limites, notamment lorsqu’on consulte régulièrement l’API. L’utilisateur va rapidement se rendre compte qu’il est beaucoup plus commode d’utiliser une API via un logiciel de traitement pour automatiser la consultation ou pour réaliser du téléchargement de masse. De plus, l’interface Web n’existe pas systématiquement pour toutes les API.

Requêter une API

Le mode principal de consultation d’une API consiste à adresser une requête à cette API via un logiciel adapté (R, Python, Java…). Comme pour l’utilisation d’une fonction, l’appel d’une API comprend des paramètres qui sont détaillées dans la documentation de l’API. Voici les éléments importants à avoir en tête sur les requêtes :

  • Le point d’entrée d’un service offert par une API se présente sous la forme d’une URL (adresse web). Chaque service proposé par une API a sa propre URL. Par exemple, dans le cas de l’API Sirene, l’URL à utiliser pour obtenir des informations sur un Siren est : https://api.insee.fr/api-sirene/3.11/siren/.

  • Cette URL doit être complétée avec différents paramètres qui précisent la requête (par exemple l’identifiant Siren). Ces paramètres viennent s’ajouter à l’URL (qui peut donc devenir très longue!). Chaque service proposé par une API a ses propres paramètres, détaillés dans la documentation. S’agissant de l’API Sirene, l’utilisateur intéressé peut retrouver dans la documentation le paramétrage de l’URL.

  • Lorsque l’utilisateur soumet sa requête, l’API lui renvoie une réponse structurée contenant l’ensemble des informations demandées. Le résultat envoyé par une API est majoritairement aux formats JSON ou XML (deux formats dans lesquels les informations sont hiérarchisées de manière emboîtée). Plus rarement, certains services proposent une information sous forme plate (de type csv).

  • Du fait de la dimension hiérarchique des formats JSON ou XML, le résultat n’est pas toujours facile à récupérer. Certains packages, comme jsonlite ou xml2, facilitent l’extraction de champs d’une sortie d’API. Dans certains cas, des packages spécifiques à une API ont été créés pour simplifier l’écriture d’une requête ou la récupération du résultat.

Quelques packages permettant une utilisation simple des API des données Insee

Nous allons voir ici quelques packages permettant de traiter l’information d’une API facilement :

Le package doremifasol

Ce package permet d’importer facilement dans R des données mises à disposition par différents canaux sur le site de l’Insee.

Il permet entre autres de solliciter l’API Sirène dans une procédure de requêtage intégrée.

Pour cela, il faut au préalable avoir configuré un accès à l’API REST de l’Insee et passer en variable d’environnement le jeton d’authentification. Pour obtenir ce jeton, la procédure est expliquée par exemple ici ou encore . Une fois cela réalisé, on insère dans la variable d’environnement INSEE_API_TOKEN le jeton obtenu, par exemple en insérant la ligne suivante dans le fichier .Renviron (en remplaçant xxxxxxxx par la valeur du jeton) :

INSEE_API_TOKEN='xxxxxxxx'

N’oubliez pas d’ignorer le fichier .Renviron dans le dépôt Git pour ne pas partager votre jeton d’accès. Le ficher .Renviron est lu à chaque démarrage de R, donc il est nécessaire de redémarrer R pour que la variable d’environnement soit prise en compte.

Il reste ensuite à préciser le type d’information souhaitée (unités légales, établissements…) et la requête via l’argument argsApi.

Par exemple, pour lister tous les bouchers de la ville de Tourcoing :

bouchers_tourcoing <-
  doremifasol::telechargerDonnees(
    "SIRENE_SIRET",
    argsApi = list(
      q = "codeCommuneEtablissement:59599 AND activitePrincipaleUniteLegale:47.22Z"
      )
  )

Le package melodi

Ce package facilite l’utilisation des données et métadonnées diffusées par l’Insee sur le catalogue de données de l’Insee (melodi). Il permet de lister, filtrer, télécharger et accéder aux métadonnées de l’ensemble des jeux de données visibles sur le catalogue.

Le package s’appuie sur l’API melodi, accessible librement et donc sans besoin de configurer de jeton d’accès.

Une fois installé, pour lister les jeux de données proposés par l’Insee :

melodi::get_catalog()

Pour récupérer toutes les données d’un jeu de données par son identifiant :

data <- melodi::get_all_data("DS_POPULATIONS_REFERENCE")

Pour filtrer un jeu de données par critère géographiques, par exemple pour obtenir les données de toutes les communes d’un EPCI :

my_local_data_by_com <- melodi::get_local_data_by_com(
  ds_name = "DS_POPULATIONS_REFERENCE",
  geo = "244400404", # Nantes Métropole
  geo_object = "EPCI"
)

Le package insee

Ce package permet de télécharger les données et leurs métadonnées diffusées sur le service SDMX de la Base de données Macroéconomique de l’Insee (BDM). Cette API étant ouverte, son accès ne demande pas d’identification, ni de jeton. Il est uniquement nécessaire de déterminer les données souhaitées soit via une liste de catégories (idbank), soit via une liste de données (dataset).

Information Fonction
Liste des jeux de données insee::get_dataset_list()
Liste des séries insee::get_idbank_list()
# Importer les données à partir de leur idbank ou importer un dataset à partir de leur identifiant:
table_bp <- insee::get_insee_dataset("BALANCE-PAIEMENTS")
indicateur_001694056 <- insee::get_insee_idbank("001694056")

Il est possible de rajouter des filtres à ces fonctions afin de limiter le nombre de données importées (filtre sur la période, sur la date de mise à jour, sur les filtres).

Le package eurostat

Ce package permet de télécharger les données mises à disposition par Eurostat.

Le package OECD

Ce package permet de télécharger les données mises à disposition sur le site de l’OCDE. Cette API étant ouverte, son accès ne demande pas d’identification, ni de jeton. Il est uniquement nécessaire de déterminer les données souhaitées.

WarningSpécificité Insee

Ce package utilise la librairie rsmdx qui n’est pas compatible avec la technologie Direct Access. Il ne fonctionne pas sur poste en télétravail pour les postes nomades Insee qui accèdent à internet par ce biais. En revanche il fonctionne sur site.

Comme spécifié dans le lisez-moi du package, la meilleure façon d’utiliser ce package est d’utiliser l’explorateur de données de l’OCDE pour parcourir les jeux de données disponibles et filtrer des jeux de données spécifiques.

Dans cet exemple, nous allons utiliser les données des Comptes nationaux en bref, Chapitre 1 : PIB.

Après avoir filtré les données à l’aide de l’explorateur de données intégré, cliquez sur le bouton « API Développeur ».

Nous extrayons la première chaîne (représentant le jeu de données dans son ensemble) et la deuxième chaîne (représentant le filtre que nous avons appliqué) :

dataset <- "OECD.SDD.NAD,DSD_NAAG@DF_NAAG_I,1.0"
filter <- "A.USA+EU.B1GQ_R_POP+B1GQ_R_GR.USD_PPP_PS+PC."

Nous utilisons ensuite la fonction get_dataset pour récupérer les données :

df <- OECD::get_dataset(dataset, filter)
head(df)
  ACCOUNTING_ENTRY ADJUSTMENT CHAPTER CONF_STATUS CONSOLIDATION
1                B          N  NAAG_I           F            _Z
2                B          N  NAAG_I           F            _Z
3                B          N  NAAG_I           F            _Z
4                B          N  NAAG_I           F            _Z
5                B          N  NAAG_I           F            _Z
6                B          N  NAAG_I           F            _Z
  COUNTERPART_AREA COUNTERPART_SECTOR CURRENCY DECIMALS FREQ INSTR_ASSET
1                D                 S1       _Z        2    A          _Z
2                D                 S1       _Z        2    A          _Z
3                D                 S1       _Z        2    A          _Z
4                D                 S1       _Z        2    A          _Z
5                D                 S1       _Z        2    A          _Z
6                D                 S1       _Z        2    A          _Z
     MEASURE OBS_STATUS         ObsValue PRICE_BASE REF_AREA REF_YEAR_PRICE
1 B1GQ_R_POP          A 27316.3124870555         LR      USA           2020
2 B1GQ_R_POP          A 27862.1352063473         LR      USA           2020
3 B1GQ_R_POP          A  29014.619365484         LR      USA           2020
4 B1GQ_R_POP          A 30361.2628427727         LR      USA           2020
5 B1GQ_R_POP          A 29920.5822855034         LR      USA           2020
6 B1GQ_R_POP          A 29571.0880983905         LR      USA           2020
  SECTOR TIME_PERIOD TRANSACTION TRANSFORMATION UNIT_MEASURE UNIT_MULT
1     S1        1970    B1GQ_POP              N   USD_PPP_PS         0
2     S1        1971    B1GQ_POP              N   USD_PPP_PS         0
3     S1        1972    B1GQ_POP              N   USD_PPP_PS         0
4     S1        1973    B1GQ_POP              N   USD_PPP_PS         0
5     S1        1974    B1GQ_POP              N   USD_PPP_PS         0
6     S1        1975    B1GQ_POP              N   USD_PPP_PS         0

Nous sélectionnons les variables pertinentes :

df <- df |>
  subset(select = c(REF_AREA, MEASURE, UNIT_MEASURE, TIME_PERIOD, ObsValue)) |>
  transform(
    ObsValue = as.numeric(ObsValue),
    TIME_PERIOD = as.numeric(TIME_PERIOD)
  )

names(df) <- tolower(names(df))

head(df)
  ref_area    measure unit_measure time_period obsvalue
1      USA B1GQ_R_POP   USD_PPP_PS        1970 27316.31
2      USA B1GQ_R_POP   USD_PPP_PS        1971 27862.14
3      USA B1GQ_R_POP   USD_PPP_PS        1972 29014.62
4      USA B1GQ_R_POP   USD_PPP_PS        1973 30361.26
5      USA B1GQ_R_POP   USD_PPP_PS        1974 29920.58
6      USA B1GQ_R_POP   USD_PPP_PS        1975 29571.09

Les valeurs des variables measure et unit_measure ne sont pas immédiatement claires, nous récupérons donc un dictionnaire de données et l’intégrons au jeu de données.

La fonction get_data_structure renvoie une liste de dataframes avec des valeurs lisibles pour les noms et les valeurs des variables. Le premier dataframe contient les noms des variables et affiche les dimensions d’un jeu de données :

data_structure <- OECD::get_data_structure(dataset)

Utiliser une API sans package

Les exemples précédents proposaient l’accès à une API par le biais d’un package. Pour lire les données d’une API ne possédant pas de package, il faut utiliser le package httr2 pour lancer la requête et traiter son retour

Note

Selon la structure du JSON récupéré, la manipulation du résultat d’une requête peut être assez fastidieuse avec R. Python propose des outils plus performants pour retravailler des JSON (le package json notamment). Heureusement, grâce au package reticulate, il est aisé de faire tourner un code Python dans une session R et récupérer le résultat dans un format de données (par exemple data.frame) de R. L’approche par les API étant plus fréquente en Python qu’en R, on trouve également plus de packages facilitant l’accès à des données par ce biais en Python.

Le package httr2 (recommandé)

Le package httr2 permet de se connecter aux sites web et aux API. Il offre des fonctionnalités modernes comme le gestion des échecs et l’extraction directe des réponses JSON.

Accès à une API sans jeton

Un appel à une API répondant au format JSON via httr2 peut s’effectuer de la manière suivante :

resultats <- httr2::request(url) |>   # url à interroger
  httr2::req_retry(max_tries = 3) |>  # réessayer automatiquement en cas d'échec
  httr2::req_timeout(60) |>           # définir une limite de temps en secondes plus haute si besoin
  httr2::req_perform() |>             # effectuer la requête
  httr2::resp_body_json()             # extraire le corps de la réponse

Prenons par exemple l’API d’OpenFood Facts, une base de données alimentaire. Imaginons qu’on désire récupérer l’information sur un produit. Cela s’obtient de la manière suivante :

url <- "https://world.openfoodfacts.org/api/v0/product/3017620425400.json"

resultats <- httr2::request(url) |>   # url à interroger
  httr2::req_retry(max_tries = 3) |>  # réessayer automatiquement en cas d'échec
  httr2::req_timeout(60) |>           # définir une limite de temps en secondes plus haute si besoin
  httr2::req_perform() |>             # effectuer la requête
  httr2::resp_body_json()             # extraire le corps de la réponse

Le résultat JSON est formaté sous forme de liste imbriquée :

str(resultats, max.level = 1)
List of 4
 $ code          : chr "3017620425400"
 $ product       :List of 252
 $ status        : int 1
 $ status_verbose: chr "product found"

Pour en faire une information exploitable, il est nécessaire de retraiter le résultat de la requête. Par exemple, en extrayant ainsi manuellement les variables d’intérêt dans la liste (libellé, indice de transformation NOVA, nutriscore) :

dataset <- tibble::tibble(
  product_name      = resultats$product$product_name,
  nova_groups       = resultats$product$nova_groups,
  nutriscore_grade  = resultats$product$nutriscore_grade
)

dataset
# A tibble: 1 × 3
  product_name nova_groups nutriscore_grade
  <chr>        <chr>       <chr>           
1 Nutella      4           e               

Accès à une API avec jeton

Pour les API protégées par des jetons, il faut rajouter un paramètre d’identification. Les jetons d’authentification (token) étant des informations personnelles, il ne faut pas les faire figurer dans un script. Comme expliqué précédemment, ils peuvent être stockés sous forme de variable d’environnement, par exemple sous le nom MON_JETON_SECRET. Il suffit alors d’utiliser Sys.getenv pour récupérer la valeur derrière le nom MON_JETON_SECRET, puis d’ajouter le jeton dans l’en-tête HTTP (nommée X-INSEE-Api-Key-Integration pour les API de l’Insee).

library(httr2)

jeton <- Sys.getenv("MON_JETON_SECRET") # création d'une variable contenant le jeton

resultats <- request(url) |>
  req_headers(`X-INSEE-Api-Key-Integration` = jeton) |>  # ajout du jeton
  req_retry(max_tries = 3) |>                            # retry automatique
  req_timeout(60) |>                                     # timeout
  req_perform() |>                                       # exécution de la requête
  resp_body_json()                                       # extraction JSON

Exemple d’utilisation de l’API Métadonnées Insee

Une API sur les métadonnées de l’Insee est disponible sur le portail des API de l’Insee. Elle permet d’obtenir de manière simplifiée des métadonnées (sources, concepts, liste de codes géographiques).

Voici un exemple de requête pour obtenir des informations sur une catégorie juridique :

library(httr2)

url <- "https://api.insee.fr/metadonnees/codes/cj/n2/10"

resultats <- request(url) |>
  req_perform() |>             # exécution de la requête
  resp_body_json()             # extraction JSON

Exemple d’utilisation de l’API Sirene

Vous pouvez utiliser un fichier .Renviron pour stocker votre jeton de manière sécurisée, comme spécifié au début. Le fichier .Renviron est chargé au démarrage de R, rechargez votre session R après avoir modifié ce fichier pour que les changements prennent effet. Vous pouvez aussi utiliser la fonction Sys.setenv() pour définir temporairement votre jeton dans la session R en cours, mais cela ne sera pas persistant entre les sessions. Cet exemple va aller chercher les liens de succession pour un établissement :

library(httr2)

jeton <- Sys.getenv("INSEE_API_TOKEN") # création d'une variable contenant le jeton 

url <- "https://api.insee.fr/api-sirene/3.11/siret/liensSuccession?q=siretEtablissementPredecesseur:39478192600016"

resultats <- request(url) |>
  req_headers(`X-INSEE-Api-Key-Integration` = jeton) |> # ajout du jeton
  req_perform() |>                                    # exécution de la requête
  resp_body_json()                                    # extraction JSON

sortie <- as.data.frame(resultats$liensSuccession)

L’API Sirene permet d’effectuer des recherches multicritères. Dans ce cas, il faut séparer les codes par %20OR%20 (code HTML signifiant OR) :

url <- "https://api.insee.fr/api-sirene/3.11/siret/liensSuccession?q=siretEtablissementPredecesseur%3A39478192600016%20OR%20siretEtablissementPredecesseur%3A39488939800027"

resultats <- request(url) |>
  req_headers(`X-INSEE-Api-Key-Integration` = jeton) |> # ajout du jeton
  req_perform() |>                                    # exécution de la requête
  resp_body_json()                                    # extraction JSON

sortie <- as.data.frame(resultats$liensSuccession)

Le package jsonlite

Ce package propose principalement la fonction fromJSON qui permet de convertir une résultat en format json en un objet R. Il était nécessaire en complément de httr, mais désormais httr2 gère cette conversion nativement avec resp_body_json(). Son usage peut être nécessaire pour traiter des structures JSON complexes pour laquelle le traitement par httr2 ne convient pas, dans ce cas retourner un résultat sous forme textuel, puis le traiter avec jsonlite :

library(httr2)
library(jsonlite)
resp_text <- httr2::request(url) |>
  httr2::req_perform() |>
  httr2::resp_body_string()

resultats <- jsonlite::fromJSON(resp_text, flatten = TRUE)

Limites de requêtes et temporisation

Pour l’utilisation d’API comme celles proposées sur le catalogue des API de l’Insee, le nombre de requêtes autorisées par minute est limité. La fonction req_retry de httr2, utilisée dans les exemples précédents, permet de gérer automatiquement et intelligemment la relance des requêtes en cas de quota de requêtes atteint. Une alternative peut aussi consister à temporiser les appels successifs en introduisant une latence avec Sys.sleep. Par exemple, pour laisser 2 secondes d’attente entre chaque requête, ajouter Sys.sleep(2).

(déprécié, privilégier désormais httr2) Le package httr

Le package httr permet de se connecter aux sites web et de se connecter aux API.

Il est possible de configurer la connexion internet localement sans modifier les variables système :

  • set_config() permet de configurer l’accès internet utilisée par les fonctions du package.
  • use_proxy() permet de déterminer le proxy à utiliser. De nombreuses institutions passent par un intermédiaire, le proxy, pour accéder à internet. L’adresse du proxy est à ajouter aux requêtes car sinon R ne sait pas communiquer avec internet. Il s’agit d’un paramètre à ajouter dans les options httr.
Note

Sous windows, le proxy peut être paramétré de la manière suivante:

proxy <- curl::ie_get_proxy_for_url()
httr::set_config(httr::use_proxy(proxy))

Le package httr permet, lorsqu’on effectue une requête GET (une requête d’accès au résultat d’une recherche), de récupérer le résultat sous la forme d’un texte à retravailler.

Accès à une API sans jeton avec httr

En général, un appel à une API via httr s’effectue ainsi de la manière suivante :

httr::content(
  httr::GET(url),             # url correspond à l'url à interroger
  as = "text",                # type de la sortie renvoyée
  httr::content_type_json(),  # type de la réponse de l'url
  encoding = "UTF-8"          # encodage de la réponse de l'url
)

Prenons par exemple l’API d’OpenFood Facts, une base de données alimentaire. Imaginons qu’on désire récupérer l’information sur un produit. Cela s’obtient de la manière suivante :

url <- "https://world.openfoodfacts.org/api/v0/product/3017620425400.json"

resultats <- 
  httr::content(
    httr::GET(url),             # url correspond à l'url à interroger
    as="text",                  # type de la sortie renvoyée
    httr::content_type_json(),  # type de la réponse de l'url
    encoding= "UTF-8"            # encodage de la réponse de l'url
  )

Le résultat est formaté sous forme de JSON, ce qui est pratique mais peu intelligible. Pour en faire une information exploitable, il est nécessaire de retraiter le résultat de la requête. Par exemple, pour n’extraire que le libellé et le nutriscore d’un produit, ainsi que son indice de transformation NOVA, il faut utiliser une boucle sur les différentes caractéristiques à extraire :

df <- data.frame(
  lapply(c("product_name","nova_groups","nutriscore_grade"), function(x){
    jsonlite::fromJSON(resultats, flatten = TRUE)$product[[x]]
  })
)
colnames(df) <- c("product_name","nova_groups","nutriscore_grade")
df
  product_name nova_groups nutriscore_grade
1      Nutella           4                e

Accès à une API avec jeton avec httr

Pour les API protégées par des jetons, il faut rajouter un paramètre d’identification. Les jetons d’authentification (token) étant des informations personnelles, il ne faut pas les faire figurer dans un script. Comme expliqué précédemment, ils peuvent être stockés sous forme de variable d’environnement, par exemple sous le nom MON_JETON_SECRET. Il suffit alors d’utiliser Sys.getenv pour récupérer la valeur derrière le nom MON_JETON_SECRET :

jeton <- Sys.getenv("MON_JETON_SECRET") # création d'une variable contenant le jeton

auth_header <- httr::add_headers('Authorization'= paste('Bearer',jeton)) # création d'une variable d'authentification

res <- httr::content(httr::GET(url),
                     auth_header, # ajout de la variable d'authentification
                     as="text", 
                     httr::content_type_json(), 
                     encoding='UTF-8')