Odoo API Integration: REST, JSON-RPC, and XML-RPC Guide

Complete developer guide to Odoo API integration using REST, JSON-RPC, and XML-RPC. Includes authentication, endpoints, code examples, and best practices.

E
ECOSIRE Research and Development Team
|19 mars 202611 min de lecture2.4k Mots|

Intégration de l'API Odoo : Guide REST, JSON-RPC et XML-RPC

Odoo 19 expose trois interfaces API qui couvrent tout, de la simple récupération de données à l'automatisation de flux de travail complexes. Que vous créiez une application mobile personnalisée, que vous la synchronisiez avec une plate-forme tierce ou que vous étendiez les capacités d'Odoo avec des microservices externes, la maîtrise de la couche API Odoo est fondamentale pour tout projet d'intégration sérieux.

Ce guide fournit des exemples de code de travail, des flux d'authentification et des recommandations architecturales pour les intégrations REST, JSON-RPC et XML-RPC — les trois interfaces principales disponibles dans Odoo 19 Enterprise.

Points clés à retenir

  • Odoo 19 propose les interfaces REST (OpenAPI 3.0), JSON-RPC 2.0 et XML-RPC
  • L'authentification utilise des clés API (recommandées) ou une connexion basée sur la session
  • JSON-RPC est l'interface la plus complète en fonctionnalités pour les opérations complexes
  • L'API REST suit les spécifications OpenAPI 3.0 et prend en charge les verbes HTTP standard
  • XML-RPC est hérité mais toujours entièrement pris en charge pour une compatibilité ascendante
  • La limitation du débit et la gestion des erreurs doivent être implémentées côté client
  • Les Webhooks dans Odoo 19 transmettent des données à des systèmes externes sur les modifications enregistrées
  • Tous les appels API respectent les droits d'accès et les règles d'enregistrement d'Odoo

Comparaison des interfaces API

Avant d'écrire une seule ligne de code, choisissez l'interface API adaptée à votre cas d'utilisation :

FonctionnalitéAPI RESTJSON-RPCXML-RPC
ProtocoleHTTP/HTTPSHTTP/HTTPSHTTP/HTTPS
Format de charge utileJSONJSONXML
Spécification OpenAPIOui (Swagger)NonNon
Opérations CRUDOuiOuiOui
Appels de méthodeLimitéCompletComplet
Déclencheurs de flux de travailVia des actionsVia exécuter_kwVia exécuter
Recommandé pourNouvelles intégrationsLogique complexeSystèmes hérités
Bibliothèque Pythondemandesodoo-xmlrpc / requêtesxmlrpc.client

Quand utiliser REST : créer une application mobile, intégrer des plateformes webhook natives (Shopify, Stripe) ou lorsque votre équipe est plus à l'aise avec les conventions REST.

Quand utiliser JSON-RPC : exécution de méthodes complexes côté serveur Odoo, lecture de grands ensembles de données avec des filtres de domaine ou lorsque vous avez besoin d'accéder à des méthodes non exposées via REST.

Quand utiliser XML-RPC : maintenir les intégrations existantes créées avant que REST ne soit disponible, ou lorsque votre plate-forme dispose de bibliothèques client XML-RPC matures.


Authentification

Authentification par clé API (recommandé)

Odoo 19 prend en charge l'authentification par clé API pour les trois interfaces. Générez une clé API sous Paramètres → Utilisateurs → Votre utilisateur → Clés API.

import requests

ODOO_URL = "https://your-odoo.com"
API_KEY = "your_api_key_here"
DATABASE = "your_database"

headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {API_KEY}"
}

Les clés API sont limitées à un utilisateur spécifique et héritent des droits d'accès de cet utilisateur. Créez des utilisateurs de service dédiés avec les autorisations requises minimales pour les comptes d'intégration.

Authentification basée sur la session (JSON-RPC / XML-RPC)

Pour JSON-RPC, authentifiez-vous à l'aide du point de terminaison /web/dataset/call_kw après avoir établi une session :

import requests
import json

session = requests.Session()

# Authenticate
auth_payload = {
    "jsonrpc": "2.0",
    "method": "call",
    "params": {
        "db": "your_database",
        "login": "admin",
        "password": "your_password"
    }
}

response = session.post(
    f"{ODOO_URL}/web/session/authenticate",
    json=auth_payload
)
uid = response.json()['result']['uid']
print(f"Authenticated as UID: {uid}")

Pour XML-RPC, utilisez l'authentification standard en deux étapes :

import xmlrpc.client

url = "https://your-odoo.com"
db = "your_database"
username = "admin"
password = "your_password"

# Step 1: Get UID
common = xmlrpc.client.ServerProxy(f"{url}/xmlrpc/2/common")
uid = common.authenticate(db, username, password, {})

# Step 2: Use UID for subsequent calls
models = xmlrpc.client.ServerProxy(f"{url}/xmlrpc/2/object")

API REST : OpenAPI 3.0

Odoo 19 introduit une API REST complète avec la spécification OpenAPI 3.0. Accédez à la documentation interactive sur https://your-odoo.com/api/docs.

Liste des enregistrements

# GET /api/sale.order — list all sales orders
response = requests.get(
    f"{ODOO_URL}/api/sale.order",
    headers=headers,
    params={
        "domain": '[["state", "=", "sale"]]',
        "fields": '["name", "partner_id", "amount_total", "state"]',
        "limit": 50,
        "offset": 0
    }
)
orders = response.json()

Lecture d'un seul enregistrement

# GET /api/sale.order/{id}
order_id = 123
response = requests.get(
    f"{ODOO_URL}/api/sale.order/{order_id}",
    headers=headers
)
order = response.json()

Création d'un enregistrement

# POST /api/sale.order
payload = {
    "partner_id": 42,
    "order_line": [
        {
            "product_id": 7,
            "product_uom_qty": 5,
            "price_unit": 100.0
        }
    ]
}
response = requests.post(
    f"{ODOO_URL}/api/sale.order",
    headers=headers,
    json=payload
)
new_order = response.json()

Mettre à jour un enregistrement

# PATCH /api/sale.order/{id}
response = requests.patch(
    f"{ODOO_URL}/api/sale.order/{order_id}",
    headers=headers,
    json={"note": "Rush order — priority handling required"}
)

Supprimer un enregistrement

# DELETE /api/sale.order/{id}
response = requests.delete(
    f"{ODOO_URL}/api/sale.order/{order_id}",
    headers=headers
)

Interface JSON-RPC

JSON-RPC donne accès à l'API Python Odoo complète, y compris aux méthodes côté serveur qui ne sont pas exposées via REST. Le critère d'évaluation principal est /web/dataset/call_kw.

Recherche et lecture de base

def call_kw(model, method, args, kwargs=None):
    payload = {
        "jsonrpc": "2.0",
        "method": "call",
        "params": {
            "model": model,
            "method": method,
            "args": args,
            "kwargs": kwargs or {}
        }
    }
    response = session.post(
        f"{ODOO_URL}/web/dataset/call_kw",
        json=payload
    )
    return response.json().get('result')

# Search for confirmed sales orders
order_ids = call_kw(
    "sale.order",
    "search",
    [[["state", "=", "sale"]]],
    {"limit": 100, "order": "date_order desc"}
)

# Read specific fields
orders = call_kw(
    "sale.order",
    "read",
    [order_ids],
    {"fields": ["name", "partner_id", "amount_total", "date_order"]}
)

Recherche Lecture (combinée)

orders = call_kw(
    "sale.order",
    "search_read",
    [[["partner_id.country_id.code", "=", "US"]]],
    {
        "fields": ["name", "partner_id", "amount_total"],
        "limit": 50,
        "offset": 0,
        "order": "amount_total desc"
    }
)

Création d'enregistrements

new_id = call_kw(
    "sale.order",
    "create",
    [{
        "partner_id": 42,
        "order_line": [
            (0, 0, {
                "product_id": 7,
                "product_uom_qty": 10,
                "price_unit": 150.0
            })
        ]
    }]
)

Appel de méthodes côté serveur

JSON-RPC donne accès à toutes les méthodes Python définies sur les modèles Odoo :

# Confirm a sales order (triggers workflow)
call_kw("sale.order", "action_confirm", [[order_id]])

# Validate an inventory transfer
call_kw("stock.picking", "button_validate", [[picking_id]])

# Get the action for a button (useful for understanding what a button does)
action = call_kw("sale.order", "action_quotations_with_onboarding", [[]])

Interface XML-RPC

XML-RPC est l'API Odoo d'origine et reste entièrement prise en charge. L'interface se compose de deux points de terminaison :

  • /xmlrpc/2/common — méthodes non authentifiées (authentifier, version)
  • /xmlrpc/2/object — toutes les opérations du modèle (nécessite UID)
import xmlrpc.client

url = "https://your-odoo.com"
db, username, password = "mydb", "admin", "mypassword"

common = xmlrpc.client.ServerProxy(f"{url}/xmlrpc/2/common")
uid = common.authenticate(db, username, password, {})
models = xmlrpc.client.ServerProxy(f"{url}/xmlrpc/2/object")

# Search for products
product_ids = models.execute_kw(
    db, uid, password,
    'product.template', 'search',
    [[['sale_ok', '=', True]]],
    {'limit': 100}
)

# Read product data
products = models.execute_kw(
    db, uid, password,
    'product.template', 'read',
    [product_ids],
    {'fields': ['name', 'list_price', 'categ_id']}
)

# Create a new product
new_product_id = models.execute_kw(
    db, uid, password,
    'product.template', 'create',
    [{
        'name': 'My New Product',
        'list_price': 99.99,
        'type': 'consu'
    }]
)

Filtres de domaine

La syntaxe de filtre de domaine d'Odoo est utilisée dans les trois types d'API. Comprendre les domaines est essentiel pour une récupération efficace des données.

# Basic operators: =, !=, >, <, >=, <=, like, ilike, in, not in, child_of
domain = [
    ["state", "in", ["sale", "done"]],      # Confirmed or done orders
    ["amount_total", ">=", 1000],            # Total at least 1000
    ["partner_id.country_id.code", "=", "US"] # US customers (related field)
]

# Logical operators: & (AND, default), | (OR), ! (NOT)
domain = [
    "|",
    ["state", "=", "draft"],
    ["state", "=", "cancel"]
]

# Complex: orders from US or UK customers with total > 5000
domain = [
    "|",
    ["partner_id.country_id.code", "=", "US"],
    ["partner_id.country_id.code", "=", "GB"],
    ["amount_total", ">", 5000]
]

Webhooks et intégration basée sur les événements

Odoo 19 prend en charge les webhooks sortants déclenchés par les modifications d'enregistrement. Configurez les webhooks sous Paramètres → Technique → Webhooks.

Configuration du webhook :

  1. Accédez à Paramètres → Technique → Webhooks → Créer
  2. Définissez le Modèle (par exemple, sale.order)
  3. Sélectionnez Déclencheur : créer, écrire, dissocier ou méthode personnalisée.
  4. Saisissez l'URL du point de terminaison de votre service de réception.
  5. Vous pouvez éventuellement définir Domaine pour filtrer les enregistrements qui déclenchent le webhook.
  6. Configurez les Champs à inclure dans la charge utile

Réception d'événements webhook dans un service Flask :

from flask import Flask, request, jsonify
import hmac, hashlib

app = Flask(__name__)
WEBHOOK_SECRET = "your_webhook_secret"

@app.route("/odoo-webhook", methods=["POST"])
def handle_webhook():
    # Verify signature
    signature = request.headers.get("X-Odoo-Signature")
    body = request.get_data()
    expected = hmac.new(
        WEBHOOK_SECRET.encode(),
        body,
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(signature, expected):
        return jsonify({"error": "Invalid signature"}), 401

    event = request.json
    model = event.get("model")
    record_id = event.get("id")

    # Process the event
    if model == "sale.order":
        handle_order_event(record_id, event)

    return jsonify({"status": "ok"}), 200

Gestion des erreurs et logique de nouvelle tentative

Les intégrations robustes doivent gérer les erreurs de l'API Odoo avec élégance.

import time
import requests
from requests.exceptions import RequestException

def api_call_with_retry(url, payload, headers, max_retries=3, backoff=2):
    for attempt in range(max_retries):
        try:
            response = requests.post(url, json=payload, headers=headers, timeout=30)
            response.raise_for_status()

            data = response.json()
            if "error" in data:
                error = data["error"]
                code = error.get("code", 0)
                message = error.get("data", {}).get("message", "Unknown error")

                # Don't retry validation errors
                if code in [200, 100]:
                    raise ValueError(f"Odoo validation error: {message}")

                raise RuntimeError(f"Odoo API error {code}: {message}")

            return data.get("result")

        except (RequestException, RuntimeError) as e:
            if attempt == max_retries - 1:
                raise
            wait = backoff ** attempt
            print(f"Attempt {attempt + 1} failed: {e}. Retrying in {wait}s...")
            time.sleep(wait)

Codes d'erreur courants :

CodesSignificationActions
100Erreur de serveurVérifier les journaux Odoo
200Accès refuséVérifier les autorisations des utilisateurs
300Enregistrement manquantVérifier que l'ID de l'enregistrement existe
304Champ obligatoire manquantExaminer la charge utile

Meilleures pratiques en matière de performances

Opérations par lots : n'appelez jamais l'API en boucle pour des enregistrements individuels. Utilisez create_multi et write avec des listes :

# Bad: loop with individual creates
for product in products:
    call_kw("product.template", "create", [product])

# Good: batch create
call_kw("product.template", "create", [products])

Sélection de champs : spécifiez toujours le paramètre fields pour éviter de récupérer tous les champs :

# Good: only fetch needed fields
orders = call_kw(
    "sale.order", "search_read",
    [[["state", "=", "sale"]]],
    {"fields": ["name", "amount_total"], "limit": 1000}
)

Pagination : pour les ensembles de données volumineux, paginez à l'aide de limit et offset :

def fetch_all_records(model, domain, fields, batch_size=500):
    records = []
    offset = 0
    while True:
        batch = call_kw(
            model, "search_read", [domain],
            {"fields": fields, "limit": batch_size, "offset": offset}
        )
        records.extend(batch)
        if len(batch) < batch_size:
            break
        offset += batch_size
    return records

Questions fréquemment posées

Quelle est la différence entre JSON-RPC et l'API REST dans Odoo 19 ?

JSON-RPC donne accès à l'API Odoo Python complète, y compris toutes les méthodes côté serveur, tandis que REST suit les conventions OpenAPI 3.0 et expose une interface plus limitée mais standardisée. Pour les nouvelles intégrations où REST couvre votre cas d'utilisation, REST est préféré pour sa découvrabilité. Pour l'automatisation de flux de travail complexes ou l'accès à des méthodes Python personnalisées, utilisez JSON-RPC.

Comment gérer efficacement les exportations de données volumineuses (plus de 100 000 enregistrements) ?

Utilisez la pagination avec search_read et une taille de lot de 500 à 1 000 enregistrements. Pour les exportations très volumineuses, envisagez d'utiliser la fonction d'exportation d'Odoo via l'interface utilisateur pour des extractions ponctuelles, ou planifiez des tâches en arrière-plan à l'aide du modèle ir.cron d'Odoo pour traiter les données par morceaux pendant les heures creuses plutôt que d'effectuer des appels API en temps réel.

Puis-je utiliser des clés API au lieu d'un nom d'utilisateur/mot de passe pour XML-RPC ?

Oui. Dans Odoo 13+, les clés API peuvent être utilisées comme mots de passe dans l'appel d'authentification XML-RPC. Générez une clé API à partir de votre profil utilisateur et utilisez-la à la place de votre mot de passe : common.authenticate(db, username, api_key, {}). Il s'agit de l'approche recommandée pour les comptes de service.

Comment créer des enregistrements Many2many et One2many via l'API ?

Utilisez les tuples de commandes d'Odoo : (0, 0, vals) crée un nouvel enregistrement associé, (1, id, vals) met à jour un enregistrement associé existant, (2, id, 0) supprime un enregistrement associé, (4, id, 0) lie un enregistrement existant, (5, 0, 0) dissocie tous les enregistrements associés. Ces commandes fonctionnent de manière identique sur JSON-RPC, XML-RPC et REST.

Comment déclencher une action de workflow (comme la confirmation d'une commande) via l'API ?

Appelez la méthode correspondante sur le modèle. Pour confirmer une commande client, appelez action_confirm au sale.order. Pour valider une livraison, appelez button_validate au stock.picking. Ces méthodes sont visibles dans le code source d'Odoo et peuvent être découvertes en inspectant l'attribut name du bouton dans le mode développeur de l'interface utilisateur.

Quelles limites de débit Odoo impose-t-il aux appels d'API ?

Odoo n'applique pas nativement les limites de débit de l'API au niveau de l'application. La limitation de débit doit être configurée au niveau du proxy inverse (Nginx) ou de l'infrastructure. Une valeur par défaut raisonnable est de 60 requêtes par minute et par IP pour les intégrations externes. Pour les intégrations à haut débit, utilisez une approche basée sur la file d'attente avec un utilisateur de service dédié.


Prochaines étapes

Construire une intégration API Odoo fiable nécessite plus que des exemples de code fonctionnels : cela nécessite une gestion des erreurs, une surveillance, une gestion des informations d'identification et un alignement appropriés avec le modèle de données d'Odoo.

L'équipe d'intégration d'ECOSIRE a établi des connexions de niveau production entre Odoo et des dizaines de plateformes, notamment Shopify, Amazon, GoHighLevel, Power BI, des ERP personnalisés et des systèmes propriétaires. Nous gérons l'architecture d'authentification, la conception de webhooks, la transformation des données et la surveillance continue.

Parlez à ECOSIRE de votre projet d'intégration Odoo →

Que vous démarriez une nouvelle intégration ou répariez une intégration défaillante, nos ingénieurs examineront vos besoins et vous fourniront une solution qui gère les cas extrêmes dès le premier jour.

E

Rédigé par

ECOSIRE Research and Development Team

Création de produits numériques de niveau entreprise chez ECOSIRE. Partage d'analyses sur les intégrations Odoo, l'automatisation e-commerce et les solutions d'entreprise propulsées par l'IA.

Discutez sur WhatsApp