Joplin Data API | Joplin (2025)

Joplin Data API | Joplin (1) Joplin Data API | Joplin (2) Joplin Data API | Joplin (3)

This API is available when the clipper server is running. It provides access to the notes, notebooks, tags and other Joplin object via a REST API. Plugins can also access this API even when the clipper server is not running.

In order to use it, you'll first need to find on which port the service is running. To do so, open the Web Clipper Options in Joplin and if the service is running it should tell you on which port. Normally it runs on port 41184. If you want to find it programmatically, you may follow this kind of algorithm:

let port = null;
for (let portToTest = 41184; portToTest <= 41194; portToTest++) {
const result = pingPort(portToTest); // Call GET /ping
if (result == 'JoplinClipperServer') {
port = portToTest; // Found the port
break;
}
}

Authorisation

To prevent unauthorised applications from accessing the API, the calls must be authenticated. To do so, you must provide a token as a query parameter for each API call. You can get this token from the Joplin desktop application, on the Web Clipper Options screen.

This would be an example of valid cURL call using a token:

curl http://localhost:41184/notes?token=ABCD123ABCD123ABCD123ABCD123ABCD123

In the documentation below, the token will not be specified every time however you will need to include it.

If needed you may also request the token programmatically

Using the API

All the calls, unless noted otherwise, receives and send JSON data. For example to create a new note:

curl --data '{ "title": "My note", "body": "Some note in **Markdown**"}' http://localhost:41184/notes

In the documentation below, the calls may include special parameters such as :id or :note_id. You would replace this with the item ID or note ID.

For example, for the endpoint DELETE /tags/:id/notes/:note_id, to remove the tag with ID "ABCD1234" from the note with ID "EFGH789", you would run for example:

curl -X DELETE http://localhost:41184/tags/ABCD1234/notes/EFGH789

The four verbs supported by the API are the following ones:

  • GET: To retrieve items (notes, notebooks, etc.).
  • POST: To create new items. In general most item properties are optional. If you omit any, a default value will be used.
  • PUT: To update an item. Note in a REST API, traditionally PUT is used to completely replace an item, however in this API it will only replace the properties that are provided. For example if you PUT {"title": "my new title"}, only the "title" property will be changed. The other properties will be left untouched (they won't be cleared nor changed).
  • DELETE: To delete items.

Filtering data

You can change the fields that will be returned by the API using the fields= query parameter, which takes a list of comma separated fields. For example, to get the longitude and latitude of a note, use this:

curl http://localhost:41184/notes/ABCD123?fields=longitude,latitude

To get the IDs only of all the tags:

curl http://localhost:41184/tags?fields=id

By default API results will contain the following fields: id, parent_id, title

Pagination

All API calls that return multiple results will be paginated and will return the following structure:

KeyAlways present?Description
itemsYesThe array of items you have requested.
has_moreYesIf true, there are more items after this page. If false, it means you have reached the end of the data set.

You can specify how the results should be sorted using the order_by and order_dir query parameters, and which page to retrieve using the page parameter (starts at and defaults to 1). You can specify the number of items to be returned using the limit parameter (the maximum being 100 items).

The following call for example will initiate a request to fetch all the notes, 10 at a time, and sorted by "updated_time" ascending:

curl http://localhost:41184/notes?order_by=updated_time&order_dir=ASC&limit=10

This will return a result like this

{ "items": [ /* 10 notes */ ], "has_more": true }

Then you will resume fetching the results using this query:

curl http://localhost:41184/notes?order_by=updated_time&order_dir=ASC&limit=10&page=2

Eventually you will get some results that do not contain an "has_more" parameter, at which point you will have retrieved all the results

As an example the pseudo-code below could be used to fetch all the notes:


async function fetchJson(url) {
return (await fetch(url)).json();
}

async function fetchAllNotes() {
let pageNum = 1;
do {
const response = await fetchJson((http://localhost:41184/notes?page=' + pageNum++);
console.info('Printing notes:', response.items);
} while (response.has_more)
}

Error handling

In case of an error, an HTTP status code >= 400 will be returned along with a JSON object that provides more info about the error. The JSON object is in the format { "error": "description of error" }.

About the property types

  • Text is UTF-8.
  • All date/time are Unix timestamps in milliseconds.
  • Booleans are integer values 0 or 1.

Testing if the service is available

Call GET /ping to check if the service is available. It should return "JoplinClipperServer" if it works.

Searching

Call GET /search?query=YOUR_QUERY to search for notes. This end-point supports the field parameter which is recommended to use so that you only get the data that you need. The query syntax is as described in the main documentation: https://joplinapp.org/help/#searching

To retrieve non-notes items, such as notebooks or tags, add a type parameter and set it to the required item type name. In that case, full text search will not be used - instead it will be a simple case-insensitive search. You can also use * as a wildcard. This is convenient for example to retrieve notebooks or tags by title.

For example, to retrieve the notebook named recipes: GET /search?query=recipes&type=folder

To retrieve all the tags that start with project-: GET /search?query=project-*&type=tag

Item type IDs

Item type IDs might be referred to in certain objects you will retrieve from the API. This is the correspondence between name and ID:

NameValue
note1
folder2
setting3
resource4
tag5
note_tag6
search7
alarm8
master_key9
item_change10
note_resource11
resource_local_state12
revision13
migration14
smart_filter15
command16

Notes

Properties

NameTypeDescription
idtext
parent_idtextID of the notebook that contains this note. Change this ID to move the note to a different notebook.
titletextThe note title.
bodytextThe note body, in Markdown. May also contain HTML.
created_timeintWhen the note was created.
updated_timeintWhen the note was last updated.
is_conflictintTells whether the note is a conflict or not.
latitudenumeric
longitudenumeric
altitudenumeric
authortext
source_urltextThe full URL where the note comes from.
is_todointTells whether this note is a todo or not.
todo_dueintWhen the todo is due. An alarm will be triggered on that date.
todo_completedintTells whether todo is completed or not. This is a timestamp in milliseconds.
sourcetext
source_applicationtext
application_datatext
ordernumeric
user_created_timeintWhen the note was created. It may differ from created_time as it can be manually set by the user.
user_updated_timeintWhen the note was last updated. It may differ from updated_time as it can be manually set by the user.
encryption_cipher_texttext
encryption_appliedint
markup_languageint
is_sharedint
share_idtext
conflict_original_idtext
master_key_idtext
user_datatext
deleted_timeint
body_htmltextNote body, in HTML format
base_urltextIf body_html is provided and contains relative URLs, provide the base_url parameter too so that all the URLs can be converted to absolute ones. The base URL is basically where the HTML was fetched from, minus the query (everything after the '?'). For example if the original page was https://stackoverflow.com/search?q=%5Bjava%5D+test, the base URL is https://stackoverflow.com/search.
image_data_urltextAn image to attach to the note, in Data URL format.
crop_recttextIf an image is provided, you can also specify an optional rectangle that will be used to crop the image. In format { x: x, y: y, width: width, height: height }

GET /notes

Gets all notes

By default, this call will return the all notes except the notes in the trash folder and any conflict note. To include these too, you can specify include_deleted=1 and include_conflicts=1 as query parameters.

GET /notes/:id

Gets note with ID :id

GET /notes/:id/tags

Gets all the tags attached to this note.

GET /notes/:id/resources

Gets all the resources attached to this note.

POST /notes

Creates a new note

You can either specify the note body as Markdown by setting the body parameter, or in HTML by setting the body_html.

Examples:

  • Create a note from some Markdown text
curl --data '{ "title": "My note", "body": "Some note in **Markdown**"}' http://127.0.0.1:41184/notes
  • Create a note from some HTML
curl --data '{ "title": "My note", "body_html": "Some note in <b>HTML</b>"}' http://127.0.0.1:41184/notes
  • Create a note and attach an image to it:
curl --data '{ "title": "Image test", "body": "Here is Joplin icon:", "image_data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANZJREFUeNoAyAA3/wFwtO3K6gUB/vz2+Prw9fj/+/r+/wBZKAAExOgF4/MC9ff+MRH6Ui4E+/0Bqc/zutj6AgT+/Pz7+vv7++nu82c4DlMqCvLs8goA/gL8/fz09fb59vXa6vzZ6vjT5fbn6voD/fwC8vX4UiT9Zi//APHyAP8ACgUBAPv5APz7BPj2+DIaC2o3E+3o6ywaC5fT6gD6/QD9/QEVf9kD+/dcLQgJA/7v8vqfwOf18wA1IAIEVycAyt//v9XvAPv7APz8LhoIAPz9Ri4OAgwARgx4W/6fVeEAAAAASUVORK5CYII="}' http://127.0.0.1:41184/notes

Creating a note with a specific ID

When a new note is created, it is automatically assigned a new unique ID so normally you do not need to set the ID. However, if for some reason you want to set it, you can supply it as the id property. It needs to be a 32 characters long string in hexadecimal. Make sure it is unique, for example by generating it using whatever GUID function is available in your programming language.

curl --data '{ "id": "00a87474082744c1a8515da6aa5792d2", "title": "My note with custom ID"}' http://127.0.0.1:41184/notes

PUT /notes/:id

Sets the properties of the note with ID :id

DELETE /notes/:id

Deletes the note with ID :id

By default, the note will be moved to the trash. To permanently delete it, add the query parameter permanent=1

Folders

This is actually a notebook. Internally notebooks are called "folders".

Properties

NameTypeDescription
idtext
titletextThe folder title.
created_timeintWhen the folder was created.
updated_timeintWhen the folder was last updated.
user_created_timeintWhen the folder was created. It may differ from created_time as it can be manually set by the user.
user_updated_timeintWhen the folder was last updated. It may differ from updated_time as it can be manually set by the user.
encryption_cipher_texttext
encryption_appliedint
parent_idtext
is_sharedint
share_idtext
master_key_idtext
icontext
user_datatext
deleted_timeint

GET /folders

Gets all folders

The folders are returned as a tree. The sub-notebooks of a notebook, if any, are under the children key.

GET /folders/:id

Gets folder with ID :id

GET /folders/:id/notes

Gets all the notes inside this folder.

POST /folders

Creates a new folder

PUT /folders/:id

Sets the properties of the folder with ID :id

DELETE /folders/:id

Deletes the folder with ID :id

By default, the folder will be moved to the trash. To permanently delete it, add the query parameter permanent=1

Resources

Properties

NameTypeDescription
idtext
titletextThe resource title.
mimetext
filenametext
created_timeintWhen the resource was created.
updated_timeintWhen the resource was last updated.
user_created_timeintWhen the resource was created. It may differ from created_time as it can be manually set by the user.
user_updated_timeintWhen the resource was last updated. It may differ from updated_time as it can be manually set by the user.
file_extensiontext
encryption_cipher_texttext
encryption_appliedint
encryption_blob_encryptedint
sizeint
is_sharedint
share_idtext
master_key_idtext
user_datatext
blob_updated_timeint
ocr_texttext
ocr_detailstext
ocr_statusint
ocr_errortext

GET /resources

Gets all resources

GET /resources/:id

Gets resource with ID :id

GET /resources/:id/file

Gets the actual file associated with this resource.

GET /resources/:id/notes

Gets the notes (IDs) associated with a resource.

POST /resources

Creates a new resource

Creating a new resource is special because you also need to upload the file. Unlike other API calls, this one must have the "multipart/form-data" Content-Type. The file data must be passed to the "data" form field, and the other properties to the "props" form field. An example of a valid call with cURL would be:

curl -F 'data=@/path/to/file.jpg' -F 'props={"title":"my resource title"}' http://localhost:41184/resources

To update the resource content, you can make a PUT request with the same arguments:

curl -X PUT -F 'data=@/path/to/file.jpg' -F 'props={"title":"my modified title"}' http://localhost:41184/resources/8fe1417d7b184324bf6b0122b76c4696

The "data" field is required, while the "props" one is not. If not specified, default values will be used.

Or if you only need to update the resource properties (title, etc.), without changing the content, you can make a regular PUT request:

curl -X PUT --data '{"title": "My new title"}' http://localhost:41184/resources/8fe1417d7b184324bf6b0122b76c4696

From a plugin the syntax to create a resource is also a bit special:

 await joplin.data.post(
["resources"],
null,
{ title: "test.jpg" }, // Resource metadata
[
{
path: "/path/to/test.jpg", // Actual file
},
]
);

PUT /resources/:id

Sets the properties of the resource with ID :id

You may also update the file data by specifying a file (See POST /resources example).

DELETE /resources/:id

Deletes the resource with ID :id

Properties

NameTypeDescription
idtext
titletextThe tag title.
created_timeintWhen the tag was created.
updated_timeintWhen the tag was last updated.
user_created_timeintWhen the tag was created. It may differ from created_time as it can be manually set by the user.
user_updated_timeintWhen the tag was last updated. It may differ from updated_time as it can be manually set by the user.
encryption_cipher_texttext
encryption_appliedint
is_sharedint
parent_idtext
user_datatext

GET /tags

Gets all tags

GET /tags/:id

Gets tag with ID :id

GET /tags/:id/notes

Gets all the notes with this tag.

POST /tags

Creates a new tag

POST /tags/:id/notes

Post a note to this endpoint to add the tag to the note. The note data must at least contain an ID property (all other properties will be ignored).

PUT /tags/:id

Sets the properties of the tag with ID :id

DELETE /tags/:id

Deletes the tag with ID :id

DELETE /tags/:id/notes/:note_id

Remove the tag from the note.

Revisions

Properties

NameTypeDescription
idtext
parent_idtext
item_typeint
item_idtext
item_updated_timeint
title_difftext
body_difftext
metadata_difftext
encryption_cipher_texttext
encryption_appliedint
updated_timeint
created_timeint

GET /revisions

Gets all revisions

GET /revisions/:id

Gets revision with ID :id

POST /revisions

Creates a new revision

PUT /revisions/:id

Sets the properties of the revision with ID :id

DELETE /revisions/:id

Deletes the revision with ID :id

Events

This end point can be used to retrieve the latest note changes. Currently only note changes are tracked.

Properties

NameTypeDescription
idint
item_typeintThe item type (see table above for the list of item types)
item_idtextThe item ID
typeintThe type of change - either 1 (created), 2 (updated) or 3 (deleted)
created_timeintWhen the event was generated
sourceintUnused
before_change_itemtextUnused

GET /events

Returns a paginated list of recent events. A cursor property should be provided, which tells from what point in time the events should be returned. The API will return a cursor property, to tell from where to resume retrieving events, as well as an has_more (tells if more changes can be retrieved) and items property, which will contain the list of events. Events are kept for up to 90 days.

If no cursor property is provided, the API will respond with the latest change ID. That can be used to retrieve future events later on.

The results are paginated so you may need multiple calls to retrieve all the events. Use the has_more property to know if more can be retrieved.

GET /events/:id

Returns the event with the given ID.

Joplin Data API | Joplin (2025)

FAQs

How to collect data through API? ›

Now, we will use Acho as an example to demonstrate how to connect to your API with no coding.
  1. Configure the API endpoint. An API endpoint can be complex. ...
  2. Create an API resource. ...
  3. Store data into a database. ...
  4. Transform the API data. ...
  5. Export the data to an application. ...
  6. Check and maintain the pipeline.
Dec 17, 2023

What is Joplin server? ›

Joplin Server is a free, self-hosted open-source note-taking and to-do application that captures ideas as notes, organizes them and synchronizes them to devices.

How can I extract data from an API? ›

There's a structured way to access data from the endpoints as provided by the API. The protocols are confined and must be followed as per the tools that are used to make the API request. The tools that can be used for this purpose can be cURL, Postman, or an HTTP library of the programming language being incorporated.

How to fetch data using API? ›

With the Fetch API, you make a request by calling fetch() , which is available as a global function in both window and worker contexts. You pass it a Request object or a string containing the URL to fetch, along with an optional argument to configure the request.

What is better than Joplin? ›

The best overall Joplin alternative is Evernote. Other similar apps like Joplin are Microsoft OneNote, Google Workspace, ClickUp, and Notion.

How does Joplin work? ›

Joplin is a free, open source note taking and to-do application, which can handle a large number of notes organised into notebooks. The notes are searchable, can be copied, tagged and modified either from the applications directly or from your own text editor. The notes are in Markdown format.

Who is the default user of Joplin server? ›

By default, Joplin Server will be setup with an admin user with email admin@localhost and password admin. For security purposes, the admin user's credentials should be changed.

Can you send data through an API? ›

The API can pass data back and forth between two applications, access the features or services of other applications, or create an application. APIs are essential because they allow developers to easily integrate existing services or features from other applications without actually developing the service or feature.

How to create API to get data? ›

How to Create an API
  1. Determine Your Requirements. First, you'll need to determine your API requirements. ...
  2. Design Your API. Next, you'll need to consider API design. ...
  3. Develop Your API. Now, it's time to start developing your API product. ...
  4. Test Your API. ...
  5. Publish/Deploy Your API. ...
  6. Monitor Your API.
Feb 7, 2023

How to store data through API? ›

The Web Storage API is a set of APIs exposed by the browser so that you can store data in the browser. The data stored in the Web Storage use the key/value pair format, and both data will be stored as strings.

How to pull data from API into Excel? ›

Then complete the following steps to load data from API to Excel sheet.
  1. Collect data. Insert the API URL to the JSON URL field. ...
  2. Transform data. At this step, you can preview your data and organize it if needed. ...
  3. Load data and schedule refresh. Select a workbook on OneDrive and a worksheet where the data will be loaded.
5 days ago

Top Articles
Latest Posts
Recommended Articles
Article information

Author: Moshe Kshlerin

Last Updated:

Views: 6579

Rating: 4.7 / 5 (77 voted)

Reviews: 84% of readers found this page helpful

Author information

Name: Moshe Kshlerin

Birthday: 1994-01-25

Address: Suite 609 315 Lupita Unions, Ronnieburgh, MI 62697

Phone: +2424755286529

Job: District Education Designer

Hobby: Yoga, Gunsmithing, Singing, 3D printing, Nordic skating, Soapmaking, Juggling

Introduction: My name is Moshe Kshlerin, I am a gleaming, attractive, outstanding, pleasant, delightful, outstanding, famous person who loves writing and wants to share my knowledge and understanding with you.