The simplest method to access the server is using XML-RPC. We can use the xmlrpclib library from Python's standard library for this. Remember that we are programming a client in order to connect to a server, so we need an Odoo server instance running to connect to. In our examples, we will assume that an Odoo server instance is running on the same machine (localhost), but you can use any reachable IP address or server name, if the server is running in a different machine.

With XML-RPC, no session is maintained and the authentication credentials are sent with every request. This adds some overhead to the protocol, but makes it simpler to use.

Next, we set up access to the server methods that need a login to be accessed. These are exposed at the /xmlrpc/2/object endpoint, as shown in the following:

>>> api = xmlrpclib.ServerProxy('%s/xmlrpc/2/object' % srv)
>>> api.execute_kw(db, uid, pwd, 'res.partner', 'search_count'[[]])
40

Here, we are doing our first access to the server API, performing a count on the Partner records. Methods are called using the execute_kw() method that takes the following arguments:

The preceding example calls the search_count method of the res.partner model with one positional argument, [], and no keyword arguments. The positional argument is a search domain; since we are providing an empty list, it counts all the Partners.

Frequent actions are search and read. When called from the RPC, the search method returns a list of IDs matching a domain. The browse method is not available from the RPC, and read should be used in its place to give a list of record IDs and retrieve their data, as shown in the following code:

>>> api.execute_kw(db, uid, pwd, 'res.partner', 'search', [[('country_id', '=', 'be'), ('parent_id', '!=', False)]]) 
[18, 33, 23, 22] 
>>> api.execute_kw(db, uid, pwd, 'res.partner', 'read',  [[18]],  {'fields': ['id', 'name', 'parent_id']}) 
[{'parent_id': [8, 'Agrolait'], 'id': 18, 'name': 'Edward Foster'}] 

Note that for the read method, we are using one positional argument for the list of IDs, [18], and one keyword argument, fields. We can also notice that many-to-one relational fields are retrieved as a pair, with the related record's ID and display name. That's something to keep in mind when processing the data in your code.

The search and read combination is so frequent that a search_read method is provided to perform both operations in a single step. The same result as the previous two steps can be obtained with the following:

>>> api.execute_kw(db, uid, pwd, 'res.partner', 'search_read',  [[('country_id', '=', 'be'), ('parent_id', '!=', False)]],  {'fields': ['id', 'name', 'parent_id']})

The search_read method behaves like read, but expects a domain as a first positional argument instead of a list of IDs. It's worth mentioning that the field argument on read and search_read is not mandatory. If not provided, all fields will be retrieved. This may cause expensive computations of function fields and a large amount data to be retrieved , but probably never used, so it is generally recommended to provide an explicit list of fields.