SiYuan Note API
Use the SiYuan kernel API via curl to search, read, create, update, and delete blocks and documents in a self-hosted knowledge base. No extra tools needed — just curl and an API token.
When to Use
- User asks to search, read, create, update, or delete content in a SiYuan knowledge base.
- User mentions "SiYuan", "思源笔记", or references a self-hosted PKM with block-level IDs.
- User wants to run SQL queries against their notes database or export documents as Markdown.
- User needs to manage notebooks, documents, blocks, or block attributes programmatically.
Prerequisites
Install and run SiYuan (desktop app or Docker container).
Get your API token: Settings > About > API token in the SiYuan desktop app.
Store credentials in
${HERMES_HOME:-~/.hermes}/.env:SIYUAN_TOKEN=your_token_here SIYUAN_URL=http://127.0.0.1:6806SIYUAN_URLdefaults tohttp://127.0.0.1:6806if not set.Ensure
curlandjqare available on PATH.
Windows (PowerShell) note: In PowerShell, use $env:SIYUAN_TOKEN and $env:SIYUAN_URL instead of $SIYUAN_TOKEN. For multi-line JSON bodies, prefer writing the JSON to a temp file and using -d "@file.json" to avoid quoting issues, or use single-quoted here-strings.
Procedure
API Basics
All SiYuan API calls are POST with a JSON body — even read-only operations. Never use GET.
Every request follows this pattern:
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/..." \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"param": "value"}'
Responses are JSON:
{"code": 0, "msg": "", "data": { ... }}
code: 0 means success. Any other value is an error — check msg for details. Always verify code == 0 before processing data.
ID format: SiYuan IDs look like 20210808180117-6v0mkxr (14-digit timestamp + 7 alphanumeric chars). Reject any ID that does not match the pattern YYYYMMDDHHmmss-xxxxxxx.
Quick Reference
| Operation | Endpoint |
|---|---|
| Full-text search | /api/search/fullTextSearchBlock |
| SQL query | /api/query/sql |
| Read block (Kramdown) | /api/block/getBlockKramdown |
| Read child blocks | /api/block/getChildBlocks |
| Get human-readable path | /api/filetree/getHPathByID |
| Get block attributes | /api/attr/getBlockAttrs |
| List notebooks | /api/notebook/lsNotebooks |
| List documents | /api/filetree/listDocsByPath |
| Create notebook | /api/notebook/createNotebook |
| Create document | /api/filetree/createDocWithMd |
| Append block | /api/block/appendBlock |
| Prepend block | /api/block/prependBlock |
| Insert block | /api/block/insertBlock |
| Update block | /api/block/updateBlock |
| Rename document | /api/filetree/renameDocByID |
| Set attributes | /api/attr/setBlockAttrs |
| Delete block | /api/block/deleteBlock |
| Delete document | /api/filetree/removeDocByID |
| Delete notebook | /api/notebook/removeNotebook |
| Export as Markdown | /api/export/exportMdContent |
Step 1 — Verify Connectivity
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/notebook/lsNotebooks" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{}' | jq '.code, .data.notebooks[] | {id, name, closed}'
If code is not 0, the token or URL is wrong.
Step 2 — Search (Full-Text)
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/search/fullTextSearchBlock" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query": "meeting notes", "page": 0}' | jq '.data.blocks[:5]'
Step 3 — Search (SQL)
Query the blocks database directly. Only SELECT statements are safe. Never send INSERT, UPDATE, DELETE, or DROP.
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/query/sql" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"stmt": "SELECT id, content, type, box FROM blocks WHERE content LIKE '''%keyword%''' AND type='''p''' LIMIT 20"}' | jq '.data'
Useful columns: id, parent_id, root_id, box (notebook ID), path, content, type, subtype, created, updated.
Step 4 — Read Block Content (Kramdown)
Returns block content in Kramdown (Markdown-like) format.
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/block/getBlockKramdown" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id": "20210808180117-6v0mkxr"}' | jq '.data.kramdown'
Step 5 — Read Child Blocks
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/block/getChildBlocks" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id": "20210808180117-6v0mkxr"}' | jq '.data'
Step 6 — Get Human-Readable Path
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/filetree/getHPathByID" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id": "20210808180117-6v0mkxr"}' | jq '.data'
Step 7 — Get Block Attributes
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/attr/getBlockAttrs" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id": "20210808180117-6v0mkxr"}' | jq '.data'
Step 8 — List Notebooks
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/notebook/lsNotebooks" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{}' | jq '.data.notebooks[] | {id, name, closed}'
Step 9 — List Documents in a Notebook
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/filetree/listDocsByPath" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"notebook": "NOTEBOOK_ID", "path": "/"}' | jq '.data.files[] | {id, name}'
Step 10 — Create a Notebook
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/notebook/createNotebook" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "My New Notebook"}' | jq '.data.notebook.id'
Step 11 — Create a Document
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/filetree/createDocWithMd" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"notebook": "NOTEBOOK_ID",
"path": "/Meeting Notes/2026-03-22",
"markdown": "# Meeting Notes\n\n- Discussed project timeline\n- Assigned tasks"
}' | jq '.data'
Step 12 — Append Block to a Document or Block
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/block/appendBlock" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"parentID": "DOCUMENT_OR_BLOCK_ID",
"data": "New paragraph added at the end.",
"dataType": "markdown"
}' | jq '.data'
Also available:
/api/block/prependBlock— same params, inserts at the beginning./api/block/insertBlock— usespreviousIDinstead ofparentIDto insert after a specific block.
Step 13 — Update Block Content
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/block/updateBlock" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "BLOCK_ID",
"data": "Updated content here.",
"dataType": "markdown"
}' | jq '.data'
Step 14 — Rename a Document
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/filetree/renameDocByID" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id": "DOCUMENT_ID", "title": "New Title"}'
Step 15 — Set Block Attributes
Custom attributes must be prefixed with custom-:
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/attr/setBlockAttrs" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "BLOCK_ID",
"attrs": {
"custom-status": "reviewed",
"custom-priority": "high"
}
}'
Step 16 — Delete a Block
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/block/deleteBlock" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id": "BLOCK_ID"}'
To delete a whole document: use /api/filetree/removeDocByID with {"id": "DOC_ID"}.
To delete a notebook: use /api/notebook/removeNotebook with {"notebook": "NOTEBOOK_ID"}.
Step 17 — Export Document as Markdown
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/export/exportMdContent" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id": "DOCUMENT_ID"}' | jq -r '.data.content'
Block Types
Common type values in SQL queries:
| Type | Description |
|---|---|
d |
Document (root block) |
p |
Paragraph |
h |
Heading |
l |
List |
i |
List item |
c |
Code block |
m |
Math block |
t |
Table |
b |
Blockquote |
s |
Super block |
html |
HTML block |
Examples
Find all headings containing "project" and show their document paths
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/query/sql" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"stmt": "SELECT id, content, root_id FROM blocks WHERE type='''h''' AND content LIKE '''%project%''' LIMIT 50"}' \
| jq '.data[]'
Create a daily journal document and append a task
# 1. Create the document
DOC_ID=$(curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/filetree/createDocWithMd" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"notebook": "NOTEBOOK_ID",
"path": "/Journal/2026-03-22",
"markdown": "# 2026-03-22"
}' | jq -r '.data')
# 2. Append a task block
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/block/appendBlock" \
-H "Authorization: Token $SIYUAN_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"parentID\": \"$DOC_ID\", \"data\": \"- [ ] Review pull requests\", \"dataType\": \"markdown\"}"
Pitfalls
- All endpoints are POST — even read-only operations like search and SQL. Do not use GET.
- SQL safety: only use SELECT queries. INSERT/UPDATE/DELETE/DROP are dangerous and should never be sent.
- ID validation: IDs match the pattern
YYYYMMDDHHmmss-xxxxxxx. Reject anything else before making API calls. - Error responses: always check
code != 0in responses before processingdata. Themsgfield contains the error message. - Large documents: block content and export results can be very large. Use
LIMITin SQL and pipe throughjqto extract only what you need. - Notebook IDs: when working with a specific notebook, get its ID first via
lsNotebooks— do not assume the notebook name is the ID. - Custom attributes: must be prefixed with
custom-. Attributes without this prefix may be ignored or overwritten by SiYuan internals. - PowerShell quoting: nested single quotes in SQL JSON bodies are painful in PowerShell. Write the JSON to a temp file and use
curl -d "@body.json"to avoid quoting hell. - Closed notebooks:
lsNotebooksreturns both open and closed notebooks. Filter onclosedif you only want accessible notebooks.
Verification
Check token and connectivity:
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/notebook/lsNotebooks" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{}' | jq '.code'Expected output:
0Verify a search returns results:
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/search/fullTextSearchBlock" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"query": "test", "page": 0}' | jq '.data.blocks | length'Expected output: a non-negative integer.
Verify a block read succeeds:
curl -s -X POST "${SIYUAN_URL:-http://127.0.0.1:6806}/api/block/getBlockKramdown" \ -H "Authorization: Token $SIYUAN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"id": "20210808180117-6v0mkxr"}' | jq '.code, .data.kramdown'Expected:
0followed by the block's Kramdown content.
Alternative: MCP Server
If you prefer a native integration instead of curl, install the SiYuan MCP server:
# In ~/.hermes/config.yaml under mcp_servers:
mcp_servers:
siyuan:
command: npx
args: ["-y", "@porkll/siyuan-mcp"]
env:
SIYUAN_TOKEN: "YOUR_TOKEN"
SIYUAN_URL: "http://127.0.0.1:6806"
Related Skills
- obsidian — local Markdown-based PKM with file-system access.
- notion — cloud-based workspace with database and page APIs.