Data API
Your data, from your code
Every <workspace>.madata.app workspace exposes its
data API: everything you see on screen can be read and
written from a program, with your credentials and your permissions.
One engine, two front doors. The MCP server talks to AI assistants in ready made business tools. The data API talks to your code: it exposes the raw models of the workspace, their fields and their methods. A customer, a quotation, a journal entry, a stock move: all of it readable and writable.
01Which one to pick
| You want to | Use |
|---|---|
| Connect Claude, ChatGPT, Gemini or Mistral to the workspace | MCP server: about 150 business tools, no knowledge of the data model needed. |
| Sync an online shop, a till, a directory | Data API: full control, field by field. |
| Build a custom export or feed a dashboard | Data API: search_read and read_group do the work. |
| Let an assistant act without writing code | Connect an assistant. |
Both share the same API key and the same permissions. Nothing stops you from using them together.
02Address & database
Two pieces of information are enough to call a workspace.
- Address
https://<workspace>.madata.app, the one you log into every day.- Database
prod_<workspace>. Forkouassi.madata.app, the database isprod_kouassi.- User
- A numeric
uid, returned when you log in. It is not your email address: it is its internal identifier. - Password
- An API key. Never your login password.
Each workspace has its own database, isolated from the others. A key only opens the workspace that issued it. There is nothing to "select": the database name follows from the subdomain, and a database that is not yours stays invisible.
03Three protocols, one engine
| Entry point | Protocol | For whom |
|---|---|---|
/xmlrpc/2/common/xmlrpc/2/object | XML-RPC | The most widespread. Python, PHP, Ruby and Java ship an XML-RPC client in their standard library. |
/jsonrpc | JSON-RPC 2.0 | JavaScript, Node, Go, and anything that prefers JSON. Same services, same semantics. |
/web/dataset/call_kw | JSON over session | The browser channel. Useful to replay exactly what the interface does. Requires a session cookie. |
All three hit the same engine and honour the same permissions. XML-RPC and JSON-RPC are stateless: every call carries its credentials, there is no session to maintain.
04The first call
Three steps: check the version, log in, count records.
import xmlrpc.client URL = "https://<workspace>.madata.app" DB = "prod_<workspace>" KEY = "your-api-key" LOGIN = "[email protected]" common = xmlrpc.client.ServerProxy(f"{URL}/xmlrpc/2/common") print(common.version()) # {'server_version': '17.0', 'server_serie': '17.0', 'protocol_version': 1} uid = common.authenticate(DB, LOGIN, KEY, {}) print("uid =", uid) # 7 (False if the key is refused) models = xmlrpc.client.ServerProxy(f"{URL}/xmlrpc/2/object") count = models.execute_kw(DB, uid, KEY, 'res.partner', 'search_count', [[]]) print(count, "contacts")
curl -s https://<workspace>.madata.app/jsonrpc \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"call","params":{
"service":"object","method":"execute_kw",
"args":["prod_<workspace>", 7, "your-api-key",
"res.partner","search_count",[[]]]}}'
# {"jsonrpc": "2.0", "id": null, "result": 143}
authenticate costs a round trip and
a credential check. Call it once when your program starts, keep the
uid in memory, and only go back to it when a call fails with
AccessDenied.
05The shape of a call
Everything goes through execute_kw, on the
object service. Seven arguments, always the same ones.
models.execute_kw(
db, # database name of the workspace
uid, # numeric user id
key, # API key
'res.partner', # model
'search_read', # method
[[['customer_rank', '>', 0]]], # positional arguments
{'fields': ['name', 'email'], 'limit': 10}, # keyword arguments
)
The last two are the key to reading any example: a list
of positional arguments, then a dictionary of keyword
arguments. A method with no keyword argument gets {}, or
nothing at all.
06Type conventions
| Field type | What you receive | What you send |
|---|---|---|
char, text, html | A string. An empty field is false, not "". | A string. |
integer, float, monetary | A number. A monetary is a float: the currency lives in currency_id. | A number. |
boolean | true / false. | Same. |
date | "2026-09-17". | Same shape. |
datetime | "2026-09-17 14:32:08", always UTC, with no timezone suffix. | In UTC. Converting to local time is up to you. |
selection | The technical value ("posted"), not its label. | The technical value. |
many2one | A pair [id, "label"], or false. | The id alone. |
one2many, many2many | A list of id. | Relational commands. |
binary | Base64 encoded content. | Base64. |
An unset field comes back as
false whatever its type: empty string, missing date, empty
many2one. This is the most common early trap.
if contact["email"]: is the right reflex;
contact["email"].lower() crashes on a contact with no
email.
07What stays closed
- Database management (
/web/database/*: create, duplicate, restore, list) answers403from the outside. No key opens it. - The server database list is not published:
db.listanswers "access denied". You already know yours, and it is the only one that concerns you. - Users, companies, subscription, applications are billed resources, driven by the portal (madata.africa/portail). Creating them through the API puts the workspace and the billing out of step. The MCP server refuses them outright.
08Limits & good manners
- Call duration
- Around 300 seconds. A call that runs longer is cut off: split the work instead of insisting.
- Volume
- No row cap, but a
search_readwithoutlimiton a large model brings back everything. Paginate. - Rate
- No published quota on the data API. A tight loop that saturates the workspace penalises your own users first: batch your calls.
- Batch
create,writeandreadaccept lists. A hundred contacts in one call beats a hundred calls.- Maintenance
- While the workspace is being updated, the API
answers
503with aRetry-Afterheader. Honour it and resume: nothing is lost.
Authenticate
Create a key, get a uid, understand what two factor
authentication changes.
The data model
Models, fields, external identifiers, multi company, and how to discover them instead of guessing.
Models & fields ›Write
Create, update, relate, and trigger business actions: a confirmed order, a posted invoice.
Writing data ›