Récupérer articles, auteurs et nombre de citations depuis Google Scholar avec Python

Interrogez Google Scholar sur un sujet et récupérez articles, années et nombre de citations en JSON, conçu autour d'un endpoint assez lent pour devoir tourner en tâche de fond. Testé en août 2026.

Vous construisez une revue de littérature, ou un dashboard qui suit un sujet de recherche dans le temps, et il vous faut les articles : titre, auteurs, année, nombre de fois où chacun a été cité. La méthode par défaut, c’est un onglet de navigateur sur Google Scholar et un tableur que vous remplissez à la main, une page de résultats à la fois. Google Scholar n’a pas d’API publique officielle, donc la plupart des outils autour se résument à ça : une personne qui recopie des lignes.

La requête

Un appel à /v1/scholar prend la même requête que celle que vous taperiez dans la barre de recherche, plus quelques paramètres optionnels : language, page, results, et as_ylo / as_yhi si vous voulez que Scholar restreigne lui-même la réponse à une plage d’années avant que quoi que ce soit ne vous revienne.

Ça tourne près de huit secondes, donc ne l’attendez pas

Dans des mesures effectuées le 27 août 2026, /v1/scholar avait un temps de réponse médian d’environ 7,9 secondes, le plus lent des endpoints que nous vendons. Ce n’est pas un bug à contourner, c’est le prix à payer pour interroger un index de littérature aussi large. Concevez votre système en conséquence : récupérez un lot de sujets dans un job en arrière-plan ou une file d’attente, écrivez le JSON dans votre propre stockage, et faites lire votre dashboard ou votre export depuis ce stockage. Ne mettez pas cet endpoint sur un chemin de requête où quelqu’un regarde une roue qui tourne.

Articles, années et citations depuis le même JSON

Voici une requête pour « web scraping api » qui filtre les articles à partir de 2020 et trie ce qui reste par nombre de citations :

import re
import requests

API_KEY = "faf_live_..."  # votre clé FastAF

resp = requests.get(
    "https://api.fastaf.co/v1/scholar",
    params={"query": "web scraping api"},
    headers={"Authorization": f"Bearer {API_KEY}"},
    timeout=60,
)
resp.raise_for_status()
papers = resp.json()["scholar_results"]

def citation_count(paper):
    total = paper.get("inline_links", {}).get("cited_by", {}).get("total", "")
    match = re.search(r"\d+", total)
    return int(match.group()) if match else 0

def pub_year(paper):
    match = re.search(r"\b(19|20)\d{2}\b", paper.get("displayed_link", ""))
    return int(match.group()) if match else None

recent = [p for p in papers if (pub_year(p) or 0) >= 2020]
recent.sort(key=citation_count, reverse=True)

for p in recent:
    authors = ", ".join(a["name"] for a in p.get("authors", []))
    print(f'{citation_count(p):>5}  {pub_year(p)}  {p["title"]}')

Nous avons exécuté exactement ça en écrivant l’article (août 2026). Dix articles sont revenus pour 5 crédits ; six dataient de 2020 ou après, et le tri par citations a placé en tête une étude de 2021 sur les techniques de web scraping avec 651 citations, devant un article de 2024 à 14 citations et un article de 2025 sans aucun nombre de citations, que le code ci-dessus compte correctement comme zéro au lieu de planter dessus.

Ce qui est solide, et ce que vous devez calculer vous-même

Chaque entrée de scholar_results vous donne title, title_link, un snippet, et une liste authors de {name, link, author_id} quand Scholar attache des auteurs nommés au résultat (les livres et quelques résultats de notre échantillon sont revenus avec une liste vide). Deux choses que les gens attendent ne sont pas là comme champs propres. Il n’y a pas d’year autonome : elle est intégrée dans la chaîne en texte libre displayed_link, avec la revue, ce qui explique pourquoi le code ci-dessus l’extrait avec une regex plutôt que de lire une clé. Et il n’y a pas non plus de simple entier pour le nombre de citations, juste inline_links.cited_by.total, une chaîne qui affiche “Cited by 418” pour la plupart des résultats mais affichait “Related articles” pour une entrée de notre échantillon, ce qui veut dire que ce résultat n’a aucun nombre de citations à montrer. Traitez les deux comme du texte extrait au mieux d’une chaîne de citation, pas comme des champs structurés garantis, et écrivez le cas « absent » avant d’écrire le cas nominal. Quand un résultat a un PDF disponible, ça apparaît sous resources.

La même requête ne renvoie pas le même nombre d’articles

Celle-là va vous mordre si vous faites comme si elle n’existait pas. Le 27 août 2026, nous avons envoyé la requête identique, graph neural networks, six fois de suite via /v1/scholar. Les nombres de résultats obtenus ont été 5, 10, 10, 8, 0 et 10. Un appel sur six n’a rien renvoyé du tout, et il a été facturé comme n’importe quel autre appel, parce que la requête elle-même a réussi : Scholar n’avait tout simplement rien à donner à ce moment-là.

Donc ne traitez pas une liste vide comme une réponse, et ne traitez pas le nombre de résultats comme stable. Réessayez une fois après une courte pause avant d’enregistrer « aucun article trouvé », et si vous comparez les résultats d’aujourd’hui à ceux de la semaine dernière, comparez article par article plutôt que par nombre, sinon vous allez générer des alertes sur des articles qui apparaissent et disparaissent alors qu’ils n’ont jamais vraiment disparu.

import time


def search_with_retry(query, attempts=2):
    for attempt in range(attempts):
        papers = search(query)          # la fonction de la section précédente
        if papers:
            return papers
        time.sleep(2)
    return []

Prévoyez un budget pour les réessais : à 5 crédits l’appel, un taux d’échec vide d’un sur six ajoute environ 17 % au coût d’une exécution qui réessaie chaque résultat vide.

Ce que ça coûte

/v1/scholar coûte 5 crédits par appel, quel que soit le nombre d’articles renvoyés. Les 500 crédits ponctuels de l’offre gratuite couvrent 100 appels, de quoi vérifier la couverture des champs sur vos propres sujets. Sur le pack Scale (99 $ pour 300 000 crédits), ça fait 60 000 appels, 1,65 $ pour 1 000.

En bref

Traitez /v1/scholar comme une tâche de fond, pas une recherche en direct : mettez les sujets en file d’attente, mettez le JSON en cache de votre côté, et laissez votre propre interface faire le filtrage et le tri instantanés que l’endpoint lui-même est trop lent à faire à la demande. Récupérez 500 crédits gratuits, sans carte et testez-le sur un sujet que vous suivez vraiment.