- APPS
- Odoo REST API 17.0
Technical name | rest_api |
License | OPL-1 |
Website | https://app.swaggerhub.com/apis-docs/avs3/odoo_rest_api/1 |
REST API engine for quick and productive developers
Overview
Enhanced RESTful API access to Odoo resources with simplified variant of OAuth2 authentication and OpenAPI specification.
The module may have a predefined (statically and dynamically customizable) tree-like schema of response Odoo fields for 'Read one', 'Read all' and 'Create one' methods. This allow not to fetch unnecessary fields (like a heavy image/binary fields or technical garbage fields) in each request or not to compose the list of desired fields in each request. Also that schemas can be used as a quick and clear model reference guide for your developers (backend, client side, etc.). See "Example #3" below.
The schema of the response fields can have a tree-like structure with any level of nesting. So you can read an object at once with absolutely all its inner data (including its lines with all inner data) in just one http request. Therefore you don't need to make a two (or much more) requests to get one object (if would so, the possible interruptions or lags between that requests can be fatal to the integrity of your data). See "Example #1" below.
The schema of the request fields can have a tree-like structure with one (and more in some cases) level of nesting. So you can easily update (or create) an object at once with all its lines (including all their data) in just one http request. See "Example #2" below.
This module has a high-load ready feature: it uses a mechanism which allow to cope with intensive and concurrent reading and writing the same Odoo records.
The previous features improves the integrity of your data, enhance the reliability of data processing and also reduce the size and complexity of code on your REST client side.
Also this module allow to fetch any PDF report, attachment or binary data from Odoo.
This module works with standard and custom Odoo models, also this API is CORS compatible.
All features and behaviour of this module are identical in all Odoo versions, so you will not have a problems when migrating.
We don't cheat with itself purchasing and don't use any SEO techniques.
By default, all Odoo models have a flat and non-predefined response schema.
There are two possible ways to set up predefined schemas for any Odoo model - in UI mode or by generating a special file. The special files also allow to create a custom API routes/endpoints, and in this case you can debug its code like an ordinary Python code using your favorite IDE.
- Each Odoo model has the following API methods:
- Read all (with optional filters, offset, limit, order, exclude_fields, include_fields)
- Read one (with optional exclude_fields, include_fields)
- Create one (with optional static default values)
- Update one/multi
- Delete one/multi
- Call any method of Odoo model, including workflow manipulations (till the Odoo v10)
- Also the 'Call any method' feature allow to execute any standard model's methods, like:
- copy()
- check_access_rights()
- check_access_rule()
- fields_get()
- etc.
- Simple REST Client example (Python):
-
import requests # Authentication r = requests.get('https://rest-api-demo.root.sx/api/auth/get_tokens?username=demo&password=demo') access_token = r.json()['access_token'] # GET - Read record r = requests.get( 'https://rest-api-demo.root.sx/api/sale.order/21', headers = {'Access-Token': access_token}) print(r.json()) # PUT - Update record r = requests.put( 'https://rest-api-demo.root.sx/api/res.partner/26?name=Tom Lenz&city=New York', headers = {'Access-Token': access_token}) print(r)
- Custom API route/endpoint example:
-
from .main import * class ControllerREST(http.Controller): @http.route('/api/your.custom.endpoint', methods=['GET'], type='http', auth='none', cors=rest_cors_value) @check_permissions def a(self, **kw): # get Odoo env params cr, uid = request.cr, request.session.uid # get any model as ordinary Odoo object odoo_model_obj = request.env(cr, uid)['res.partner'] # make models manipulations, actions, computations etc. And if you need, fill the results dictionary your_custom_dict_data = {'your_vals': '...'} # send HTTP response return successful_response(status=200, dict_data=your_custom_dict_data)
- This module adds the following 'System Parameters' in Odoo:
- rest_api.access_token_expires_in (600 seconds)
- rest_api.refresh_token_expires_in (7200 seconds)
- rest_api.cors_parameter_value_in_all_routes ('null')
- rest_api.u_escape_characters_for_unicode_in_responses (False)
- rest_api.use_redis_token_store (False)
- rest_api.redis_host (localhost)
- rest_api.redis_port (6379)
- rest_api.redis_db (0)
- rest_api.redis_password (None)
- you can create your custom API route/endpoint with your custom arbitrary code (for example call some Server Action, etc.),
- API controller in a separate file allow to debug its code like an ordinary Python code using your favorite IDE,
- you can create multiple versions of the same API resource (eg for different REST clients).
- Python examples and tests:
- /rest_api/tests/test__Auth_GetTokens.py
- /rest_api/tests/test__Create__OrderInvoice.py
- /rest_api/tests/test__Create__product.template.py
- /rest_api/tests/test__Create__res.partner.py
- /rest_api/tests/test__CreateWithAttributes__product.template.py
- /rest_api/tests/test__CreateWithImage__res.partner.py
- /rest_api/tests/test__CreateWithVendors__product.template.py
- /rest_api/tests/test__get_pdf__report.py
- /rest_api/tests/test__ReadAllWithFiltersInURL__res.partner.py
- /rest_api/tests/test__ReadAllWithFiltersOffsetLimitOrder__res.partner.py
- /rest_api/tests/test__ReadOneWithIncludeFieldsInURL__res.partner.py
- /rest_api/tests/test__Update__res.partner.py
- /rest_api/tests/test__Update__SO_TaxInLine.py
- /rest_api/tests/test__UpdateWithVendors__product.template.py
- "date_order": "2016-06-02 18:41:42",
- "name": "SO001",
- "partner_id": {
- "city": "City 1",
- "id": 6,
- "name": "Customer 1"
- "order_line": [
- {
- "name": "Product 1",
- "price_unit": 111,
- "product_uom_qty": 11,
- "price_subtotal": 1221,
- "product_id": {
- "barcode": "2400000032632",
- "name": "Product 1",
- "type": "consu",
- "attribute_line_ids": [
- {
- "display_name": "Attribute 1",
- "id": 1
- {
- "display_name": "Attribute 2",
- "id": 2
- {
- "categ_id": {
- "id": 1,
- "name": "All"
- "id": 2
- "id": 1,
- "tax_id": [
- {
- "id": 6,
- "name": "ITAX X"
- {
- "id": 7,
- "name": "Tax 15.00%"
- {
- {
- "name": "Product 2",
- "price_unit": 222,
- "product_uom_qty": 22,
- "price_subtotal": 4884,
- "product_id": {
- "barcode": null,
- "name": "Product 2",
- "type": "consu",
- "attribute_line_ids": [],
- "categ_id": {
- "id": 1,
- "name": "All"
- "id": 3
- "id": 2,
- "tax_id": [
- {
- "id": 7,
- "name": "Tax 15.00%"
- {
- {
- "amount_tax": 915.75,
- "state": "manual",
- "user_id": {
- "id": 1,
- "name": "Admin"
- "create_date": "2016-06-02 18:42:48",
- "payment_term_id": {
- "id": 2,
- "name": "15 Days"
- "id": 1,
- "amount_total": 7020.75
- # simple fields (non relational):
- "name": "TEST Name~~",
- "street": "TEST Street~~",
- "street2": "TEST Street2~~",
- "city": "TEST City~~",
- "zip": "123~~",
- "phone": "+123456789~~",
- "email": "a@b.com~~",
- # many2one fields (existing 'id', not dictionary of new record!):
- "state_id": 6,
- "country_id": 14,
- # one2many fields (list of dictionaries of records):
- "bank_ids": [
- {
- # this record will be updated (because 'id' is specified)
- "id": 56,
- "acc_number": "acc_number 1~~",
- "bank_bic": "bank_bic 1~~"
- {
- # this record will be removed (because 'id' is specified and record is empty)
- "id": 57
- {
- # this record will be created (because 'id' is not specified but record is not empty)
- "acc_number": "acc_number 4",
- "bank_bic": "bank_bic 4"
- {
- # many2many fields (list of dictionaries of existing 'ids'):
- "category_id": [
- # field's values will be replaced by this 'ids'
- {
- "id": 3
- {
- "id": 4
- # simple fields (non relational):
- "name": "TEST Name",
- "street": "TEST Street",
- "street2": "TEST Street2",
- "city": "TEST City",
- "zip": "123",
- "phone": "+123456789",
- "email": "a@b.com",
- # many2one fields (existing 'id', not dictionary of new record!):
- "state_id": 10,
- "country_id": 235,
- # one2many fields (list of dictionaries of new records):
- "bank_ids": [
- {
- "acc_number": "acc_number 1",
- "bank_bic": "bank_bic 1"
- {
- "acc_number": "acc_number 2",
- "bank_bic": "bank_bic 2"
- {
- "acc_number": "acc_number 3",
- "bank_bic": "bank_bic 3"
- {
- # many2many fields (list of dictionaries of existing 'ids'):
- "category_id": [
- {
- "id": 1
- {
- "id": 2
- {
Installation and Setup
First install one Python dependency:
pip install simplejson
Then install this module as ordinary Odoo module - in developer mode, go to menu Apps > Update Apps List > find this app 'rest_api' and install it.
This module requires 'db_name' Odoo config parameter (or command line option) with only one database (without aliases)!:
(config parameter) db_name = your_db_name (or command line option) --database=your_db_name
After the installation (or updating) of this module it need to restart Odoo server!
If you want to use the Redis token store (what is optional), you should set the Odoo system parameter rest_api.use_redis_token_store = True
, and also you need to install, (optional) setup and run Redis
server, something like this (in terminal):
(choose your package manager) sudo apt install redis-server python3-redis sudo apt-get install redis-server python3-redis sudo yum install redis python3-redis sudo dnf install redis python3-redis (run) redis-server
And then restart Odoo server.
Useful Redis links:
First possible way to set up predefined schemas for any Odoo model - in UI mode:
Activate the developer mode, go to the menu Settings > Technical > Database Structure > Models, open desired model and go to "REST API" tab:

Here is the syntax of the schema (also you can see the working schema in "Example #3").
Second possible way to set up predefined schemas for any Odoo model - by generating a special file:
1. In terminal, go to the controllers
folder and run SCRIPT_add_model.sh
script (previously ensure you have execution rights on script and write
permissions inside the folder and its files):
cd <your odoo addons path>/rest_api/controllers/ ./SCRIPT_add_model.sh
That script will ask you desired model name
and will create a new model file - model__your_model_name.py
2. In that new file, fill the three schemas of response Odoo fields for "Read one", "Read all" and "Create one" methods in three variables - OUT__your_model_name__read_one__SCHEMA
, OUT__your_model_name__read_all__SCHEMA
and OUT__your_model_name__create_one__SCHEMA
.
( # simple fields (non relational): 'simple_field_1', 'simple_field_2', ... # many2one fields: 'many2one_field_1', # will return just 'id' OR ('many2one_field_1', ( # will return dictionary of inner fields 'inner_field_1', 'inner_field_2', ... )), ... # one2many fields: ('one2many_field_1', [( 'inner_field_1', 'inner_field_2', ... )]), ... # many2many fields: ('many2many_field_1', [( 'inner_field_1', 'inner_field_2', ... )]), ... )
There can be any level of nesting of inner fields.
If you'll want to add or remove some Odoo field in REST API in the future, you'll need just add or remove/comment out a field in this schema.
3. If necessary (but not mandatory), change the values of some variables which are labeled by tag # editable
in that file. There are such variables:
- default values in "Create one" method; - successful response codes in all methods; - etc.
4. Restart Odoo server.
The advantages of that method:
Examples and Tests
See endpoints DOCUMENTATION, online TESTS & EXAMPLES and also simple REST Client Python example.
Simple 'cURL' tests:
(Bash syntax) 1. Login in Odoo and get access and refresh token: curl -i -H "Content-Type: text/html" -X GET http://localhost:8069/api/auth/get_tokens -d '{"username":"admin", "password":"admin"}' 2. Refresh access token: curl -i -H "Content-Type: text/html" -X POST http://localhost:8069/api/auth/refresh_token -d '{"refresh_token":"XXXXXXXXXXXXXXXXX"}' 3. Delete access token: curl -i -H "Content-Type: text/html" -X POST http://localhost:8069/api/auth/delete_tokens -d '{"refresh_token":"XXXXXXXXXXXXXXXXX"}' 4. res.partner - Read all (without filters): curl -i -H "Content-Type: text/html" -X GET http://localhost:8069/api/res.partner -H "Access-Token: XXXXXXXXXXXXXXXXX" 5. res.partner - Read all (with two filters): curl -i -H "Content-Type: text/html" -X GET http://localhost:8069/api/res.partner -H "Access-Token: XXXXXXXXXXXXXXXXX" -d '{"filters": [["name", "like", "ompany"], ["id", "<=", 50]]}' 6. res.partner - Read one: curl -i -H "Content-Type: text/html" -X GET http://localhost:8069/api/res.partner/3 -H "Access-Token: XXXXXXXXXXXXXXXXX" 7. res.partner - Create one: curl -i -H "Content-Type: text/html" -X POST http://localhost:8069/api/res.partner -H "Access-Token: XXXXXXXXXXXXXXXXX" -d '{"name": "TEST Name", "street": "TEST Street", "city": "TEST City"}' 8. res.partner - Update one: curl -i -H "Content-Type: text/html" -X PUT http://localhost:8069/api/res.partner/2361 -H "Access-Token: XXXXXXXXXXXXXXXXX" -d '{"name": "TEST Name~~", "street": "TEST Street~~", "city": "TEST City~~"}' 9. res.partner - Delete multi: curl -i -H "Content-Type: text/html" -X DELETE http://localhost:8069/api/res.partner/57,58,59 -H "Access-Token: XXXXXXXXXXXXXXXXX" 10. res.partner - Call method 'address_get' (without parameters): curl -i -H "Content-Type: text/html" -X PUT http://localhost:8069/api/res.partner/2361/address_get -H "Access-Token: XXXXXXXXXXXXXXXXX" 11. res.partner - Call method '_email_send' (with parameters and context): curl -i -H "Content-Type: text/html" -X PUT http://localhost:8069/api/res.partner/2361/_email_send -H "Access-Token: XXXXXXXXXXXXXXXXX" -d '{"email_from": "test@test.com", "subject": "TEST Subject", "body": "TEST Body", "__context__": {"lang": "en_US"}}' 12. report - Call method 'get_pdf' (with parameters): curl -i -H "Content-Type: text/html" -X GET http://localhost:8069/api/report/get_pdf -H "Access-Token: XXXXXXXXXXXXXXXXX" -d '{"report_name": "account.report_invoice", "ids": [3]}'
Example #1: 'sale.order - Read one' - response json:
Example #2: 'res.partner - Update one' - request json:
For "x2many" fields in second and deeper levels of nesting, you can use the special Odoo format.
Example #3: 'sale.order - Read one' - response fields schema:
( # simple fields (non relational): 'id', 'name', 'date_order', 'create_date', 'amount_tax', 'amount_total', 'state', # many2one fields: ('partner_id', ( 'id', 'name', 'city', )), ('user_id', ( 'id', 'name', )), ('payment_term_id', ( 'id', 'name', )), # one2many fields: ('order_line', [( 'id', ('product_id', ( # many2one 'id', 'name', 'type', 'barcode', ('categ_id', ( # many2one 'id', 'name', )), ('attribute_line_ids', [( # one2many 'id', 'display_name', )]), )), 'name', 'product_uom_qty', 'price_unit', ('tax_id', [( # many2many 'id', 'name', )]), 'price_subtotal', )]), )
Example #5: 'res.partner - Create one' - request json:
For "x2many" fields in second and deeper levels of nesting, you can use the special Odoo format.
Support

The support consists of free lifetime bug-fixing and keeping the actuality of this module's code according with all stable and old (since v8) Odoo versions.
This software and associated files (the "Software") may only be used (executed, modified, executed after modifications) if you have purchased a valid license from the authors, typically via Odoo Apps, or if you have received a written agreement from the authors of the Software (see the COPYRIGHT file).
You may develop Odoo modules that use the Software as a library (typically by depending on it, importing it and using its resources), but without copying any source code or material from the Software. You may distribute those modules under the license of your choice, provided that this license is compatible with the terms of the Odoo Proprietary License (For example: LGPL, MIT, or proprietary licenses similar to this one).
It is forbidden to publish, distribute, sublicense, or sell copies of the Software or modified copies of the Software.
The above copyright notice and this permission notice must be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.