Appeler un Script Python depuis n8n

9 min read

Mise à jour (Mars 2026) : Depuis la publication de cet article, j'utilise Claude Code + n8n-MCP pour construire et déboguer les workflows n8n directement depuis le terminal. Cette combinaison rend l'approche FastAPI ci-dessous encore plus puissante : Claude Code échafaude le workflow, n8n l'exécute, Python gère les tâches lourdes. J'ai détaillé toute la configuration ici.

Parfois, il faut appeler un script Python depuis un workflow n8n. Et là, c'est le drame. Certes, n8n propose un nœud Code qui prend en charge Python, mais son implémentation est limitée. Si vous voulez exploiter toute la puissance de Python — bibliothèques externes, calculs complexes, modèles de machine learning — il vous faut un environnement Python dédié.

TL;DR : Utilisez FastAPI pour encapsuler vos scripts Python en endpoints API, conteneurisez avec Docker, et appelez depuis vos workflows n8n via des requêtes HTTP. Cela contourne les limitations du nœud Code Python de n8n pour accéder à toutes les capacités Python, bibliothèques externes incluses.

Dans ce guide, je vais vous montrer comment intégrer un script Python dans un workflow n8n en utilisant FastAPI pour créer un endpoint API. Que vous souhaitiez traiter des images, manipuler des données, ou effectuer n'importe quelle tâche personnalisée, cette approche vous permet d'exploiter la puissance de Python au sein des capacités d'automatisation de n8n.

Nous couvrirons :

  1. L'encapsulation d'un script Python dans une application FastAPI.
  2. Sa conteneurisation avec Docker.
  3. Son appel depuis un workflow n8n.

À la fin, vous aurez une configuration fonctionnelle où n8n déclenche votre script Python via une requête HTTP.

Prérequis

Avant de commencer, assurez-vous d'avoir :

  • Docker installé sur votre machine ou serveur.
  • n8n en cours d'exécution (de préférence dans Docker, bien que d'autres configurations fonctionnent aussi).
  • Une familiarité de base avec Python, Docker, et les workflows n8n.
  • Un pot de baume du tigre pour booster le courage.

Étape 1 : Créer votre script Python

Commençons par un script Python simple. Pour cet exemple, nous utiliserons un script de redimensionnement d'image, mais vous pouvez le remplacer par n'importe quelle logique Python dont vous avez besoin.

Script d'exemple : resize.py

Ce script redimensionne une image en carré de 1000x1000 tout en conservant ses proportions et en ajoutant un arrière-plan blanc.

J'ai choisi cet exemple parce que le nœud de redimensionnement d'image dans n8n ne fonctionne pas correctement en ce moment. Les gars de n8n 😵‍💫, vous devez corriger ça !

from PIL import Image
import argparse

def resize_image(input_path, output_path, size=1000):
    # Open the original image
    original_image = Image.open(input_path)
    width, height = original_image.size
    
    # Calculate new dimensions while preserving aspect ratio
    ratio = min(size / width, size / height)
    new_width = int(width * ratio)
    new_height = int(height * ratio)
    
    # Resize the image
    resized_image = original_image.resize((new_width, new_height), Image.LANCZOS)
    
    # Create a white 1000x1000 background and paste the resized image
    final_image = Image.new("RGB", (size, size), (255, 255, 255))
    offset_x = (size - new_width) // 2
    offset_y = (size - new_height) // 2
    final_image.paste(resized_image, (offset_x, offset_y))
    
    # Save the result
    final_image.save(output_path)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Resize image to 1000x1000")
    parser.add_argument("input_image", help="Path to input image")
    parser.add_argument("output_image", help="Path to output image")
    args = parser.parse_args()
    resize_image(args.input_image, args.output_image)

Sauvegardez ceci sous resize.py. Il prend un chemin d'image d'entrée et un chemin de sortie comme arguments.


Étape 2 : Créer un wrapper FastAPI

Au lieu de simplement construire une application FastAPI, nous encapsulons en fait notre script Python avec une API, le rendant accessible à n8n. Ce wrapper permettra à n8n d'interagir avec le script via des requêtes HTTP, garantissant une meilleure flexibilité et évolutivité.

💡 Une autre option aurait été d'utiliser Flask, qui est un framework léger populaire pour construire des API. Cependant, nous avons choisi FastAPI car il offre un support asynchrone, une validation automatique des données, et une documentation OpenAPI intégrée, le rendant plus rapide et plus efficace pour gérer les requêtes API — surtout dans des scénarios d'automatisation comme n8n.

L'un des principaux avantages de FastAPI est la facilité avec laquelle on peut l'étendre. Ajouter plus de scripts est aussi simple que de définir de nouveaux endpoints. Par exemple, si vous avez besoin d'un autre script pour appliquer un filtre en niveaux de gris à une image, vous pouvez simplement créer une nouvelle route comme /grayscale et appeler le script correspondant. Cette approche modulaire facilite la montée en charge de votre API et l'intégration de plusieurs scripts Python dans votre workflow n8n sans modifier les endpoints existants.

## app.py

from fastapi import FastAPI, UploadFile, File
from fastapi.responses import StreamingResponse
import subprocess
import tempfile
import os
import io

app = FastAPI()

@app.post("/resize")
async def resize_image(file: UploadFile = File(...)):
    # Create temporary files for input and output
    with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as input_file:
        input_path = input_file.name
        input_file.write(await file.read())
    
    with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as output_file:
        output_path = output_file.name
    
    # Run the resize script
    try:
        subprocess.run(["python", "resize.py", input_path, output_path], check=True)
    except subprocess.CalledProcessError as e:
        return {"error": f"Failed to resize image: {e}"}
    
    # Read the resized image
    with open(output_path, "rb") as f:
        image_data = f.read()
    
    # Clean up temporary files
    os.remove(input_path)
    os.remove(output_path)
    
    # Return the image as a response
    return StreamingResponse(io.BytesIO(image_data), media_type="image/jpeg")

Comment ça fonctionne :

  • L'endpoint /resize accepte un fichier uploadé.
  • Il sauvegarde le fichier temporairement, appelle resize.py en utilisant subprocess, et retourne l'image redimensionnée.
  • Les fichiers temporaires sont supprimés après traitement pour éviter l'encombrement.

Sauvegardez ceci sous app.py dans le même répertoire que resize.py.

Note : Réflexion sur l'approche FastAPI avec subprocess vs classe Python 🥊

Jazys m'a fait remarquer : "Bon, pour un développeur, ce genre de ligne pique les yeux. Le mieux serait de créer une classe Python et d'appeler cette classe. De toute façon, quand tu voudras ajouter de nouvelles API un jour, tu devras modifier ton app.py, alors autant intégrer Python jusqu'au bout."

Il a absolument raison d'un point de vue puriste : utiliser une classe Python (comme ImageProcessor) pour encapsuler la logique, intégrée directement dans l'application FastAPI, améliore la robustesse, les performances et la maintenabilité, surtout pour une application évolutive avec de nouvelles routes. Pour un développeur cherchant une solution propre, modulaire et bien documentée (avec documentation Swagger automatique), cette approche est idéale (par ex., importer une classe dans app.py avec des méthodes réutilisables).

Cependant, en tant que bricoleur pragmatique et non-développeur, je préfère ma solution subprocess : elle me permet d'appeler rapidement des petits scripts indépendants sans me soucier de robustesse ou d'évolutivité, ce qui convient à mes besoins simples. Merci pour cette remarque pertinente, Jazys — ta sagesse de dev transpire ! 😉


Étape 3 : Définir les dépendances

Créez un fichier requirements.txt pour lister les packages Python dont nous avons besoin (au fait, si vous êtes aussi paresseux que moi, vous pouvez générer un fichier requirements.txt automatiquement en exécutant la commande suivante dans votre répertoire de projet : pip freeze > requirements.txt)

## requirements.txt

fastapi
uvicorn
pillow
python-multipart

Sauvegardez ceci sous requirements.txt.


Étape 4 : Conteneuriser avec Docker

Pour assurer une intégration transparente avec n8n et maintenir la cohérence entre les environnements, nous allons conteneuriser notre application FastAPI en utilisant Docker. Puisque n8n fonctionne probablement dans un conteneur Docker, nous connecterons notre API Python au même réseau Docker dès le départ.

De plus, nous explorerons deux approches : utiliser un Dockerfile autonome ou exploiter docker compose pour des configurations plus complexes. Dans des cas spécifiques, comme lors du traitement d'un grand nombre de fichiers, nous discuterons aussi de l'utilisation d'un volume partagé.

Configuration du réseau

n8n et notre API Python doivent communiquer au sein du même réseau Docker. D'abord, identifiez le réseau que votre conteneur n8n utilise :

docker inspect <n8n_container_name> | grep Network

Remplacez <n8n_container_name> par le nom de votre conteneur n8n (par ex., n8n-akc0oo0ogc0gkog0g8ww). Cherchez la section NetworkID ou Networks pour trouver le nom du réseau (par ex., coolify ou n8n_default). Nous utiliserons ce réseau pour nous assurer que les conteneurs peuvent communiquer entre eux en utilisant leurs noms de conteneur.

Remarque : Nous aurions pu choisir d'exposer nos scripts Python à internet en mappant un port public (par ex., -p 8000:8000) et en le sécurisant avec HTTPS et authentification. Cependant, dans ce cas, nous avons opté pour les garder internes au VPS. Cela simplifie la configuration et évite les complexités de sécurité qui surviennent quand les services sont exposés au monde, comme gérer les pare-feu, certificats, reverse proxy, ou les attaques potentielles.

Option 1 : Utiliser un Dockerfile autonome

C'est l'approche la plus simple, idéale pour une configuration à service unique.

Créez un Dockerfile dans votre répertoire de projet :

FROM python:3.9
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY app.py .
COPY resize.py .
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

Construisez l'image Docker :

docker build -t fastapi-python-app .

Exécuter le conteneur

Exécutez le conteneur, en le connectant au réseau n8n (remplacez <n8n_network> par le nom réel du réseau) :

docker run -d --name python-api --network <n8n_network> fastapi-python-app

-name python-api : Nomme le conteneur pour une référence facile.

-network <n8n_network> : Le connecte au réseau de n8n, permettant la communication via http://python-api:8000.

L'API sera accessible à http://python-api:8000/resize au sein du réseau.

Cas spécifique : Volume partagé

Si votre workflow implique le traitement d'un grand nombre de fichiers (par ex., redimensionner des centaines d'images), vous pourriez vouloir éviter de transférer répétitivement des fichiers via HTTP. Au lieu de cela, vous pouvez utiliser un volume Docker partagé pour stocker des fichiers accessibles à la fois à n8n et à l'API Python. Par exemple :

docker run -d --name python-api --network <n8n_network> -v /path/to/shared/folder:/data fastapi-python-app
  • -v /path/to/shared/folder:/data : Monte un répertoire de l'hôte (/path/to/shared/folder) vers /data dans le conteneur.
  • Mettez à jour app.py pour lire/écrire des fichiers depuis /data au lieu d'utiliser des fichiers temporaires, et assurez-vous que n8n peut écrire dans le même volume. C'est une exception plutôt que la norme, car cela couple les conteneurs plus étroitement, mais c'est efficace pour les opérations de fichiers en masse.

Option 2 : Utiliser Docker Compose

Pour une configuration plus structurée, surtout si vous gérez plusieurs services ou voulez définir le réseau explicitement, utilisez docker-compose.

Créez un fichier docker-compose.yml :

services:
  python-api:
    build: .
    image: fastapi-python-app
    container_name: python-api
    command: uvicorn app:app --host 0.0.0.0 --port 8000
    networks:
      - n8n_network

networks:
  n8n_network:
    name: <n8n_network>
    external: true
  • build: . : Construit l'image depuis le Dockerfile dans le répertoire courant.
  • networks : Connecte le service au réseau n8n existant (remplacez <n8n_network> par le nom réel de docker network ls).

Construire et exécuter

Démarrez le service :

docker compose up -d

Cela lance le conteneur python-api dans le réseau spécifié.

Volume partagé avec Docker Compose

Pour les scénarios avec de nombreux fichiers, ajoutez un volume à docker-compose.yml :

services:
  python-api:
    build: .
    image: fastapi-python-app
    container_name: python-api
    command: uvicorn app:app --host 0.0.0.0 --port 8000
    volumes:
      - shared-data:/data
    networks:
      - n8n_network

volumes:
  shared-data:

networks:
  n8n_network:
    name: <n8n_network>
    external: true
  • volumes : Définit un volume nommé (shared-data) monté à /data dans le conteneur.
  • Si n8n est aussi géré par ce docker-compose.yml, ajoutez-le comme service et attachez-le au même volume et réseau. Sinon, assurez-vous que le conteneur de n8n monte le même volume séparément.

Gestion avec Docker Compose

Arrêtez ou redémarrez avec :

docker compose down
docker compose up -d

Étape 5 : Vérifier et tester le conteneur

Avant d'intégrer l'API Python avec n8n, confirmons que le conteneur fonctionne correctement et que l'API répond comme attendu. Nous allons le tester avec curl d'une manière que vous pourrez ensuite copier-coller directement dans le nœud HTTP Request de n8n pour une transition transparente.

Vérifier le statut du conteneur

Confirmez que le conteneur fonctionne :

docker ps

Cherchez python-api dans la liste avec un statut Up et aucun port exposé (puisque nous utilisons un réseau interne). S'il ne fonctionne pas, vérifiez les logs :

docker logs python-api

Corrigez toute erreur (par ex., fichiers manquants, problèmes de dépendances) en reconstruisant l'image si nécessaire :

docker build -t fastapi-python-app .
docker run -d --name python-api --network <n8n_network> fastapi-python-app

Tester l'API localement

Testez l'endpoint depuis le réseau Docker en utilisant curl :

docker run --rm --network <n8n_network> curlimages/curl curl -X POST -F "file=@/path/to/test.jpg" http://python-api:8000/resize -o output.jpg
  • Remplacez /path/to/test.jpg par un chemin d'image de test, en le montant si nécessaire (par ex., -v /local/path:/data et utilisez file=@/data/test.jpg).
  • Vérifiez qu'output.jpg contient l'image redimensionnée.

Bonus : Commande curl compatible n8n

Voici une commande curl que vous pouvez exécuter directement sur votre hôte (si le port est exposé) ou adapter pour n8n :

curl -X POST -F "file=@/path/to/test.jpg" http://python-api:8000/resize -o output.jpg
  • Pourquoi c'est utile : Cette commande exacte peut être copiée dans le nœud HTTP Request de n8n via l'option import cURL et en la collant dans le champ "Raw Request" (après avoir ajusté le chemin du fichier pour correspondre aux données binaires de n8n). C'est un moyen rapide de prototyper la requête avant de l'affiner dans n8n.
  • Si vous avez exposé le port localement (par ex., avec -p 8000:8000), exécutez ceci depuis votre VPS pour tester en dehors de Docker.

Si cela fonctionne, l'API est prête pour que n8n l'appelle 😀.


Étape 6 : Configurer n8n pour appeler l'API

Dans votre instance n8n :

  1. Ouvrez l'éditeur de workflow.
  2. Ajoutez un nœud HTTP Request.
  3. Configurez-le comme ceci :

Screenshot of an HTTP request editor interface with various input fields and options, including URL, authentication, query parameters, headers, and body content type.

  • URL : http://python-api:8000/resize (en utilisant le nom du conteneur comme nom d'hôte).
  • Method : POST.
  • Send Binary Data : Activez cette option.
  • Binary Property : Définissez à data (ou le nom de votre champ d'entrée binaire).

Ajouter un fichier image

Si vous redimensionnez une image depuis le disque :

  • Ajoutez un nœud Read Binary File avant le nœud HTTP Request.
  • Définissez le chemin du fichier vers votre image d'entrée.
  • Connectez-le au nœud HTTP Request, en vous assurant que les données binaires transitent.

Smoothly bypass the image resize node bug with all this mess haha! 😜


Si ce contenu vous est utile, vos applaudissements 👏, surlignages 🖍️, ou commentaires 💬 nous aident à produire plus de matériel pertinent.

Étape 7 : Tester le workflow

  1. Exécutez le workflow n8n.
  2. Vérifiez la sortie du nœud HTTP Request : Vous devriez voir l'image redimensionnée retournée comme données binaires.
  3. Admirez ce chef-d'œuvre ! 😆🔥

Dépannage

  • "Connection refused" : Assurez-vous que les deux conteneurs sont sur le même réseau Docker et que l'URL correspond au nom du conteneur (python-api).
  • "Module not found" : Vérifiez requirements.txt et reconstruisez l'image Docker.
  • Problèmes de fichiers : Si n8n ne trouve pas le fichier d'entrée, vérifiez que le chemin est accessible dans son conteneur.

Vous voilà parés !

Vous êtes maintenant capable d'appeler en toute sécurité des scripts Python depuis n8n sans aucun effet de bord sur votre environnement d'automatisation. En plus, vous pourriez même utiliser ces scripts en dehors de n8n — mais ça, c'est une toute autre histoire !

Bonne automatisation ! Faites-moi savoir dans les commentaires si vous rencontrez des problèmes ou avez des questions. 🚀

J'ai documenté la structure CLI, le CLAUDE.md de production, et les contraintes d'agent que j'utilise dans tous mes projets d'automatisation dans un kit gratuit. 3 fichiers, 10 minutes.

Obtenez l'Agent Engineering Stack


Transformer n8n et Python en duo d'automatisation puissant : découvrez comment débloquer des workflows complexes avec FastAPI et Docker.

Rejoindre la newsletter des développeurs pragmatiques