# Odoo External API: Authenticate and search_read with XML-RPC and JSON-RPC

> Source: https://netray.co/erp-help/odoo-external-api-xmlrpc-jsonrpc-authenticate-search-read-example
> Publisher: Netray (netray.co), AI and integration engineering for ERP and manufacturing systems.
> Section: ERP Help

## Short answer

Call the Odoo external API by authenticating against /xmlrpc/2/common to get a user id, then calling execute_kw on /xmlrpc/2/object with the database, uid, password or API key, model name, method and arguments. If authenticate returns False, the database name, login, or API key is wrong, or two-factor is enforced for that user.

## Kind

how-to

## System

small-erp

## Erp

Odoo

## Module

External API / Integration

## Query

Odoo external API XML-RPC authenticate search_read example

## Alternate Queries

- odoo xmlrpc authenticate returns false
- odoo json-rpc call execute_kw python example
- odoo api key instead of password xmlrpc
- odoo external api access denied for model

## Applies To

Odoo 14 to 18 (XML-RPC and JSON-RPC at /jsonrpc); newer releases add a newer JSON API and deprecate XML-RPC, so check your version's external API documentation before building long-lived integrations

## Steps

### How to connect and read data

**Items:**

- Create an API key for the integration user: profile > Account Security > New API Key. Use this in place of the password, which also works with two-factor enabled.
- Confirm the exact database name. On multi-database servers it is the name in the database manager or the db query parameter, not the company name.
- Call common.authenticate(db, login, api_key, {}) at /xmlrpc/2/common. A number is the uid. False means credentials or database are wrong.
- Create a models proxy at /xmlrpc/2/object and call execute_kw(db, uid, api_key, model, method, args, kwargs).
- Use search_read with a domain list and a fields list, plus limit and offset, to page results.
- Create records with create, update with write using a list of ids, and delete with unlink. Always test on a copy of the database first.
- Assign a dedicated user with only the access groups the integration needs. Do not use the admin account.

## Sections

### Working Python example with XML-RPC

**Body:**

The code below reads confirmed sale orders. It uses only the Python standard library. The domain uses Odoo prefix notation, which is a list of tuples with implicit AND. Fields you do not list are not returned, which keeps payloads small.

XML-RPC is synchronous and has no connection reuse by default, so create the ServerProxy objects once and reuse them. Large exports should be paged with limit and offset or by id ranges instead of one huge call.

**Code:**

import xmlrpc.client

url = 'https://mycompany.odoo.com'
db = 'mycompany'
login = 'integration@mycompany.com'
api_key = 'YOUR_API_KEY'

common = xmlrpc.client.ServerProxy(url + '/xmlrpc/2/common')
uid = common.authenticate(db, login, api_key, {})
if not uid:
    raise SystemExit('Authentication failed')

models = xmlrpc.client.ServerProxy(url + '/xmlrpc/2/object')
orders = models.execute_kw(
    db, uid, api_key, 'sale.order', 'search_read',
    [[['state', '=', 'sale']]],
    {'fields': ['name', 'partner_id', 'amount_total'], 'limit': 50, 'order': 'id desc'}
)
for o in orders:
    print(o['name'], o['partner_id'][1], o['amount_total'])

### JSON-RPC alternative

**Body:**

Odoo also exposes /jsonrpc, which accepts the same service, method and args structure. It is convenient from JavaScript and many integration platforms. The call below mirrors the XML-RPC example: service is object, method is execute_kw, and args repeats db, uid, key, model, method, positional args and kwargs.

Both interfaces enforce the same record rules, access rights and field-level groups as the UI. An integration user that cannot read a model in the UI cannot read it by API either.

**Code:**

curl -s https://mycompany.odoo.com/jsonrpc -H 'Content-Type: application/json' -d '{
  "jsonrpc": "2.0", "method": "call", "id": 1,
  "params": {
    "service": "object", "method": "execute_kw",
    "args": ["mycompany", 2, "YOUR_API_KEY", "res.partner", "search_read",
             [[["is_company", "=", true]]], {"fields": ["name", "email"], "limit": 5}]
  }
}'

### Why authenticate returns False or the call errors

**Body:**

Wrong database is the most common cause. Odoo Online uses the subdomain as the database name, but many on-premise installs use a different name. Use the db list endpoint (if enabled) or the database manager to confirm. If list_db is disabled, ask your admin.

Using a password for a user with two-factor authentication enabled fails, since only API keys can bypass it. Generate a key and use it as the password argument.

AccessError or AccessDenied on a call usually means the user lacks a group for that model, not that authentication failed. The error text includes the model and operation (read, write, create, unlink) and the record rule involved.

Faults wrapped as xmlrpc.client.Fault contain the Python traceback from the server in faultString. Read the final line for the real cause, such as a required field missing on create.

### Practical integration rules

**Body:**

Use many2one values correctly: read returns [id, name] tuples, but create and write expect an integer id. For one2many and many2many fields, use command tuples like (0, 0, vals) to create, (4, id) to link and (6, 0, [ids]) to replace.

Do not poll. For event-driven integrations use automated actions with webhooks (Enterprise and 17+) or scheduled exports, and keep read frequency modest on shared hosting. Respect rate limits on Odoo Online and Odoo.sh.

Store the API key as a secret, rotate it when staff change, and give it an expiry where your version supports key duration.

## Pitfalls

- Using the company name rather than the database name in authenticate.
- Using an account password with two-factor enabled instead of an API key.
- Running the integration as the admin user, which sidesteps access review and logs everything under one identity.
- Passing [id, name] back to write for a many2one field instead of the integer id.
- Pulling an entire model in one call with no limit, which times out on large tables.
- Building a long-lived integration on XML-RPC without checking your version's deprecation notes.

## Ai Angle

Most integration questions are about the shape of a call: which model, which fields, what domain. An assistant that knows your Odoo instance's model and field definitions can draft the execute_kw call, run it as a read-only query and show you the result and the exact request. With ERPray style read-only access the assistant uses an API key scoped to the integration user. Write calls are proposed, shown and sent only after a person approves.

## Faq

### Does Odoo have a REST API?

**Answer:** Odoo's long-standing external APIs are XML-RPC and JSON-RPC. Recent versions have added a newer JSON based API, and community modules provide REST endpoints. For stable production use, follow the documentation for your exact Odoo version.

### How do I get an API key?

**Answer:** Open your user profile, go to Account Security and choose New API Key. Enter a description, confirm your password and copy the key shown. It is displayed once. Use it in place of the password in calls.

### Why does search_read return only some records?

**Answer:** Record rules and multi-company settings filter results to what the user can see. Also check a default limit in your code. The API never bypasses rules, so an integration user needs the right company and groups.

### Can I call custom methods on a model?

**Answer:** Yes, public methods (not starting with an underscore) can be called through execute_kw. Methods starting with an underscore are private and rejected. Return values must be serializable, so avoid returning recordsets.

## Search terms

odoo xmlrpc example, odoo json-rpc execute_kw, odoo external api authenticate false, odoo api key, odoo search_read domain, odoo python external api, odoo create write via api, odoo api access denied model

---
Talk to an engineer: https://netray.co/contact?intent=erp_ai | Machine catalog: https://netray.co/agent/catalog.json
