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 de marzo de 202611 min de lectura2.4k Palabras|

Integración de API de Odoo: Guía REST, JSON-RPC y XML-RPC

Odoo 19 expone tres interfaces API que cubren todo, desde la simple recuperación de datos hasta la compleja automatización del flujo de trabajo. Ya sea que esté creando una aplicación móvil personalizada, sincronizándola con una plataforma de terceros o ampliando las capacidades de Odoo con microservicios externos, dominar la capa API de Odoo es fundamental para cualquier proyecto de integración serio.

Esta guía proporciona ejemplos de código de trabajo, flujos de autenticación y recomendaciones arquitectónicas para integraciones REST, JSON-RPC y XML-RPC, las tres interfaces principales disponibles en Odoo 19 Enterprise.

Conclusiones clave

  • Odoo 19 ofrece interfaces REST (OpenAPI 3.0), JSON-RPC 2.0 y XML-RPC
  • La autenticación utiliza claves API (recomendado) o inicio de sesión basado en sesión
  • JSON-RPC es la interfaz con más funciones para operaciones complejas
  • La API REST sigue las especificaciones de OpenAPI 3.0 y admite verbos HTTP estándar.
  • XML-RPC es heredado pero aún es totalmente compatible para compatibilidad con versiones anteriores
  • La limitación de velocidad y el manejo de errores deben implementarse en el lado del cliente.
  • Los webhooks en Odoo 19 envían datos a sistemas externos sobre cambios de registros
  • Todas las llamadas API respetan los derechos de acceso y las reglas de registro de Odoo.

Comparación de interfaces API

Antes de escribir una sola línea de código, elija la interfaz API adecuada para su caso de uso:

CaracterísticaAPI RESTJSON-RPCXML-RPC
ProtocoloHTTP/HTTPSHTTP/HTTPSHTTP/HTTPS
Formato de carga útilJSONJSONXML
Especificaciones de OpenAPISí (arrogancia)NoNo
Operaciones CRUD
Llamadas a métodosLimitadoCompletoCompleto
Activadores del flujo de trabajoA través de accionesA través de ejecutar_kwVía ejecutar
Recomendado paraNuevas integracionesLógica complejaSistemas heredados
Biblioteca de Pythonsolicitudesodoo-xmlrpc/solicitudesxmlrpc.cliente

Cuándo usar REST: crear una aplicación móvil, integrarla con plataformas nativas de webhook (Shopify, Stripe) o cuando su equipo se sienta más cómodo con las convenciones REST.

Cuándo usar JSON-RPC: ejecutar métodos complejos del lado del servidor de Odoo, leer grandes conjuntos de datos con filtros de dominio o cuando necesita acceso a métodos no expuestos a través de REST.

Cuándo usar XML-RPC: mantener las integraciones existentes creadas antes de que REST estuviera disponible, o cuando su plataforma tiene bibliotecas cliente XML-RPC maduras.


Autenticación

Autenticación de clave API (recomendado)

Odoo 19 admite la autenticación de clave API para las tres interfaces. Genere una clave API en Configuración → Usuarios → Su usuario → Claves 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}"
}

Las claves API están dirigidas a un usuario específico y heredan los derechos de acceso de ese usuario. Cree usuarios de servicios dedicados con permisos mínimos requeridos para cuentas de integración.

Autenticación basada en sesiones (JSON-RPC / XML-RPC)

Para JSON-RPC, autentíquese utilizando el punto final /web/dataset/call_kw después de establecer una sesión:

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}")

Para XML-RPC, utilice la autenticación estándar de dos pasos:

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 presenta una API REST completa con la especificación OpenAPI 3.0. Acceda a la documentación interactiva en https://your-odoo.com/api/docs.

Listado de registros

# 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()

Leer un solo registro

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

Creando un registro

# 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()

Actualización de un registro

# 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"}
)

Eliminar un registro

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

Interfaz JSON-RPC

JSON-RPC proporciona acceso a la API completa de Odoo Python, incluidos los métodos del lado del servidor que no están expuestos a través de REST. El criterio de valoración principal es /web/dataset/call_kw.

Búsqueda y lectura básica

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"]}
)

Búsqueda Lectura (Combinada)

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"
    }
)

Creando registros

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
            })
        ]
    }]
)

Llamar a métodos del lado del servidor

JSON-RPC brinda acceso a todos los métodos de Python definidos en los modelos de 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", [[]])

Interfaz XML-RPC

XML-RPC es la API original de Odoo y sigue siendo totalmente compatible. La interfaz consta de dos puntos finales:

  • /xmlrpc/2/common — métodos no autenticados (autenticar, versión)
  • /xmlrpc/2/object — todas las operaciones del modelo (requiere 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'
    }]
)

Filtros de dominio

La sintaxis de filtro de dominio de Odoo se utiliza en los tres tipos de API. Comprender los dominios es esencial para una recuperación de datos eficiente.

# 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 e integración basada en eventos

Odoo 19 admite webhooks salientes activados por cambios de registros. Configure webhooks en Configuración → Técnico → Webhooks.

Configuración del webhook:

  1. Vaya a Configuración → Técnico → Webhooks → Crear
  2. Establezca el Modelo (por ejemplo, sale.order)
  3. Seleccione Activador: crear, escribir, desvincular o personalizar el método.
  4. Ingrese la URL del punto final de su servicio receptor.
  5. Opcionalmente, configure Dominio para filtrar qué registros activan el webhook.
  6. Configure Campos para incluirlos en la carga útil

Recepción de eventos de webhook en un servicio 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

Manejo de errores y lógica de reintento

Las integraciones sólidas deben manejar los errores de la API de Odoo con elegancia.

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)

Códigos de error comunes:

CódigoSignificadoAcción
100Error del servidorVerifique los registros de Odoo
200Acceso denegadoVerificar permisos de usuario
300Registro faltanteComprobar que el ID del registro existe
304Falta campo obligatorioRevisar la carga útil

Mejores prácticas de rendimiento

Operaciones por lotes: nunca llame a la API en un bucle para registros individuales. Utilice create_multi y write con listas:

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

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

Selección de campo: especifique siempre el parámetro fields para evitar recuperar todos los campos:

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

Paginación: para conjuntos de datos grandes, pagina usando limit y 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

Preguntas frecuentes

¿Cuál es la diferencia entre JSON-RPC y la API REST en Odoo 19?

JSON-RPC proporciona acceso a la API completa de Odoo Python, incluidos todos los métodos del lado del servidor, mientras que REST sigue las convenciones de OpenAPI 3.0 y expone una interfaz más limitada pero estandarizada. Para nuevas integraciones en las que REST cubre su caso de uso, se prefiere REST por su capacidad de descubrimiento. Para una automatización de flujo de trabajo compleja o acceso a métodos Python personalizados, utilice JSON-RPC.

¿Cómo puedo manejar exportaciones de datos grandes (más de 100.000 registros) de manera eficiente?

Utilice paginación con search_read y un tamaño de lote de 500 a 1000 registros. Para exportaciones muy grandes, considere usar la función de exportación de Odoo a través de la interfaz de usuario para extracciones únicas, o programe trabajos en segundo plano usando el modelo ir.cron de Odoo para procesar datos en fragmentos durante las horas de menor actividad en lugar de realizar llamadas API en tiempo real.

¿Puedo usar claves API en lugar de nombre de usuario/contraseña para XML-RPC?

Sí. En Odoo 13+, las claves API se pueden usar como contraseñas en la llamada de autenticación XML-RPC. Genere una clave API a partir de su perfil de usuario y úsela en lugar de su contraseña: common.authenticate(db, username, api_key, {}). Este es el enfoque recomendado para las cuentas de servicio.

¿Cómo creo registros Many2many y One2many a través de la API?

Utilice las tuplas de comandos de Odoo: (0, 0, vals) crea un nuevo registro relacionado, (1, id, vals) actualiza un registro relacionado existente, (2, id, 0) elimina un registro relacionado, (4, id, 0) vincula un registro existente, (5, 0, 0) desvincula todos los registros relacionados. Estos comandos funcionan de manera idéntica en JSON-RPC, XML-RPC y REST.

¿Cómo activo una acción de flujo de trabajo (como confirmar un pedido) a través de la API?

Llame al método correspondiente en el modelo. Para confirmar un pedido de venta, llame a action_confirm al sale.order. Para validar una entrega, llame a button_validate al stock.picking. Estos métodos son visibles en el código fuente de Odoo y se pueden descubrir inspeccionando el atributo name del botón en el modo de desarrollador de la interfaz de usuario.

¿Qué límites de velocidad impone Odoo a las llamadas API?

Odoo no aplica límites de tasa de API de forma nativa a nivel de aplicación. La limitación de velocidad debe configurarse en el proxy inverso (Nginx) o en el nivel de infraestructura. Un valor predeterminado sensato es 60 solicitudes por minuto por IP para integraciones externas. Para integraciones de alto rendimiento, utilice un enfoque basado en colas con un usuario de servicio dedicado.


Próximos pasos

Construir una integración API de Odoo confiable requiere más que ejemplos de código funcional: exige un manejo adecuado de errores, monitoreo, administración de credenciales y alineación con el modelo de datos de Odoo.

El equipo de integración de ECOSIRE ha creado conexiones de nivel de producción entre Odoo y docenas de plataformas, incluidas Shopify, Amazon, GoHighLevel, Power BI, ERP personalizados y sistemas propietarios. Nos encargamos de la arquitectura de autenticación, el diseño de webhooks, la transformación de datos y el monitoreo continuo.

Hable con ECOSIRE sobre su proyecto de integración de Odoo →

Ya sea que esté iniciando una nueva integración o arreglando una rota, nuestros ingenieros revisarán sus requisitos y le brindarán una solución que maneje los casos extremos desde el primer día.

E

Escrito por

ECOSIRE Research and Development Team

Construyendo productos digitales de nivel empresarial en ECOSIRE. Compartiendo perspectivas sobre integraciones Odoo, automatización de eCommerce y soluciones empresariales impulsadas por IA.

Chatea en whatsapp