diff --git a/tools/create_api_map.py b/tools/create_api_map.py
index 543a90697fbe8b23c415dd5038336caebcc3ae23..9afa57327493c3fef155b50f4bbc0ae9e37f4b03 100644
--- a/tools/create_api_map.py
+++ b/tools/create_api_map.py
@@ -461,6 +461,7 @@ def gen_api_map(app, endpoint_start="api"):
 
         # Récupération de la fonction associée à la route
         func = app.view_functions[rule.endpoint]
+        func_name = parse_doc_name(func.__doc__ or "") or func.__name__
 
         # Pour chaque segment de la route
         for i, segment in enumerate(segments):
@@ -494,7 +495,7 @@ def gen_api_map(app, endpoint_start="api"):
 
                 # On ajoute le token comme enfant du token courant
                 # en donnant la méthode et le nom de la fonction associée
-                child.func_name = func.__name__
+                child.func_name = func_name
                 method: str = "POST" if "POST" in rule.methods else "GET"
                 child.method = method
                 current_token.add_child(child)
@@ -602,6 +603,9 @@ def generate_svg(element, fname):
     )
     ET.SubElement(marker, "polygon", {"points": "0 0, 10 3.5, 0 7"})
 
+    # Ajoute un décalage vertical pour avoir un peu de padding en haut
+    element.set("transform", "translate(0, 10)")
+
     # Ajout de l'élément principal à l'élément racine
     svg.append(element)
 
@@ -610,6 +614,50 @@ def generate_svg(element, fname):
     tree.write(fname, encoding="utf-8", xml_declaration=True)
 
 
+def _get_doc_lines(keyword, doc) -> list[str]:
+    """
+    Renvoie les lignes de la doc qui suivent le mot clé keyword
+
+    La doc doit contenir des lignes de la forme:
+
+    KEYWORD
+    -------
+    ...
+
+    """
+    # Récupérer les lignes de la doc
+    lines = [line.strip() for line in doc.split("\n")]
+    # On cherche la ligne "KEYWORD" et on vérifie que la ligne suivante est "-----"
+    # Si ce n'est pas le cas, on renvoie un dictionnaire vide
+    try:
+        kw_index = lines.index(keyword)
+        kw_line = "-" * len(keyword)
+        if lines[kw_index + 1] != kw_line:
+            return []
+    except ValueError:
+        return []
+    # On récupère les lignes de la doc qui correspondent au keyword (enfin on espère)
+    kw_lines = lines[kw_index + 2 :]
+    return kw_lines
+
+
+def parse_doc_name(doc):
+    """
+    renvoie le nom de la route à partir de la docstring
+
+    La doc doit contenir des lignes de la forme:
+
+    DOC_ANCHOR
+    ----------
+    nom_de_la_route
+
+    Il ne peut y avoir qu'une seule ligne suivant -----
+
+    """
+    name_lines: list[str] = _get_doc_lines("DOC_ANCHOR", doc)
+    return name_lines[0] if name_lines else None
+
+
 def parse_query_doc(doc):
     """
     renvoie un dictionnaire {param: <type:nom_param>} (ex: {assiduite_id : <int:assiduite_id>})
@@ -625,18 +673,7 @@ def parse_query_doc(doc):
     Dès qu'une ligne ne respecte pas ce format (voir regex dans la fonction), on arrête de parser
     Attention, la ligne ----- doit être collée contre QUERY et contre le premier paramètre
     """
-    # Récupérer les lignes de la doc
-    lines = [line.strip() for line in doc.split("\n")]
-    # On cherche la ligne "QUERY" et on vérifie que la ligne suivante est "-----"
-    # Si ce n'est pas le cas, on renvoie un dictionnaire vide
-    try:
-        query_index = lines.index("QUERY")
-        if lines[query_index + 1] != "-----":
-            return {}
-    except ValueError:
-        return {}
-    # On récupère les lignes de la doc qui correspondent à la query (enfin on espère)
-    query_lines = lines[query_index + 2 :]
+    query_lines: list[str] = _get_doc_lines("QUERY", doc)
 
     query = {}
     regex = re.compile(r"^(\w+):(<.+>)$")