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
|2026年3月19日6 分钟阅读1.3k 字数|

Odoo API 集成:REST、JSON-RPC 和 XML-RPC 指南

Odoo 19 公开了三个 API 接口,涵盖从简单的数据检索到复杂的工作流程自动化的所有内容。无论您是构建自定义移动应用程序、与第三方平台同步,还是通过外部微服务扩展 Odoo 的功能,掌握 Odoo API 层都是任何重要集成项目的基础。

本指南提供了适用于 REST、JSON-RPC 和 XML-RPC 集成(Odoo 19 Enterprise 中提供的三个主要接口)的工作代码示例、身份验证流程和架构建议。

要点

  • Odoo 19 提供 REST (OpenAPI 3.0)、JSON-RPC 2.0 和 XML-RPC 接口
  • 身份验证使用 API 密钥(推荐)或基于会话的登录
  • JSON-RPC是功能最齐全的复杂操作接口
  • REST API 遵循 OpenAPI 3.0 规范并支持标准 HTTP 动词
  • XML-RPC 是遗留的,但仍然完全支持向后兼容
  • 速率限制和错误处理必须在客户端实现
  • Odoo 19 中的 Webhook 将记录更改的数据推送到外部系统
  • 所有 API 调用均尊重 Odoo 的访问权限和记录规则

API接口对比

在编写一行代码之前,请为您的用例选择正确的 API 接口:

特色休息 APIJSON-RPCXML-RPC
协议HTTP/HTTPSHTTP/HTTPSHTTP/HTTPS
有效负载格式JSONJSONXML
OpenAPI 规范是的(大摇大摆)没有没有
CRUD 操作是的是的是的
方法调用有限公司完整完整
工作流程触发器通过行动通过execute_kw通过执行
推荐用于新的集成复杂逻辑遗留系统
Python 库请求odoo-xmlrpc / 请求xmlrpc.client

何时使用 REST:构建移动应用程序、与 webhook 本机平台(Shopify、Stripe)集成,或者当您的团队更熟悉 REST 约定时。

何时使用 JSON-RPC:执行复杂的 Odoo 服务器端方法、使用域过滤器读取大型数据集,或者当您需要访问未通过 REST 公开的方法时。

何时使用 XML-RPC:维护在 REST 可用之前构建的现有集成,或者当您的平台具有成熟的 XML-RPC 客户端库时。


身份验证

API密钥认证(推荐)

Odoo 19 支持所有三个接口的 API 密钥身份验证。在 设置 → 用户 → 您的用户 → API 密钥 下生成 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}"
}

API 密钥的范围仅限于特定用户并继承该用户的访问权限。创建具有集成帐户所需的最低权限的专用服务用户。

基于会话的身份验证(JSON-RPC / XML-RPC)

对于 JSON-RPC,在建立会话后使用 /web/dataset/call_kw 端点进行身份验证:

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

对于 XML-RPC,使用标准的两步身份验证:

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

REST API:OpenAPI 3.0

Odoo 19 引入了符合 OpenAPI 3.0 规范的完整 REST API。访问 https://your-odoo.com/api/docs 处的交互式文档。

列出记录

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

读取单个记录

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

创建记录

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

更新记录

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

删除记录

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

JSON-RPC 接口

JSON-RPC 提供对完整 Odoo Python API 的访问,包括不通过 REST 公开的服务器端方法。主要终点是 /web/dataset/call_kw

基本搜索和阅读

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

搜索阅读(合并)

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

创建记录

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

调用服务器端方法

JSON-RPC 允许访问 Odoo 模型上定义的所有 Python 方法:

# 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", [[]])

XML-RPC 接口

XML-RPC 是原始的 Odoo API,并且仍然得到完全支持。该接口由两个端点组成:

  • /xmlrpc/2/common — 未经身份验证的方法(身份验证、版本)
  • /xmlrpc/2/object — 所有模型操作(需要 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'
    }]
)

域过滤器

Odoo 的域过滤器语法适用于所有三种 API 类型。了解域对于有效的数据检索至关重要。

# 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]
]

Webhook 和事件驱动集成

Odoo 19 支持由记录更改触发的出站 Webhook。在 设置 → 技术 → Webhooks 下配置 Webhooks。

网络钩子配置:

  1. 导航至 设置 → 技术 → Webhooks → 创建
  2. 设置 型号(例如 sale.order
  3. 选择触发器:创建、写入、取消链接或自定义方法
  4. 输入接收服务的 端点 URL
  5. (可选)设置 Domain 来过滤哪些记录触发 webhook
  6. 配置 Fields 以包含在有效负载中

在 Flask 服务中接收 webhook 事件:

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

错误处理和重试逻辑

强大的集成必须妥善处理 Odoo API 错误。

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)

常见错误代码:

代码意义行动
100100服务器错误检查 Odoo 日志
200200访问被拒绝验证用户权限
300300失踪记录检查记录ID是否存在
304304缺少必填字段审查有效负载

性能最佳实践

批量操作:切勿在循环中为单个记录调用 API。将 create_multiwrite 与列表一起使用:

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

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

字段选择:始终指定 fields 参数以避免获取所有字段:

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

分页:对于大型数据集,使用 limitoffset 进行分页:

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

常见问题

Odoo 19 中 JSON-RPC 和 REST API 有什么区别?

JSON-RPC 提供对完整 Odoo Python API 的访问,包括所有服务器端方法,而 REST 遵循 OpenAPI 3.0 约定并公开更有限但标准化的接口。对于 REST 涵盖您的用例的新集成,REST 因其可发现性而成为首选。对于复杂的工作流程自动化或访问自定义 Python 方法,请使用 JSON-RPC。

如何高效处理大数据导出(10万+条记录)?

使用带有 search_read 的分页和 500–1000 条记录的批量大小。对于非常大的导出,请考虑通过 UI 使用 Odoo 的导出功能进行一次性提取,或者使用 Odoo 的 ir.cron 模型安排后台作业,以便在非高峰时段处理大块数据,而不是进行实时 API 调用。

我可以使用 API 密钥代替 XML-RPC 的用户名/密码吗?

是的。在 Odoo 13+ 中,API 密钥可以用作 XML-RPC 身份验证调用中的密码。从您的用户配置文件生成 API 密钥并使用它代替您的密码:common.authenticate(db, username, api_key, {})。这是服务帐户的推荐方法。

如何通过 API 创建 Many2many 和 One2many 记录?

使用 Odoo 的命令元组: (0, 0, vals) 创建新的相关记录, (1, id, vals) 更新现有的相关记录, (2, id, 0) 删除相关记录, (4, id, 0) 链接现有记录, (5, 0, 0) 取消链接所有相关记录。这些命令在 JSON-RPC、XML-RPC 和 REST 中的工作方式相同。

如何通过 API 触发工作流程操作(例如确认订单)?

调用模型上相应的方法。要确认销售订单,请致电 sale.order 上的 action_confirm。要验证交货,请致电 stock.picking 上的 button_validate。这些方法在 Odoo 的源代码中可见,并且可以通过在 UI 开发者模式中检查按钮的 name 属性来发现。

Odoo 对 API 调用施加什么速率限制?

Odoo 本身并不在应用程序级别强制执行 API 速率限制。必须在反向代理 (Nginx) 或基础设施级别配置速率限制。对于外部集成,合理的默认值是每个 IP 每分钟 60 个请求。对于高吞吐量集成,请使用基于队列的方法和专门的服务用户。


后续步骤

构建可靠的 Odoo API 集成需要的不仅仅是工作代码示例 - 它还需要正确的错误处理、监控、凭证管理以及与 Odoo 数据模型的一致性。

ECOSIRE 的集成团队已在 Odoo 与数十个平台(包括 Shopify、Amazon、GoHighLevel、Power BI、自定义 ERP 和专有系统)之间建立了生产级连接。我们处理身份验证架构、Webhook 设计、数据转换和持续监控。

与 ECOSIRE 讨论您的 Odoo 集成项目 →

无论您是开始新的集成还是修复损坏的集成,我们的工程师都会审查您的需求,并从第一天起就提供处理边缘情况的解决方案。

E

作者

ECOSIRE Research and Development Team

在 ECOSIRE 构建企业级数字产品。分享关于 Odoo 集成、电商自动化和 AI 驱动商业解决方案的洞见。

通过 WhatsApp 聊天