OdooWebApps.com

Odoo 19's JSON-2 API: What Changed and What to Do About XML-RPC

By Tal Oz, OdooWebApps. Published 22 September 2026.

What is the JSON-2 API?

It is the HTTP API Odoo introduced in version 19. You POST a JSON object to /json/2/<model>/<method> with an Authorization: bearer <api key> header, and you get the method's return value back as JSON with a normal HTTP status code. It replaces the pattern every Odoo integration used before it: authenticate to get a user id, then push everything through one generic execute_kw dispatcher over XML-RPC or JSON-RPC.

It is not REST in the resource sense. The URL names a model and a method, not a resource and a verb. What you gain over the RPC protocols is a normal HTTP surface: real status codes, bearer auth, one request per call, and a /doc page on your own database listing the models, fields and methods that database actually exposes.

What does a JSON-2 request look like?

The URL carries the model and method. The body carries the arguments as named fields, plus ids for the records to act on (omitted for model-level methods such as search_read) and an optional context.

HTTP
POST /json/2/res.partner/search_read HTTP/1.1
Host: mycompany.example.com
Authorization: bearer 6578616d706c65206a736f6e20617069206b6579
Content-Type: application/json; charset=utf-8
X-Odoo-Database: mycompany
User-Agent: mysoftware python-requests/2.25.1

{
  "context": {"lang": "en_US"},
  "domain": [["name", "ilike", "%deco%"], ["is_company", "=", true]],
  "fields": ["name"]
}

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

[{"id": 25, "name": "Deco Addict"}]

Errors come back with a 4xx or 5xx status and a JSON object naming the Python exception, its message and arguments, the context used, and a traceback for debugging. That is a real improvement over JSON-RPC, where a failure is an HTTP 200 with an error key that naive clients read as success.

Who can use it? The plan and edition gate

On Odoo Online, access to the external API, JSON-2 included, is only available on the Custom pricing plan. It is not available on the One App Free or Standard plans. Self-hosted databases and Odoo.sh are not limited that way. This catches people out: the code is correct, the key looks fine, and the calls still fail because the subscription plan does not include external API access.

How do API keys work in Odoo 19?

A key is created per user under Preferences, Account Security, New API Key, with a description and a duration. Odoo 19 will not create a key that lasts more than three months, so any production integration needs a rotation routine rather than a key pasted once into a config file. The key value is shown once at creation and cannot be retrieved later.

Keys can also be generated and revoked programmatically, which is what a rotation job uses, though Odoo restricts that to privileged users unless a system parameter is changed. Either way, the practical consequence is the one that catches teams out: an integration that nobody maintains will stop working within three months of going live.

One call, one transaction: the rule that changes how you write code

Every JSON-2 call runs in its own SQL transaction, committed on success and discarded on error. You cannot chain several calls inside one transaction. So a sequence like create an order, then confirm it, is two commits: if the second fails you are left with a draft order, not with nothing.

The fix is to call a single method that does the whole job. search_read is the everyday example (a search and a read in one transaction, so a record deleted between them cannot break it), and business methods prefixed action_, such as sale.order.action_confirm, are the same idea for writes. When no such method exists for your sequence, the clean answer is a small module that adds one. This matters most for reservations, payments and stock moves, where a half-finished sequence has real consequences.

What happens to XML-RPC and JSON-RPC?

Both still work on Odoo 19, and both are deprecated. Odoo has scheduled the /xmlrpc, /xmlrpc/2 and /jsonrpc endpoints for removal in Odoo 22 (fall 2028), along with the three services they expose: common, db and object. Controllers declared with @route(type='jsonrpc'), which were called type='json' until Odoo 18, are not part of that deprecation.

Migrating the calls you actually use
Old RPC callJSON-2 equivalent
common.version()GET /web/version
common.authenticate(db, login, password, {})No login step; send the key as a bearer token. For your own uid, call res.users/context_get with no ids
object.execute_kw(db, uid, key, model, method, args, kwargs)POST /json/2/<model>/<method> with named JSON fields
db.list(), db.create_database(), db.dump()The /web/database controllers, with the master password
db.server_version()GET /web/version

The practical migration is smaller than it looks, because the model names, method names and domains do not change. If your integration keeps its transport in one module, as ours do, the change is one file plus the credential handling. If RPC calls are scattered through the codebase, this is the moment to centralise them.

Should you move now?

If you are on Odoo 19 and writing something new: yes, use JSON-2. If you are on Odoo 16, 17 or 18: you cannot, JSON-2 does not exist there, so use JSON-RPC and keep it isolated. If you have a working RPC integration on Odoo 19: there is no urgency, but treat the transport swap as part of the upgrade plan for Odoo 22 rather than as a surprise in 2028. See what happens to a custom app when Odoo upgrades for the rest of that checklist.

Sources

The endpoint shape, the header names, the three-month key limit, the Custom-plan restriction, the one-call-one-transaction rule and the Odoo 22 removal date are all from Odoo's own reference, External JSON-2 API, read on 22 September 2026. Where Odoo's documentation and a third-party blog post disagree about this API, the documentation is the one to trust: this area changed recently and a lot of what is written about it predates Odoo 19.

Want this looked at for your setup?

Every Odoo instance is configured a little differently. Tell us what you're working with and we'll give you a straight answer.