Odoo JSON-2 API Migration: Move XML-RPC Integrations to Odoo 20
Odoo JSON-2 API migration guide: XML-RPC deprecation dates, bearer API keys, Python call mapping, error handling and a cut-over checklist for Odoo 19 and 20.
Most Odoo shops run a script, connector or BI feed written against XML-RPC years ago. Those links now carry a deadline. Our Odoo integration services start by finding every one of them before the clock runs out.
This guide is for developers and technical leads who own Odoo integrations on Odoo 19 or Odoo 20. For the wider picture of connectors, webhooks and MCP, read our Odoo API and third-party integration guide first.
Why is XML-RPC deprecated in Odoo, and when does it stop working?
XML-RPC and JSON-RPC are deprecated because Odoo replaced them with the JSON-2 API in Odoo 19, and the remaining services are scheduled for removal in Odoo 22 and Odoo Online 21.1. The old endpoints still answer today, which is exactly why teams forget them.
Odoo's external JSON-2 API documentation is the source of record. It marks /xmlrpc, /xmlrpc/2 and /jsonrpc as deprecated and dates the removal of each service.
| Service | What it did | Status in Odoo 20 | Removal |
|---|---|---|---|
| common | version, login, authenticate | Deprecated, still works | Odoo 22 (fall 2028), Online 21.1 (winter 2027) |
| db | create, duplicate, dump, restore, drop | Removed | Odoo 20 (fall 2026), Online 19.1 (winter 2025) |
| object | execute and execute_kw on any model | Deprecated, still works | Odoo 22 (fall 2028), Online 21.1 (winter 2027) |
Early Odoo 19 server logs and the first docs said the endpoints would go in Odoo 20. Odoo later moved the date to Odoo 22, so a ticket that says "fix before Odoo 20" has a new deadline but the same work.
Controllers your own modules declare with route type jsonrpc (type json until Odoo 18) are not part of this deprecation. For every other version change, see our Odoo 20 release notes.
How does the Odoo JSON-2 API differ from XML-RPC?

The JSON-2 API replaces one generic execute_kw entry point with a plain HTTP POST per model and method, authenticated by an API key and fed only with named arguments. Every other difference follows from those three changes.
| Field | XML-RPC today | JSON-2 in Odoo 20 |
|---|---|---|
| Endpoint | POST /xmlrpc/2/object, method name inside the payload | POST /json/2/{model}/{method}, model and method in the URL |
| Authentication | database, user id and password on every call | Authorization header with bearer and an API key |
| Database selection | first argument of every call | X-Odoo-Database header, only when one host serves several databases |
| Arguments | positional list plus an optional keyword dictionary | one JSON object, every argument named, plus ids and context |
| Record ids | first positional argument | the ids key in the body, empty for model-level methods |
| Errors | fault inside an HTTP 200 response | real 4xx or 5xx status with a JSON error object |
| Version check | common.version() | GET https://erp.iventureteam.com/web/version |
| User id lookup | common.authenticate() returns uid | POST /json/2/res.users/context_get with no ids |
There is no way to call a method with positional arguments in JSON-2, so every call site is rewritten with argument names. The response is the plain JSON return value of the method, with no result envelope around it.
Which Odoo plans and editions include the JSON-2 API?
JSON-2 ships in Odoo itself, so Community, Odoo.sh and on-premise Enterprise all have it, but on Odoo Online the external API is enabled only on the Custom plan. The same rule applied to the old RPC endpoints, so the migration adds no new license cost.
The docs note that external API access is not available on the One App Free or Standard plans of Odoo Online. On the Odoo pricing page, US Custom is US$61.00 per user a month billed yearly against US$31.10 for Standard; India Custom is Rs 1,350 against Rs 725.
Community users sometimes assume JSON-2 is a paid feature. When a Community user asked on the Odoo forum, the accepted answer was that the JSON-2 API is not a commercial feature and is already available in Odoo 19 Community.
How do you authenticate with an Odoo JSON-2 API key?
You create an API key for a user under Preferences, Account Security, New API Key, then send it on every request as a bearer token in the Authorization header. The key replaces both the password and the user id that XML-RPC needed.
| Rule | What Odoo enforces | What it means for your integration |
|---|---|---|
| Key creation | description and duration are mandatory | name one key per service so you can revoke it alone |
| Maximum life | no key lasts more than three months | rotate keys at least quarterly |
| Visibility | the 160-bit key is shown once, never again | store it in a secrets manager at creation |
| Access rights | the key's user rights, record rules and field access apply | give the bot user minimum permissions |
| Programmatic keys | generate and revoke methods on res.users.apikeys, default limit 10 per user | automate rotation; needs Settings rights or the base.enable_programmatic_api_keys parameter |
| Over the limit | HTTP 422 Unprocessable Content | revoke unused keys first |
Odoo recommends a dedicated bot user for automated use: an empty password blocks interactive login, permissions can be trimmed, and audit fields show the bot rather than a person. When rotating, authenticate with the new key to revoke the old one; the two keys do not have to match.
The pain point developers raise most is cost, since each bot user is a full internal user and a paid seat on Enterprise. A forum thread confirmed there are no native scoped API keys without additional billable users, so most teams run one bot user with one named key per integration.
How do you convert an XML-RPC call to JSON-2 in Python?
Replace the xmlrpc.client ServerProxy with a requests session, move the model and method into the URL, and turn every positional argument into a named key in the JSON body. The steps below map the JSON-2 replacement for each execute_kw pattern.
| Step | Request detail | Notes |
|---|---|---|
| 1. Headers | Authorization set to bearer plus the key; Content-Type application/json; X-Odoo-Database set to the database name; User-Agent set to your software name | build once on a requests.Session |
| 2. Version check | GET https://mycompany.example.com/web/version | replaces common.version() |
| 3. Who am I | POST /json/2/res.users/context_get with an empty body | replaces authenticate(); the user comes from the key |
| 4. Search and read | POST /json/2/res.partner/search_read with body keys domain, fields, limit and context | returns a JSON array of records |
| 5. Create | POST /json/2/res.partner/create with body key vals_list holding a list of objects | returns the new ids |
| 6. Update | POST /json/2/res.partner/write with body keys ids and vals | returns true |
| 7. Business action | POST /json/2/sale.order/action_confirm with body key ids | one transaction, one method |
| 8. Delete | POST /json/2/res.partner/unlink with body key ids | same access rules as the user |
In Python each step is one call to requests.post with the URL, the headers dictionary and the body as the json parameter. Call raise_for_status, then read response.json() for the return value. No login call and no uid variable remain.
One2many and many2many fields still use Odoo command lists. A developer creating a sales order with lines through JSON-2 hit an "unhashable type: dict" error until they found that each order line needs the two zeros, as in 0, 0 and then the values object.
Every database lists its own models, fields and methods on its /doc page; check argument names there before rewriting a call.
How should you handle JSON-2 API errors, retries and transactions?
Treat every non-2xx status as a structured error, retry only idempotent calls on 5xx and network failures, and never split one business step across several JSON-2 calls. XML-RPC code often ignored all three rules because faults came back inside a 200 response.
| HTTP status | Error object name | Usual cause | Right response |
|---|---|---|---|
| 401 | werkzeug.exceptions.Unauthorized | expired, revoked or mistyped key | rotate the key, alert, do not retry |
| 403 | AccessError, or a failed revoke | bot user lacks rights, or the key to revoke is invalid | fix access rights, do not retry |
| 4xx | odoo.exceptions.ValidationError or UserError | bad data, missing field, wrong argument name | fix the payload, log the message field |
| 5xx | server or database error | overload, lock, deployment in progress | back off and retry idempotent calls |
The error body has five keys: name, message, arguments, context and debug. Log name and message, keep debug out of production, and map name to your own exception types.
Every JSON-2 call runs in its own SQL transaction that commits on success and rolls back on error, and calls cannot be chained. A search followed by a separate read can fail if another user deletes a record in between.
Odoo's advice is to call one method that does the whole job: search_read instead of search then read, or action_confirm on sale.order instead of writing state fields. If no such method exists, add a small module with one method. Our Odoo development company team builds these wrappers often.
How do you find every integration still using XML-RPC?
Search the server logs for requests to /xmlrpc, /xmlrpc/2 and /jsonrpc, then match each caller to a system owner before you write any code. The hidden ones are usually not in your code repository at all.
- Server and proxy logs: filter for the three deprecated paths and group by source IP and user agent for at least two weeks, so monthly jobs show up.
- API keys per user: list every key and its description under Account Security; an old key with no owner is a forgotten integration.
- Automation platforms: check n8n, Zapier, Make and similar tools; the n8n Odoo node still used XML-RPC at the time of writing.
- Connectors and BI: eCommerce connectors, Power BI gateways, shipping and payment plugins and warehouse scanners often ship with an XML-RPC client inside.
- Spreadsheets and scripts: finance teams keep Python or VBA scripts that pull invoices through execute_kw; ask them directly.
- Odoo Online version: record the release your database runs, because the Online removal in 21.1 arrives before the Odoo 22 self-hosted date.
Record the result as an inventory with system, owner, direction, models, call volume and deprecated service. That inventory is the migration scope and the cut-over test plan.
What does a safe XML-RPC to JSON-2 cut-over checklist look like?
Port one integration at a time, test it against a neutralized copy of production, run old and new side by side, then revoke the old credentials only after a full business cycle. Skipping the parallel run is the most common cause of silent data loss.
- Create the bot user with minimum access rights and an empty password, and document which models and methods it may touch.
- Generate one named key per integration with a 90-day expiry, store it in your secrets manager and schedule the rotation job.
- Rewrite the client to the JSON-2 shape: URL per method, bearer header, named arguments, ids and context, and real HTTP status handling.
- Test on a staging copy of production; Odoo neutralizes test databases, so mails and scheduled actions will not fire by accident.
- Compare outputs by running the XML-RPC and JSON-2 clients against the same records and diffing the JSON field by field.
- Run both in parallel through at least one month-end, with the JSON-2 client writing to a log instead of to Odoo if it creates records.
- Switch the writer to JSON-2, keeping the XML-RPC client disabled but deployable for one more cycle as the rollback path.
- Reconcile order counts, invoice totals and stock moves for the first week, then revoke the old password and keys and confirm the logs are clean.
The reconciliation step borrows from upgrade practice, where problems hide until the month closes; our guide on Odoo upgrade verification lists those checks. If the integration feeds a webstore, read our post on Odoo 20 upgrade and eCommerce integrations.
How much does an Odoo JSON-2 API migration cost and how long does it take?
A single one-way script usually takes a few developer days to port and test, while a two-way connector across several models takes weeks, and the inventory step decides which one you have. The figures below are illustrative estimates from our own projects, not quotes.
- Read-only report or BI feed: search_read on one to three models, about 1 to 3 developer days; the main risk is field name drift between versions.
- One-way order import: create and write on sale.order and res.partner, about 3 to 8 developer days; watch command lists for order lines and duplicate detection.
- Two-way connector: five or more models synced on a schedule, about 2 to 5 developer weeks; the risks are transaction boundaries, idempotency and reconciliation.
Effort rises with positional calls scattered across many files, logic split over several RPC calls that now needs one server-side method, and no test environment. Pairing the migration with an Odoo upgrade shares the staging work.
For a quick first number, run the Odoo project estimate tool with your integration count. To add capacity instead of outsourcing, see how to hire an Odoo developer from our team.
Which industries feel the XML-RPC deprecation first?
Businesses whose revenue flows through an integration feel it first, because a broken order import or stock sync shows up as lost sales the same day. This is where we most often find XML-RPC in production.
| Industry | Pain or cost driver | Odoo approach | How iVentureTeam helps |
|---|---|---|---|
| eCommerce and marketplaces | order and stock sync scripts written for Odoo 12 to 16 | JSON-2 connector with idempotent writes and action methods | inventory, port, parallel run, reconciliation |
| Logistics and 3PL | WMS and carrier plugins calling execute_kw thousands of times a day | batch search_read and single-method writes | performance testing before cut-over |
| Manufacturing | shop-floor and MES systems posting work orders | bot users per plant with scoped access rights | custom server methods for multi-step operations |
| Finance and BI | Power BI, spreadsheets and bank feeds on XML-RPC | read-only bot user, quarterly key rotation | re-point reports, verify totals at month-end |
How iVentureTeam helps with Odoo JSON-2 API migration

iVentureTeam audits, ports and cuts over Odoo integrations from XML-RPC to JSON-2 with tests, key rotation and a rollback path. We have delivered 150+ ERP projects across 35+ countries with 40+ Odoo specialists over 13+ years.
- Integration inventory: log analysis and key review to list every caller still on /xmlrpc or /jsonrpc, with owners and deadlines.
- Porting and testing: JSON-2 clients in Python or your stack, with diff tests against the old client and month-end reconciliation.
- Security setup: bot users, named keys per integration, automated rotation and least-privilege access rights.
- Upgrade pairing: our Odoo upgrade services run the API migration inside the same staging and test cycle as a version upgrade.
- AI and MCP: when assistants need to act on Odoo data, our Odoo 20 MCP server guide shows how JSON-2 keys and access rights carry over.
Odoo API integration case study: a Lightspeed to Odoo connector
A North American online retailer kept its Lightspeed eCommerce storefront and gained an Odoo back office, with a scheduled connector syncing both ways. Before the project, every product, order and customer existed twice and web stock drifted from the warehouse.
iVentureTeam built a bidirectional connector configured entirely from the Odoo back end, with credential setup and mapping inside Odoo and no external middleware. It imports nine record types from Lightspeed, including products, categories, brands, taxes, customers, suppliers and orders with status.
Three data streams flow back: product prices, live inventory positions, and order and shipment status. Delivered in 6 weeks, the connector left zero manual re-entry steps between the systems. Read the full Lightspeed and Odoo bidirectional integration case study.
The bottom line on Odoo JSON-2 API migration
Move every XML-RPC and JSON-RPC integration to JSON-2 now, while the old endpoints still work and you can run both side by side. Odoo Online loses the old services in 21.1 and self-hosted systems in Odoo 22, and waiting turns a planned port into an emergency.
Start with the inventory, give each integration its own key, rewrite calls with named arguments and real error handling, and reconcile through one month-end before revoking old credentials. The code change is modest; the discipline around it protects your ledger.
Ready to move your Odoo integrations off XML-RPC?

Get a list of every integration still on the deprecated endpoints and a plan to port them. Request an Odoo integration audit, call +91-90235-15208, or email business@iventureteam.com.
Frequently Asked Questions
Is XML-RPC removed in Odoo 20?
+
Not fully. Only the db service of the old RPC APIs is gone in Odoo 20. The common and object services, which carry execute_kw, are deprecated but still work, with removal scheduled for Odoo 22 and Odoo Online 21.1.
What is the Odoo JSON-2 API?
+
It is Odoo's external API introduced in Odoo 19. You POST a JSON body to /json/2/{model}/{method} with a bearer API key, and Odoo returns the method's result as JSON. It exposes the same ORM methods as XML-RPC, with named arguments and real HTTP status codes.
How long is an Odoo API key valid?
+
An API key needs a description and a duration when created, and Odoo does not allow keys that last more than three months. Long-running integrations must rotate keys at least quarterly, using the generate and revoke methods on res.users.apikeys.
Do I need the Custom plan to use the Odoo API?
+
On Odoo Online, yes. The external API is not available on the One App Free or Standard plans. Odoo Community, Odoo.sh and on-premise Enterprise include the JSON-2 API without a plan restriction.
Can I still use positional arguments with JSON-2?
+
No. Every argument in a JSON-2 request is named inside the JSON body, along with the ids and context keys. Calls written for execute_kw with positional lists must be rewritten with the method's argument names.
How do I get the user id with JSON-2 if there is no authenticate call?
+
Send a POST to /json/2/res.users/context_get with no ids. Odoo identifies the user from the API key and returns the context, including the user id, so the XML-RPC login step disappears.
Will my n8n, Zapier or Make workflows break?
+
They keep working while the deprecated endpoints exist, but they depend on the vendor shipping a JSON-2 connector. Check your Odoo logs for calls to /xmlrpc or /jsonrpc from those platforms and track their release notes.
Ready to put this into action?
Talk to iVentureTeam about Odoo, AI automation, or custom development — get a free, no-obligation consultation.
Free Odoo tools
All tools- Odoo Health CheckVersion, modules, findings and upgrade cost
- Odoo App SelectorWhich apps you need, by industry
- Odoo Project EstimateIndicative cost and timeline in minutes
- Odoo Implementation Cost CalculatorImplementation cost and timeline, in minutes
- Odoo Migration Cost CalculatorVersion jump or legacy ERP, priced in minutes
Continue reading
Odoo Amazon Integration: Setup, Sync Scope, Limits and Cost
Odoo Amazon integration explained: what the Odoo Amazon connector syncs for FBA and FBM, its limits, eBay options, Odoo 20 marketplaces and real costs.
Odoo 20 MCP Server: Connect Claude or ChatGPT to Odoo Safely
Odoo 20 MCP server guide: versions and plans, OAuth and API key setup for Claude and ChatGPT, default tools, access rights, approvals and community servers.
Odoo UAE E-Invoicing Setup: PINT AE, ASPs and Odoo 19/20 Readiness
How to set up UAE e-invoicing in Odoo 19 and 20: PINT AE mapping, accredited service provider choice, EmaraTax onboarding, deadlines, penalties and costs.
