---
title: "REST API"
space: "Framework"
url: "https://docs.frappe.io/framework/user/en/api/rest"
updated: "2026-05-10"
---

Frappe framework generates REST API for all of your DocTypes out of the box. You can also run arbitrary python methods using their dotted module path.

Authentication
--------------

There are two ways to authenticate through Frappe REST API. Token based authentication and password based authentication.

### 1. Token Based Authentication

A token is a pair of API Key and API Secret. To generate these tokens follow these steps:

1. Go to User list and open a user.
2. Click on the "Settings" tab. (skip this step if you don't see tabs)
3. Expand the API Access section and click on Generate Keys.
4. You will get a popup with the API Secret. Copy this value and keep it somewhere safe (Password Manager).
5. You will also see another field "API Key" in this section.

The token is generated by concatenating `api_key` and `api_secret` with a colon `:`. Pass the string `token api_key:api_secret` to the `Authorization` header in the request.


```
fetch('http://&lt;base-url&gt;/api/method/frappe.auth.get_logged_user', {
    headers: {
        'Authorization': 'token api_key:api_secret'
    }
})
.then(r =&gt; r.json())
.then(r =&gt; {
    console.log(r);
})

```

```
➜ curl http://&lt;base-url&gt;/api/method/frappe.auth.get_logged_user -H "Authorization: token api_key:api_secret"

```
Every request you make with these tokens will be logged against the user you selected in Step 1. This also means that roles will be checked against this user. You can also create a new user just for API calls.

### 2. Password Based Authentication

Password based authentication relies on cookies and session data to maintain authentication in subsequent requests. In most cases, the library you are using to issue REST calls will handle session data, but if it doesn't you should use Token based authentication.


```
fetch('http://&lt;base-url&gt;/api/method/login', {
    method: 'POST',
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({
        usr: 'username or email',
        pwd: 'password'
    })
})
.then(r =&gt; r.json())
.then(r =&gt; {
    console.log(r);
})

```

```
➜ curl --cookie-jar snowcookie --request POST "http://&lt;base-url&gt;/api/method/login" -H 'Content-Type: application/json' -H 'Accept: application/json' --data-raw "{ "usr" : "&lt;username&gt;", "pwd": "&lt;password&gt;" }"
{"message":"Logged In","home_page":"/app","full_name":"&lt;user:full_name&gt;","dashboard_route":"/sites"}

➜ curl --cookie snowcookie --request POST "http://&lt;base-url&gt;/api/method/frappe.auth.get_logged_user" -H 'Accept: application/json'
{"message":"&lt;username&gt;"}

```
### 3. Access Token

Refer documentation for [How to setup OAuth](/framework/v14/user/en/guides/integration/how_to_set_up_oauth).

Use the generated `access_token` in request header.


```
fetch('http://&lt;base-url&gt;/api/method/frappe.auth.get_logged_user', {
    headers: {
        'Authorization': 'Bearer access_token'
    }
})
.then(r =&gt; r.json())
.then(r =&gt; {
    console.log(r);
})

```
Listing Documents
-----------------

To get a list of records of a DocType, send a GET request at `/api/resource/:doctype`. By default, it will return 20 records and will only fetch the name of the records. The result for the query can be found under `data` of the payload.

We'll be using the ToDo DocType to show example responses for the queries below.


```
GET /api/resource/:doctype

```
Response


```
{
  "data":[
    {"name":"f765eef382"},
    {"name":"2a26fa1c64"},
    {"name":"f32c68060f"},
    {"name":"9065fa9832"},
    {"name":"419082fc38"},
    {"name":"6234d15099"},
    {"name":"62f2181ee0"},
    {"name":"a50afbbfaa"},
    ...
  ]
}

```
You can specify which fields to fetch in the `fields` param. It should be a JSON array.


```
GET /api/resource/:doctype?fields=["field1", "field2"]

```
Response


```
{
  "data":[
    {"description":"Business worker talk society. Each try theory prove notice middle. Crime couple trouble guy project hit.","name":"f765eef382"},
    {"description":"This reveal as look near sister. Car staff bar specific address.","name":"2a26fa1c64"},
    {"description":"Wear bag some walk. Movie partner new class tough run. Brother Democrat imagine.","name":"f32c68060f"},
    {"description":"Break laugh apply reveal new now focus heavy. Outside local staff research total. Else point try despite.","name":"9065fa9832"},
    {"description":"Truth reduce baby artist actually model. Cost phone us others himself wife almost. Language thing wonder share talk. Factor glass significant could window certain yet.","name":"419082fc38"},
    {"description":"Tv memory understand opportunity window beat physical.","name":"6234d15099"},
    {"description":"Should floor situation in response sell. Our assume company mean red majority shoulder.","name":"62f2181ee0"},
    {"description":"Performance seem sign recent. Court form me tonight simple trouble. Address job garden play teach. Happy speech amount offer change then.","name":"a50afbbfaa"},
    ...
  ]
}

```

You can specify which fields to expand in the `expand` param. It should be a JSON array.

```
GET /api/resource/:doctype?expand=["priority"]

```
Response


```
{
  "data":[
    {
        "name":"f765eef382"
        "priority": {
            "name":"a1b2c3", 
            "title": "Medium", 
            "creation": "2025-11-05 19:02:19.106966",
        },
    },
    {
        "name":"f765eef393"
        "priority": {
            "name":"a1b2c4", 
            "title": "High", 
            "creation": "2025-11-05 20:02:19.106966",
        },
    },
    ...
  ]
}

```

You can filter the records by passing `filters` param. Filters should be an array, where each filter is of the format: `[field, operator, value]`


```
GET /api/resource/:doctype?filters=[["field1", "=", "value1"], ["field2", "&gt;", "value2"]]

```
Response


```
{
  "data":[
    {"name":"f765eef382"},
    {"name":"2a26fa1c64"},
    {"name":"f32c68060f"},
    {"name":"9065fa9832"},
    {"name":"419082fc38"},
    {"name":"6234d15099"},
    {"name":"62f2181ee0"},
    {"name":"a50afbbfaa"},
    ...
  ]
}

```
`filters` parameter joins all the specified filters using `AND` SQL operator, if you want `OR` filters you can use the `or_filters` param. Syntax for `or_filters` is same as `filters.

You can also provide the sort field and order. It should be of the format `fieldname asc` or `fieldname desc`. The space should be URL encoded. In the following line, we're taking fieldname to be `title`.


```
GET /api/resource/:doctype?order_by=title%20desc

```
You can also page the results by providing the `limit_start` and `limit_page_length` params.


```
GET /api/resource/:doctype?limit_start=5&amp;limit_page_length=10

```
Response


```
{
  "data": [
    {"name":"6234d15099"},
    {"name":"62f2181ee0"},
    {"name":"a50afbbfaa"},
    {"name":"aa12a5cf71"},
    {"name":"6ac9800d4e"},
    {"name":"4bcf8b701c"},
    {"name":"aee15f4c20"},
    {"name":"6ba753afef"},
    ...
  ]
}

```
`limit` is an alias for `limit_page_length` for accessing `/api/resource` in Version 13. This means the following should also return the same payload as the above query.


```
GET /api/resource/:doctype?limit_start=5&amp;limit=10

```
By default, you will receive the data as `List[dict]`. You can retrieve your data as `List[List]` by passing `as_dict=False`.


```
GET /api/resource/:doctype?limit_start=5&amp;limit=5&amp;as_dict=False

```
Response


```
{
  "data": [
    ["6234d15099"],
    ["62f2181ee0"],
    ["a50afbbfaa"],
    ["aa12a5cf71"],
    ["6ac9800d4e"]
  ]
}

```
To debug the query built for your reqeusts, you can pass `debug=True` with the request. This returns the executed query and execution time under `exc` of the payload.


```
GET /api/resource/:doctype?limit_start=10&amp;limit=5&amp;debug=True

```
Response


```
{
  "data": [
    {"name":"4bcf8b701c"},
    {"name":"aee15f4c20"},
    {"name":"6ba753afef"},
    {"name":"f4b7e24abc"},
    {"name":"bd9156096c"}
  ],
  "exc": "[\"select `tabToDo`.`name`\\n\\t\\t\\tfrom `tabToDo`\\n\\t\\t\\t\\n\\t\\t\\t\\n\\t\\t\\t order by `tabToDo`.`modified` DESC\\n\\t\\t\\tlimit 5 offset 10\", \"Execution time: 0.0 sec\"]"
}

```
CRUD Operations
---------------

Frappe generates REST endpoints for CRUD operations for all DocTypes automatically. Make sure you set the following headers in your requests so that you get proper JSON responses.


```
{
    "Accept": "application/json",
    "Content-Type": "application/json",
}

```
### Create

Create a new document by sending a `POST` request to `/api/resource/:doctype`. Send the document as JSON in the Request Body.


```
POST /api/resource/:doctype

# Body
{"description": "New ToDo"}

```
Response


```
{
  "data": {
    "name": "af2e2d0e33",
    "owner": "Administrator",
    "creation": "2019-06-03 14:19:00.281026",
    "modified": "2019-06-03 14:19:00.281026",
    "modified_by": "Administrator",
    "idx": 0,
    "docstatus": 0,
    "status": "Open",
    "priority": "Medium",
    "description": "New ToDo",
    "doctype": "ToDo"
  }
}

```
### Read

Get a document by sending a `GET` request to `/api/resource/:doctype/:name`.


```
GET /api/resource/:doctype/:name

```
Response


```
{
  "data": {
    "name": "bf2e760e13",
    "owner": "Administrator",
    "creation": "2019-06-03 14:19:00.281026",
    "modified": "2019-06-03 14:19:00.281026",
    "modified_by": "Administrator",
    "idx": 0,
    "docstatus": 0,
    "status": "Open",
    "priority": "Medium",
    "description": "<p>Test description</p>",
    "doctype": "ToDo"
  }
}

```
Expand all link fields by sending a `GET` request to `/api/resource/:doctype/:name?expand_links=True`.

```
GET /api/resource/:doctype/:name?expand_links=True

```
Response


```
{
  "data": {
    "name": "bf2e760e13",
    "owner": "Administrator",
    "creation": "2019-06-03 14:19:00.281026",
    "modified": "2019-06-03 14:19:00.281026",
    "modified_by": "Administrator",
    "idx": 0,
    "docstatus": 0,
    "status": "Open",
    "priority": {
        "name":"a1b2c3", 
        "title": "Medium", 
        "creation": "2025-11-05 19:02:19.106966",
    },
    "description": "<p>Test description</p>",
    "doctype": "ToDo"
  }
}

```

### Update

Update a document by sending a `PUT` request to `/api/resource/:doctype/:name`. You don't need to send the whole document, instead you can just send the fields that you want to update.


```
PUT /api/resource/:doctype/:name

# Body
{"description": "New description"}

```
Response


```
{
  "data": {
    "name": "bf2e760e13",
    "owner": "Administrator",
    "creation": "2019-06-03 14:19:00.281026",
    "modified": "2019-06-03 14:21:00.785117",
    "modified_by": "Administrator",
    "idx": 0,
    "docstatus": 0,
    "status": "Open",
    "priority": "Medium",
    "description": "New description",
    "doctype": "ToDo"
  }
}

```
### Delete

Delete a document by sending a `DELETE` request to `/api/resource/:doctype/:name`.


```
DELETE /api/resource/:doctype/:name

```
Response


```
{"message": "ok"}

```
Remote Method Calls
-------------------

Frappe allows you to trigger arbitrary python methods using the REST API for handling custom logic. These methods must be marked as *whitelisted* to make them accessible via REST.

To run a whitelisted python method at `frappe.auth.get_logged_user`, send a request to the endpoint `/api/method/frappe.auth.get_logged_user`.


```
GET /api/method/frappe.auth.get_logged_user

```
Response


```
{
  "message": "john@doe.com"
}

```
* If your method returns some values, you should send a `GET` request.
* If your method changes the state of the database, use `POST`. After a successful `POST` request, the framework will automatically call `frappe.db.commit()` to commit the changes to the database.
* A successful response will return a JSON object with a `message` key.
* An errored response will return a JSON object with `exc` key which contains the stack trace and `exc_type` which contains the thrown Exception.
* The return value of the method will be converted to a JSON and sent as the response.

File Uploads
------------

There is a dedicated method `/api/method/upload_file` that accepts binary file data and uploads it into the system.

Here is the curl command for it:


```
➜ curl -X POST \
  http://&lt;base-url&gt;/api/method/upload_file \
  -H 'Accept: application/json' \
  -H 'Authorization: token xxxx:yyyy' \
  -F file=@/path/to/file/file.png

```
If you are using client side Javascript to upload files, you can append the uploaded files as FormData and send an XHR request. Here is the [implementation code](https://github.com/frappe/frappe/blob/421f070e78ee1186e7f2f4b2575d8be1d3e2d646/frappe/public/js/frappe/file_uploader/FileUploader.vue#L381-L401) in Frappe Desk.

