SYNCTHING-REST-API(7)
๐ syncthing-rest-api โ NAME ๐
Okay bestie, welcome to the Syncthing REST API page โ this is the official SYNCTHING-REST-API(7) manual, Syncthing flavour, and it's lowkey a whole vibe โจ
So here's the tea: Syncthing exposes a REST interface over HTTP on the GUI port. That's what the GUI itself uses (from Javascript, periodt), and it's also what any other process can use if they wanna control Syncthing from the outside ๐. In most cases both the input and output data is in JSON format. And the interface is subject to change โ no cap, that's literally in the docs, so don't get too attached ๐
๐ API KEY ๐
Okay so you cannot just waltz in here. To use the REST API an API key must be set and used. Where do you get one?
- You can generate it in the GUI, or
- set it in the
configuration/gui/apikeyelement in the configuration file ๐
And then how do you actually present it? Two ways, both iconic:
- set the request header
X-API-Keyto the API key value, or - set it as a Bearer token in the
Authorizationheader ๐
For example, these work with curl:
curl -X POST -H "X-API-Key: abc123" http://localhost:8384/rest/...
curl -X POST -H "Authorization: Bearer abc123" http://localhost:8384/rest/...
๐ซถ psst โ add the -k flag when using HTTPS with a Syncthing generated or self signed certificate. Your girl is not letting a cert warning ruin the moment.
One exception to this requirement is /rest/noauth โ you do not need an API key to use those endpoints. Why? Because this way third-party devices and services can do simple calls that don't expose sensitive information without having to expose your API key. It's giving respectful boundaries, honestly ๐
๐ RESULT PAGINATION ๐
Some GET endpoints take optional page and perpage arguments for pagination. Here's how it goes:
- No more than
perpageresult entries are returned in an array โ andperpagedefaults to65536if not given ๐ญ (yes, big number, we love her). - To access further entries, passing the
pageparameter will advance in the results by that many pages. - The actually used parameters are always returned as attributes in the response object โ so you can check what the server actually did, which is honestly so transparent of it ๐ธ
๐ ๏ธ SYSTEM ENDPOINTS ๐ ๏ธ
GET /rest/system/browse
Returns a list of directories matching the path given by the optional parameter current.
The path can use patterns as described in Go's filepath package: https://pkg.go.dev/path/filepath#Match. And heads up โ a * will always be appended to the given path, so like /tmp/ matches all its subdirectories. Sneaky but efficient ๐
If the option current is not given, filesystem root paths are returned.
Watch her work โจ
$ curl -H "X-API-Key: yourkey" localhost:8384/rest/system/browse | json_pp
[
"/"
]
$ curl -H "X-API-Key: yourkey" localhost:8384/rest/system/browse?current=/var/ | json_pp
[
"/var/backups/",
"/var/cache/",
"/var/lib/",
"/var/local/",
"/var/lock/",
"/var/log/",
"/var/mail/",
"/var/opt/",
"/var/run/",
"/var/spool/",
"/var/tmp/"
]
$ curl -H "X-API-Key: yourkey" localhost:8384/rest/system/browse?current=/var/*o | json_pp
[
"/var/local/",
"/var/lock/",
"/var/log/",
"/var/opt/",
"/var/spool/"
]
GET /rest/system/config (DEPRECATED)
๐ DEPRECATED ALERT ๐ Deprecated since versionv1.12.0: This endpoint still works as before but is deprecated. Use/rest/configinstead. She's retired but still showing up to the party, we love a legacy queen ๐
Returns the current configuration.
{
"version": 35,
"folders": [
{
"id": "default",
"label": "Default Folder",
"filesystemType": "basic",
"path": "...",
"type": "sendreceive",
"devices": [
{
"deviceID": "...",
"introducedBy": "",
"encryptionPassword": ""
}
],
"rescanIntervalS": 3600,
"fsWatcherEnabled": true,
"fsWatcherDelayS": 10,
"ignorePerms": false,
"autoNormalize": true,
"minDiskFree": {
"value": 1,
"unit": "%"
},
"versioning": {
"type": "",
"params": {},
"cleanupIntervalS": 3600,
"fsPath": "",
"fsType": "basic"
},
"copiers": 0,
"pullerMaxPendingKiB": 0,
"hashers": 0,
"order": "random",
"ignoreDelete": false,
"scanProgressIntervalS": 0,
"pullerPauseS": 0,
"maxConflicts": -1,
"disableSparseFiles": false,
"paused": false,
"markerName": ".stfolder",
"copyOwnershipFromParent": false,
"modTimeWindowS": 0,
"maxConcurrentWrites": 16,
"disableFsync": false,
"blockPullOrder": "standard",
"copyRangeMethod": "standard",
"caseSensitiveFS": false,
"junctionsAsDirs": true
}
],
"devices": [
{
"deviceID": "...",
"name": "Laptop",
"addresses": [
"dynamic",
"tcp://192.168.1.2:22000"
],
"compression": "metadata",
"certName": "",
"introducer": false,
"skipIntroductionRemovals": false,
"introducedBy": "",
"paused": false,
"allowedNetworks": [],
"autoAcceptFolders": false,
"maxSendKbps": 0,
"maxRecvKbps": 0,
"ignoredFolders": [
{
"time": "2022-01-09T19:09:52Z",
"id": "br63e-wyhb7",
"label": "Foo"
}
],
"maxRequestKiB": 0,
"untrusted": false,
"remoteGUIPort": 0
}
],
"gui": {
"enabled": true,
"address": "127.0.0.1:8384",
"unixSocketPermissions": "",
"user": "Username",
"password": "$2a$10$ZFws69T4FlvWwsqeIwL.TOo5zOYqsa/.TxlUnsGYS.j3JvjFTmxo6",
"authMode": "static",
"useTLS": false,
"apiKey": "k1dnz1Dd0rzTBjjFFh7CXPnrF12C49B1",
"insecureAdminAccess": false,
"theme": "default",
"insecureSkipHostcheck": false,
"insecureAllowFrameLoading": false
},
"ldap": {
"address": "",
"bindDN": "",
"transport": "plain",
"insecureSkipVerify": false,
"searchBaseDN": "",
"searchFilter": ""
},
"options": {
"listenAddresses": [
"default"
],
"globalAnnounceServers": [
"default"
],
"globalAnnounceEnabled": true,
"localAnnounceEnabled": true,
"localAnnouncePort": 21027,
"localAnnounceMCAddr": "[ff12::8384]:21027",
"maxSendKbps": 0,
"maxRecvKbps": 0,
"reconnectionIntervalS": 60,
"relaysEnabled": true,
"relayReconnectIntervalM": 10,
"startBrowser": true,
"natEnabled": true,
"natLeaseMinutes": 60,
"natRenewalMinutes": 30,
"natTimeoutSeconds": 10,
"urAccepted": 0,
"urSeen": 0,
"urUniqueId": "...",
"urURL": "https://data.syncthing.net/newdata",
"urPostInsecurely": false,
"urInitialDelayS": 1800,
"autoUpgradeIntervalH": 12,
"upgradeToPreReleases": false,
"keepTemporariesH": 24,
"progressUpdateIntervalS": 5,
"limitBandwidthInLan": false,
"minHomeDiskFree": {
"value": 1,
"unit": "%"
},
"releasesURL": "https://upgrades.syncthing.net/meta.json",
"alwaysLocalNets": [],
"overwriteRemoteDeviceNamesOnConnect": false,
"tempIndexMinBlocks": 10,
"unackedNotificationIDs": [
"authenticationUserAndPassword"
],
"trafficClass": 0,
"setLowPriority": true,
"maxFolderConcurrency": 0,
"crURL": "https://crash.syncthing.net/newcrash",
"crashReportingEnabled": true,
"stunKeepaliveStartS": 180,
"stunKeepaliveMinS": 20,
"stunServers": [
"default"
],
"maxConcurrentIncomingRequestKiB": 0,
"announceLANAddresses": true,
"sendFullIndexOnUpgrade": false,
"featureFlags": [],
"connectionLimitEnough": 0,
"connectionLimitMax": 0
},
"remoteIgnoredDevices": [
{
"time": "2022-01-09T20:02:01Z",
"deviceID": "...",
"name": "...",
"address": "192.168.0.20:22000"
}
],
"defaults": {
"folder": {
"id": "",
"label": "",
"filesystemType": "basic",
"path": "~",
"type": "sendreceive",
"devices": [
{
"deviceID": "...",
"introducedBy": "",
"encryptionPassword": ""
}
],
"rescanIntervalS": 3600,
"fsWatcherEnabled": true,
"fsWatcherDelayS": 10,
"ignorePerms": false,
"autoNormalize": true,
"minDiskFree": {
"value": 1,
"unit": "%"
},
"versioning": {
"type": "",
"params": {},
"cleanupIntervalS": 3600,
"fsPath": "",
"fsType": "basic"
},
"copiers": 0,
"pullerMaxPendingKiB": 0,
"hashers": 0,
"order": "random",
"ignoreDelete": false,
"scanProgressIntervalS": 0,
"pullerPauseS": 0,
"maxConflicts": 10,
"disableSparseFiles": false,
"paused": false,
"markerName": ".stfolder",
"copyOwnershipFromParent": false,
"modTimeWindowS": 0,
"maxConcurrentWrites": 16,
"disableFsync": false,
"blockPullOrder": "standard",
"copyRangeMethod": "standard",
"caseSensitiveFS": false,
"junctionsAsDirs": false
},
"device": {
"deviceID": "",
"name": "",
"addresses": [
"dynamic"
],
"compression": "metadata",
"certName": "",
"introducer": false,
"skipIntroductionRemovals": false,
"introducedBy": "",
"paused": false,
"allowedNetworks": [],
"autoAcceptFolders": false,
"maxSendKbps": 0,
"maxRecvKbps": 0,
"ignoredFolders": [],
"maxRequestKiB": 0,
"untrusted": false,
"remoteGUIPort": 0
}
}
}
And that's where part one wraps โ but bestie, we are so not done, more endpoints are coming ๐ฆ
GET /rest/system/config/insync โ DEPRECATED ๐
Deprecated since version v1.12.0: this endpoint still works exactly as before, but it's deprecated, bestie. Use /rest/config/restart-required instead. โจ
Tells you whether the config is in sync โ like, is the config that's actually running the same one that's sitting on disk? ๐
{
"configInSync": true
}
POST /rest/system/config โ DEPRECATED ๐
Deprecated since version v1.12.0: still works as before, but deprecated. Use Config Endpoints instead, periodt.
You post the full contents of the configuration here, in the same format that the matching GET request gives you back. When the post goes through, the config you posted gets applied immediately โ except for the changes that need a restart. To check if a restart is needed, hit /rest/config/restart-required. ๐
This endpoint is literally the main character when it comes to controlling Syncthing โ even if the change is only about a tiny lil part of the config. The usual workflow is: get the config, change the parts you need, post it right back. That's the whole vibe. ๐
GET /rest/system/connections ๐
NOTE: the return format changed in versions0.13.0,0.14.14,1.2.0,1.19.0,1.23.0and1.25.0. So if you're on an old version, don't come at me if it looks different. ๐ญ
Returns the list of configured devices plus some metadata that goes with them. ๐ซถ
The connection types are tcp-client, tcp-server, relay-client, relay-server, quic-client and quic-server. Slay.
{
"connections": {
"DOVII4U-SQEEESM-VZ2CVTC-CJM4YN5-QNV7DCU-5U3ASRL-YVFG6TH-W5DV5AA": {
"address": "",
"at": "0001-01-01T00:00:00Z",
"clientVersion": "",
"connected": false,
"inBytesTotal": 0,
"isLocal": false,
"outBytesTotal": 0,
"paused": false,
"startedAt": "0001-01-01T00:00:00Z",
"type": ""
},
"UYGDMA4-TPHOFO5-2VQYDCC-7CWX7XW-INZINQT-LE4B42N-4JUZTSM-IWCSXA4": {
"address": "",
"at": "0001-01-01T00:00:00Z",
"clientVersion": "",
"connected": false,
"inBytesTotal": 0,
"isLocal": false,
"outBytesTotal": 0,
"paused": false,
"startedAt": "0001-01-01T00:00:00Z",
"type": ""
},
"YZJBJFX-RDBL7WY-6ZGKJ2D-4MJB4E7-ZATSDUY-LD6Y3L3-MLFUYWE-AEMXJAC": {
"address": "127.0.0.1:22002",
"at": "2015-11-07T17:29:47.691548971+01:00",
"clientVersion": "v0.12.1",
"connected": true,
"inBytesTotal": 556,
"isLocal": true,
"outBytesTotal": 550,
"paused": false,
"startedAt": "2015-11-07T00:09:47Z",
"type": "tcp-client"
}
},
"total": {
"at": "2015-11-07T17:29:47.691637262+01:00",
"inBytesTotal": 1479,
"outBytesTotal": 1318
}
}
GET /rest/system/discovery ๐งญ
Hands you back the contents of the local discovery cache. Cute, right? ๐ธ
{
"LGFPDIT7SKNNJVJZA4FC7QNCRKCE753K72BW5QD2FOZ7FRFEP57Q": [
"192.162.129.11:22000"
]
}
POST /rest/system/discovery โ REMOVED ๐
NOTE: removed in v0.12.0. Gone gone, bestie. ๐ญ
You'd post with the query parameters device and addr to add entries into the discovery cache. Here's what that used to look like for the historians:
curl -X POST http://127.0.0.1:8384/rest/system/discovery?device=LGFPDIT7SKNNJVJZA4FC7QNCRKCE753K72BW5QD2FOZ7FRFEP57Q\&addr=192.162.129.11:22000
# Or with the X-API-Key header:
curl -X POST --header "X-API-Key: TcE28kVPdtJ8COws1JdM0b2nodj77WeQ" http://127.0.0.1:8384/rest/system/discovery?device=LGFPDIT7SKNNJVJZA4FC7QNCRKCE753K72BW5QD2FOZ7FRFEP57Q\&addr=192.162.129.11:22000
POST /rest/system/error/clear ๐งน
Post with an empty body to yeet all the recent errors. Clean slate energy. โจ
GET /rest/system/error ๐ญ
NOTE: the return format changed in 0.12.0.
Returns the list of recent errors. Look at them. Look at what you did. ๐
{
"errors": [
{
"when": "2014-09-18T12:59:26.549953186+02:00",
"message": "This is an error string"
}
]
}
POST /rest/system/error ๐ฎ
Post with an error message in the body (plain text, no JSON dress-up) to register a new error. That new error gets shown on any active GUI clients โ so yeah, everyone's gonna see it. ๐
GET /rest/system/log ๐
Added in version 0.12.0.
Returns the list of recent log entries. The optional since parameter limits the results to messages newer than the given timestamp in RFC 3339 format. No cap.
{
"messages": [
{
"when": "2014-09-18T12:59:26.549953186+02:00",
"message": "This is a log entry"
}
]
}
GET /rest/system/log.txt ๐
Same info as above, but formatted as a text log instead of a JSON object. For the plain-text girlies. ๐
GET /rest/system/loglevels ๐๏ธ
Added in version 2.0.0.
Returns the set of log facilities and their current log level. A whole vibe check for your logging. โญ
{
"levels": {
"api": "INFO",
"beacon": "INFO",
...
"versioner": "INFO",
"watchaggregator": "INFO"
},
"packages": {
"api": "REST API",
"beacon": "Multicast and broadcast discovery",
...
"versioner": "File versioning",
"watchaggregator": "Filesystem event watcher"
}
}
POST /rest/system/loglevels ๐ ๏ธ
Added in version 2.0.0.
Changes the log level for the facilities you specify. Post an object with the log facilities as keys and the desired log level as the values โ the levels are DEBUG, INFO, WARN, or ERROR. Go off. ๐ฅ
$ curl -H X-API-Key:abc123 -d '{"beacon":"DEBUG","discovery":"WARN"}' 'http://localhost:8384/rest/system/loglevels'
GET /rest/system/paths ๐๏ธ
Added in version 1.21.0.
Returns the path locations that get used internally for storing configuration, database, and other stuff. It's giving "peek behind the curtain". ๐ฆ
{
"auditLog": "/home/user/.local/share/syncthing/audit-${timestamp}.log",
"baseDir-config": "/home/user/.config/syncthing",
"baseDir-data": "/home/user/.local/share/syncthing",
"baseDir-userHome": "/home/user",
"certFile": "/home/user/.config/syncthing/cert.pem",
"config": "/home/user/.config/syncthing/config.xml",
"csrfTokens": "/home/user/.config/syncthing/csrftokens.txt",
"database": "/home/user/.local/share/syncthing/index-v2",
"defFolder": "/home/user/Sync",
"guiAssets": "/home/user/src/syncthing/gui",
"httpsCertFile": "/home/user/.config/syncthing/https-cert.pem",
"httpsKeyFile": "/home/user/.config/syncthing/https-key.pem",
"keyFile": "/home/user/.config/syncthing/key.pem",
"logFile": "-",
"panicLog": "/home/user/.local/share/syncthing/panic-${timestamp}.log"
}
POST /rest/system/pause โธ๏ธ
Pauses the device you name, or all the devices.
It takes the optional parameter device (that's a device ID). When you omit it, it pauses all the devices. Returns status 200 and no content on success, or status 500 and a plain text error if it fails. ๐
GET /rest/system/ping ๐
Returns a {"ping": "pong"} object. That's it. That's the whole endpoint. Iconic, honestly.
{
"ping": "pong"
}
POST /rest/system/ping ๐
Returns a {"ping": "pong"} object. Yes, the POST version does the same thing. Yes, it's still iconic. ๐
POST /rest/system/reset ๐งจ
Post with an empty body to erase the current index database and restart Syncthing. With no query parameters, the entire database gets erased from disk. Like, gone.
If you specify the folder parameter with a valid folder ID, only the information for that folder gets erased:
curl -X POST -H "X-API-Key: abc123" http://localhost:8384/rest/system/reset?folder=ab1c2-def3g
It also creates .stfolder folders inside each sync folder, if they don't already exist. ๐
CAUTION: make sure all sync folders that are mountpoints are already mounted before you do this. If a mountpoint gets mounted later and it contains older versions, you can end up with inconsistent versions. Be careful, bestie โ this one bites. ๐ญ
POST /rest/system/restart ๐
Post with an empty body to immediately restart Syncthing. Right now. No thoughts, head empty.
POST /rest/system/resume โถ๏ธ
Resumes the device you name, or all the devices.
Takes the optional parameter device (device ID). When omitted, it resumes all the devices. Returns status 200 and no content on success, or status 500 and a plain text error on failure. Back online, slay. ๐
POST /rest/system/shutdown ๐
Post with an empty body to make Syncthing exit โ and it does not restart. It's really leaving. ๐
GET /rest/system/status ๐
Returns information about the current system status and resource usage. Heads up though: the CPU percent value has been deprecated from the API and will always report 0. So don't trust it, it's not even trying anymore. ๐
{
"alloc": 30618136,
"connectionServiceStatus": {
"dynamic+https://relays.syncthing.net/endpoint": {
"error": null,
"lanAddresses": [
"relay://23.92.71.120:443/?id=53STGR7-YBM6FCX-PAZ2RHM-YPY6OEJ-WYHVZO7-PCKQRCK-PZLTP7T-434XCAD&pingInterval=1m0s&networkTimeout=2m0s&sessionLimitBps=0&globalLimitBps=0&statusAddr=:22070&providedBy=canton7"
],
"wanAddresses": [
"relay://23.92.71.120:443/?id=53STGR7-YBM6FCX-PAZ2RHM-YPY6OEJ-WYHVZO7-PCKQRCK-PZLTP7T-434XCAD&pingInterval=1m0s&networkTimeout=2m0s&sessionLimitBps=0&globalLimitBps=0&statusAddr=:22070&providedBy=canton7"
]
},
"tcp://0.0.0.0:22000": {
"error": null,
"lanAddresses": [
"tcp://0.0.0.0:22000"
],
"wanAddresses": [
"tcp://0.0.0.0:22000"
]
}
},
"cpuPercent": 0,
"discoveryEnabled": true,
"discoveryErrors": {
"global@https://discovery-v4-1.syncthing.net/v2/": "500 Internal Server Error",
"global@https://discovery-v4-2.syncthing.net/v2/": "Post https://discovery-v4-2.syncthing.net/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)",
"global@https://discovery-v4-3.syncthing.net/v2/": "Post https://discovery-v4-3.syncthing.net/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)",
"global@https://discovery-v6-1.syncthing.net/v2/": "Post https://discovery-v6-1.syncthing.net/v2/: dial tcp [2001:470:28:4d6::5]:443: connect: no route to host",
"global@https://discovery-v6-2.syncthing.net/v2/": "Post https://discovery-v6-2.syncthing.net/v2/: dial tcp [2604:a880:800:10::182:a001]:443: connect: no route to host",
"global@https://discovery-v6-3.syncthing.net/v2/": "Post https://discovery-v6-3.syncthing.net/v2/: dial tcp [2400:6180:0:d0::d9:d001]:443: connect: no route to host"
},
"discoveryStatus": {
"IPv4 local": {
"error": null
},
"IPv6 local": {
"error": null
},
"global@https://discovery-v4-1.syncthing.net/v2/": {
"error": "500 Internal Server Error"
},
"global@https://discovery-v4-2.syncthing.net/v2/": {
"error": "Post https://discovery-v4-2.syncthing.net/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)"
},
"global@https://discovery-v4-3.syncthing.net/v2/": {
"error": "Post https://discovery-v4-3.syncthing.net/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)"
},
"global@https://discovery-v6-1.syncthing.net/v2/": {
"error": "Post https://discovery-v6-1.syncthing.net/v2/: dial tcp [2001:470:28:4d6::5]:443: connect: no route to host"
},
"global@https://discovery-v6-2.syncthing.net/v2/": {
"error": "Post https://discovery-v6-2.syncthing.net/v2/: dial tcp [2604:a880:800:10::182:a001]:443: connect: no route to host"
},
"global@https://discovery-v6-3.syncthing.net/v2/": {
"error": "Post https://discovery-v6-3.syncthing.net/v2/: dial tcp [2400:6180:0:d0::d9:d001]:443: connect: no route to host"
}
},
"discoveryMethods": 8,
"goroutines": 49,
"lastDialStatus": {
"tcp://10.20.30.40": {
"when": "2019-05-16T07:41:23Z",
"error": "dial tcp 10.20.30.40:22000: i/o timeout"
},
"tcp://172.16.33.3:22000": {
"when": "2019-05-16T07:40:43Z",
"ok": true
},
"tcp://83.233.120.221:22000": {
"when": "2019-05-16T07:41:13Z",
"error": "dial tcp 83.233.120.221:22000: connect: connection refused"
}
},
"myID": "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2",
"pathSeparator": "/",
"startTime": "2016-06-06T19:41:43.039284753+02:00",
"sys": 42092792,
"themes": [
"default",
"dark"
],
"tilde": "/Users/jb",
"uptime": 2635
}
A lil history moment for the girlies, because version notes matter and I refuse to skip them ๐
Added in version 1.2.0: the lastDialStatus dictionary contains the last error (or null for success) for each peer address that Syncthing has attempted to contact. The connectionServiceStatus entries gained "error": null attributes where previously there would be no error attribute at all in the success case. So like, in the success case, there's genuinely nothing there before โ now there's a null. Slay.
Added in version 1.18.0: the discoveryStatus dictionary lists all configured discovery methods, not only failed ones like the now deprecated discoveryErrors. Each entry is an object itself (for consistency with other fields), where a null value for the error attribute means the method is running successfully. This effectively deprecates the discoveryMethods attribute as well, which now always matches the number of entries in discoveryStatus. Obsessed with a field that justโฆ agrees with itself now. That's growth.
GET /rest/system/upgrade
Checks for a possible upgrade and returns an object describing the newest version and upgrade possibility. Vibe check on whether you're behind ๐
{
"latest": "v0.14.47",
"majorNewer": false,
"newer": true,
"running": "v0.14.46"
}
POST /rest/system/upgrade
Perform an upgrade to the newest released version and restart. Does nothing if there is no newer version than currently running. Like a refresh button that respects you enough to no-op when there's nothing new ๐ซถ
GET /rest/system/version
Returns the current Syncthing version information. Receipts for what you're running ๐
{
"arch": "amd64",
"longVersion": "syncthing v0.10.27+3-gea8c3de (go1.4 darwin-amd64 default) jb@syno 2015-03-16 11:01:29 UTC",
"os": "darwin",
"version": "v0.10.27+3-gea8c3de"
}
โ๏ธ CONFIG ENDPOINTS โ๏ธ
Config Endpoints
Added in version 1.12.0. โจ
Okay bestie, these endpoints let you access and modify the config in a granular way. Config sent to the endpoints must be in the same format as returned by the corresponding GET request. When posting the configuration succeeds, the posted configuration is immediately applied, except for changes that require a restart. Query /rest/config/restart-required to check if a restart is required. So like โ most stuff just goes live, but some things need the whole app to get up and stretch. Check first, don't guess ๐
For all endpoints supporting PATCH, it takes the existing config and unmarshals the given JSON object on top of it. This means all child objects will replace the existing objects, not extend them. For example for RawListenAddresses in options, which is an array of strings, sending {RawListenAddresses: ["tcp://10.0.0.2"]} will replace all existing listen addresses. Not me thinking it appends. It does not append. It REPLACES. Say it with me ๐ข
Real talk: PATCH replaces child objects wholesale. It is not a merge-into-arrays situation. Plan accordingly or your listen addresses are gonna have a bad time ๐ญ
/rest/config
GET returns the entire config and PUT replaces it. The whole entire thing, no crumbs โจ
/rest/config/restart-required
GET returns whether a restart of Syncthing is required for the current config to take effect. Simple yes/no energy, and honestly, we respect it.
/rest/config/folders, /rest/config/devices
GET returns all folders respectively devices as an array. PUT takes an array and POST a single object. In both cases if a given folder/device already exists, it's replaced, otherwise a new one is added. Existing? Swapped. New? Welcomed in. Very hospitable ๐
/rest/config/folders/*id*, /rest/config/devices/*id*
Put the desired folder- respectively device-ID in place of id. GET returns the folder/device for the given ID, PUT replaces the entire config, PATCH replaces only the given child objects and DELETE removes the folder/device. You got a whole little menu here: read it, overwrite it, patch a bit of it, or yeet it entirely ๐ธ
/rest/config/defaults/folder, /rest/config/defaults/device
GET returns a template folder / device configuration object with all default values, which only needs a unique ID to be applied. PUT replaces the default config (omitted values are reset to the hard-coded defaults), PATCH replaces only the given child objects. Lowkey the best way to start a folder/device โ grab the template, slap an ID on it, done ๐
/rest/config/defaults/ignores
Added in version 1.19.0. ๐
GET returns an object with a single lines attribute listing ignore patterns to be used by default on folders, as an array of single-line strings. PUT replaces the default ignore patterns from an object of the same format. One line per pattern, bestie. Not multi-line. Single. Line. ๐ฆ
/rest/config/options, /rest/config/ldap, /rest/config/gui
GET returns the respective object, PUT replaces the entire object and PATCH replaces only the given child objects. Three lil objects, three lil identical rules. Iconic consistency ๐
NOTE: The gui.password configuration option has special handling to accept already hashed passwords. Any valid bcrypt hash is stored verbatim, while a plaintext password is first hashed. So you can hand it a hash and it'll keep it exactly as-is โ but hand it plaintext and it does the hashing for you. Convenient! ๐ฅ
๐ CLUSTER ENDPOINTS ๐
Okay bestie, this whole section is about the mesh network structure โ like, who's in the group chat, who's trying to join, and who's sliding into your DMs with folders you didn't ask for. ๐
๐ซ DELETE /rest/cluster/pending/devices
Added in version 1.18.0.
This one yeets the record of a pending remote device that tried to connect. So like, they knocked on the door and you're deleting the ring-cam footage. ๐ชโจ
Valid values for the device parameter are those from the corresponding GET /rest/cluster/pending/devices endpoint โ so go look at that list first, don't just make up an ID, iykyk.
$ curl -X DELETE -H "X-API-Key: abc123" http://localhost:8384/rest/cluster/pending/devices?device=P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2
Returns status 200 and no content upon success, or status 500 and a plain text error on failure. A PendingDevicesChanged event will be generated in response. ๐
Heads up: if you want a more permanent effect, also for future connections from the same device ID, the device should be ignored in the configuration instead. Deleting the record is just closing the door; ignoring it in config is changing the locks. ๐
๐ซ GET /rest/cluster/pending/devices
Added in version 1.13.0.
Lists remote devices which have tried to connect, but are not yet configured in our instance. It's literally the "requests" folder of your Syncthing. ๐ซถ
{
"P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2": {
"time": "2020-03-18T11:43:07Z",
"name": "Friend Joe",
"address": "192.168.1.2:22000"
}
}
๐ซ DELETE /rest/cluster/pending/folders
Added in version 1.18.0.
Same vibe as the device one, but for folders instead. This removes records about a pending folder announced from a remote device. โจ
Valid values for the folder and device parameters are those from the corresponding GET /rest/cluster/pending/folders endpoint. The device parameter is optional and affects announcements of this folder from the given device, or from any device if omitted โ so leave it out and you're nuking it across the board, bestie. ๐
$ curl -X DELETE -H "X-API-Key: abc123" http://localhost:8384/rest/cluster/pending/folders?folder=cpkn4-57ysy&device=P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2
Returns status 200 and no content upon success, or status 500 and a plain text error on failure. A PendingFoldersChanged event will be generated in response. ๐
Again with the permanence: for a more permanent effect, also for future announcements of the same folder ID, the folder should be ignored in the configuration instead. ๐ง
๐ซ GET /rest/cluster/pending/folders
Added in version 1.13.0.
Lists folders which remote devices have offered to us, but are not yet shared from our instance to them. So they're holding up a folder like "want some?" and we haven't said yes yet. ๐
Takes the optional device parameter to only return folders offered by a specific remote device. Other offering devices are also omitted from the result โ it's giving "filter by bestie."
{
"cpkn4-57ysy": {
"offeredBy": {
"P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2": {
"time": "2020-03-18T11:43:07Z",
"label": "Joe's folder",
"receiveEncrypted": true,
"remoteEncrypted": false
},
"DOVII4U-SQEEESM-VZ2CVTC-CJM4YN5-QNV7DCU-5U3ASRL-YVFG6TH-W5DV5AA": {
"time": "2020-03-01T10:12:13Z",
"label": "Jane's and Joe's folder",
"receiveEncrypted": false,
"remoteEncrypted": false
}
}
},
"abcde-fghij": {
"offeredBy": {
"P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2": {
"time": "2020-03-18T11:43:07Z",
"label": "MyPics",
"receiveEncrypted": false,
"remoteEncrypted": false
}
}
}
}
๐ฆ FOLDER ENDPOINTS ๐ฆ
Runtime state of the individual shared folders. Like, how are the folders doing today? ๐
๐ธ GET /rest/folder/errors
Added in version 0.14.53.
Takes one mandatory parameter, folder, and returns the list of errors encountered during scanning or pulling. It's the "what went wrong" receipt, and ngl sometimes it's a lot. ๐ญ
The results can be paginated using the common pagination parameters โ yes, the whole page/perpage situation from earlier, slay.
{
"folder": "nnhic-sxuae",
"errors": [
{
"path": "noperm.txt",
"error": "hashing: open /path/to/folder/noperm.txt: permission denied"
}
],
"page": 1,
"perpage": 100
}
๐ธ GET /rest/folder/pullerrors (DEPRECATED)
โ ๏ธ Deprecated since version v0.14.53: This endpoint still works as before but is deprecated. Use GET /rest/folder/errors instead, which returns the same information. It's giving "we keep the old one around for the vibes but pls move on." ๐
See โ https://github.com/syncthing/syncthing/commit/d510e3cca3d5caae42121fa206b3decc981ae59e
๐ธ GET /rest/folder/versions
Added in version 0.14.44.
Takes one mandatory parameter, folder, and returns the list of archived files that could be recovered. Like a little time capsule for your files. โณโจ
How many versions are available depends on the File Versioning configuration โ so your mileage is entirely config-dependent, no cap. Each entry specifies when the file version was archived as the versionTime, the modTime when it was last modified before being archived, and the size in bytes. ๐ฅ
{
"dir1/dir2/bar": [
{
"versionTime": "2022-02-06T20:44:12+01:00",
"modTime": "2021-01-14T13:21:22+01:00",
"size": 4
}
],
"baz": [
{
"versionTime": "2022-02-06T20:44:20+01:00",
"modTime": "2021-01-14T13:23:49+01:00",
"size": 4
}
],
"foo": [
{
"versionTime": "2022-02-06T20:55:31+01:00",
"modTime": "2022-02-06T20:44:13+01:00",
"size": 4
},
{
"versionTime": "2022-02-06T20:44:20+01:00",
"modTime": "2021-01-14T13:21:16+01:00",
"size": 4
}
]
}
๐ธ POST /rest/folder/versions
Restore archived versions of a given set of files. This is the "undo" button, bestie, and it ate. ๐
Expects an object with attributes named after the relative file paths, with timestamps as values matching valid versionTime entries in the corresponding GET /rest/folder/versions response object. So like, you literally copy the timestamp from the GET and hand it back. Iconic.
Takes the mandatory parameter folder (folder ID). Returns an object containing any error messages that occurred during restoration of the file, with the file path as attribute name. ๐ซถ
curl -X POST -H X-API-key:... http://127.0.0.1:8384/rest/folder/versions?folder=default -d '{
"dir1/dir2/bar": "2022-02-06T20:44:12+01:00",
"baz": "2022-02-06T20:44:20+01:00"
}'
๐ DATABASE ENDPOINTS ๐
โญ GET /rest/db/browse
Returns the directory tree of the global model. Directories are always JSON objects (map/dictionary), and files are always arrays of modification time and size. The first integer is the files modification time, and the second integer is the file size. ๐๏ธ
The call takes one mandatory folder parameter and two optional parameters. Optional parameter levels defines how deep within the tree we want to dwell down (0 based, defaults to unlimited depth). Optional parameter prefix defines a prefix within the tree where to start building the structure. So you can be like "just the top two levels of DCIM pls." โจ
$ curl -s http://localhost:8384/rest/db/browse?folder=j663y-3ct3e&prefix=DCIM&levels=2
[
{
"modTime" : "2020-10-02T23:48:52.076996974+02:00",
"name" : "100ANDRO",
"size" : 128,
"type" : "FILE_INFO_TYPE_DIRECTORY"
},
{
"children" : [
{
"modTime" : "2020-12-16T23:31:34.5009668+01:00",
"name" : "IMG_20201114_124821.jpg",
"size" : 10682189,
"type" : "FILE_INFO_TYPE_FILE"
},
{
"modTime" : "2020-12-16T23:31:35.0106367+01:00",
"name" : "IMG_20201213_122451.jpg",
"size" : 7936351,
"type" : "FILE_INFO_TYPE_FILE"
},
{
"modTime" : "2020-12-13T12:25:05.017097469+01:00",
"name" : "IMG_20201213_122504.jpg",
"size" : 8406507,
"type" : "FILE_INFO_TYPE_FILE"
},
{
"modTime" : "2020-12-13T12:25:06.127097469+01:00",
"name" : "IMG_20201213_122505.jpg",
"size" : 8381931,
"type" : "FILE_INFO_TYPE_FILE"
},
{
"modTime" : "2020-12-13T12:53:29.707298401+01:00",
"name" : "IMG_20201213_125329.jpg",
"size" : 4388331,
"type" : "FILE_INFO_TYPE_FILE"
},
],
"modTime" : "2020-10-09T13:04:42.4410738+02:00",
"name" : "Camera",
"size" : 128,
"type" : "FILE_INFO_TYPE_DIRECTORY"
},
]
โ ๏ธ NOTE: This is an expensive call, increasing CPU and RAM usage on the device. Use sparingly. Fr fr, don't be spamming this one โ you will absolutely cook your little box. ๐ฅ๐
โญ GET /rest/db/completion
Returns the completion percentage (0 to 100) and byte / item counts. It's the progress bar of your dreams. ๐๐
Takes optional device and folder parameters:
folderspecifies the folder ID to calculate completion for. An empty or absentfolderparameter means all folders as an aggregate.devicespecifies the device ID to calculate completion for. An empty or absentdeviceparameter means the local device.
If a device is specified but no folder, completion is calculated for all folders shared with that device. Mix and match, it's cute like that. ๐
๐ Example Queries
Completion status for folder abcd-1234 on device I6KAH76-...-3PSROAU:
/rest/db/completion?folder=abcd-1234&device=I6KAH76-...-3PSROAU
Aggregated completion status for device I6KAH76-...-3PSROAU (all folders shared with them):
/rest/db/completion?device=I6KAH76-...-3PSROAU
Completion status for folder abcd-1234 on the local device:
/rest/db/completion?folder=abcd-1234
Aggregated completion status for all folders on the local device:
/rest/db/completion
๐ Example Response
{
"completion": 99.9937565835,
"globalBytes": 156793013575,
"needBytes": 9789241,
"globalItems": 7823,
"needItems": 412,
"needDeletes": 0,
"remoteState": "valid",
"sequence": 12
}
Added in version 1.8.0: The ability to aggregate multiple folders by leaving out the folder ID. Querying data for the local device by leaving out the device ID. Returning the globalItems counter in the response. โจ
Added in version 1.20.0: Indication whether the remote device has accepted the folder (shares it with us) as well, and whether it is paused. The remoteState field is meaningless for aggregated responses, unknown when the remote device is not connected. Otherwise it can be either paused, notSharing, or valid if the remote is sharing back. ๐ซถ
โญ GET /rest/db/file
Returns most data about a given file, including version and availability. It's giving "tell me everything about this one file." ๐
๐ Request
The call requires parameters for folder and file in the query string:
folderis the folder ID which you can find in the Syncthing Web GUI, e.g.5camp-slpa8.fileโ the relative path of the file, starting from the folder root, pointing at the file you actually care about ๐ฏ. Bestie, the path and the filename must be correctly URL-encoded, no cap.
โจ Example Request โจ
$ curl -s -H X-API-KEY:... "http://localhost:8384/rest/db/file?folder=YOUR_FOLDER_ID" --url-query "file=path/to/file.pdf"
๐ Response ๐
{
"availability": [
{
"id": "ITZRNXE-YNROGBZ-HXTH5P7-VK5NYE5-QHRQGE2-7JQ6VNJ-KZUEDIU-5PPR5AM",
"fromTemporary": false
}
],
"global": { /* a file entry */ },
"local": { /* a file entry */ },
"mtime": {
"err": null,
"value": {
"real": "0001-01-01T00:00:00Z",
"virtual": "0001-01-01T00:00:00Z"
}
}
So local and global are the current file on the local device and the globally newest file, respectively โ like, respectively, okay, keep them straight ๐.
A file entry looks like this ๐
{
"deleted": false,
"ignored": false,
"inodeChange": "1970-01-01T01:00:00+01:00",
"invalid": false,
"localFlags": 0,
"modified": "2022-09-28T08:07:19.979723+02:00",
"modifiedBy": "523ITIE",
"mustRescan": false,
"name": "img",
"noPermissions": false,
"numBlocks": 0,
"permissions": "0755",
"platform": { /* platform specific data */ },
"sequence": 914,
"size": 128,
"type": "FILE_INFO_TYPE_DIRECTORY",
"version": [
"523ITIE:1664345275"
]
}
Platform specific data? Could be ownership, extended attributes, etc., and it's split into entries per operating system / platform ๐. Here's a platform entry with ownership info for Unix systems plus an extended attribute for macOS (that's "darwin", iykyk) ๐:
{
"darwin": {
"xattrs": [
{
"name": "net.kastelo.xattrtest",
"value": "aGVsbG8="
}
]
},
"freebsd": null,
"linux": null,
"netbsd": null,
"unix": {
"gid": 20,
"groupName": "staff",
"ownerName": "jb",
"uid": 501
},
"windows": null
}
๐ธ GET /rest/db/ignores ๐ธ
Takes one parameter, folder, and returns the content of the .stignore as the ignore field ๐
. There's a second field, expanded, which gives you a list of strings representing globbing patterns described by gobwas/glob (based on standard wildcards) that match the patterns in .stignore and all the includes โจ. If appropriate, these globs get prepended by the following modifiers: ! to negate the glob, (?i) to do case insensitive matching, and (?d) to enable removing of ignored files in an otherwise empty directory ๐ซถ.
{
"ignore": [
"(?i)/Backups"
],
"expanded": [
"(?i)Backups",
"(?i)Backups/**"
]
}
๐ฆ POST /rest/db/ignores ๐ฆ
Expects a format similar to the output of the GET /rest/db/ignores call, but only containing the ignore field (expanded field should be omitted, bestie ๐). It takes one parameter, folder, and either updates the content of the .stignore, echoing it back as a response, or returns an error ๐ญ.
๐ GET /rest/db/localchanged ๐
Added in version 0.14.55.
Takes one mandatory parameter, folder, and returns the list of files which were changed locally in a receive-only folder ๐. So like, they differ from the global state and could be reverted by pulling from remote devices again โ see POST /rest/db/revert ๐.
The results can be paginated using the common pagination parameters โจ.
{
"files": [
{
"flags": "0755",
"sequence": 6,
"modified": "2015-04-20T23:06:12+09:00",
"name": "ls",
"size": 34640,
"version": [
"5157751870738175669:1"
]
}
],
"page": 1,
"perpage": 100
}
NOTE: This is an expensive call, increasing CPU and RAM usage on the device. Use sparingly ๐.
๐ GET /rest/db/need ๐
Takes one mandatory parameter, folder, and returns lists of files which are needed by this device in order for it to become in sync ๐ซถ.
The results can be paginated using the common pagination parameters โจ. Pagination happens across the union of all needed files โ that is, across all 3 sections of the response ๐. For example, given the current need state is as follows:
progresshas 15 itemsqueuedhas 3 itemsresthas 12 items
If you issue a query with page=1 and perpage=10, only the progress section in the response will have 10 items. If you issue a request query with page=2 and perpage=10, the progress section will have the last 5 items, the queued section will have all 3 items, and the rest section will have the first 2 items ๐ฅ. If you issue a query for page=3 and perpage=10, you will only have the last 10 items of the rest section ๐.
NOTE: Return format changed in version 0.14.43, removing the total count attribute ๐.
{
# Files currently being downloaded
"progress": [
{
"flags": "0755",
"sequence": 6,
"modified": "2015-04-20T23:06:12+09:00",
"name": "ls",
"size": 34640,
"version": [
"5157751870738175669:1"
]
}
],
# Files queued to be downloaded next (as per array order)
"queued": [
...
],
# Files to be downloaded after all queued files will be downloaded.
# This happens when we start downloading files, and new files get added while we are downloading.
"rest": [
...
],
"page": 1,
"perpage": 100
}
NOTE: This is an expensive call, increasing CPU and RAM usage on the device. Use sparingly ๐.
๐ฅ POST /rest/db/override ๐ฅ
Request an override of a send-only folder ๐ซก. Override means make the local version latest, overriding changes made on other devices โ main character energy, honestly ๐. This API call does nothing if the folder is not a send-only folder, fr fr.
Takes the mandatory parameter folder (folder ID).
curl -X POST -H X-API-key:... http://127.0.0.1:8384/rest/db/override?folder=default
โญ POST /rest/db/prio โญ
Moves the file to the top of the download queue ๐ โ jump the line, slay.
curl -X POST http://127.0.0.1:8384/rest/db/prio?folder=default&file=foo/bar
Response contains the same output as GET /rest/db/need โจ.
๐ GET /rest/db/remoteneed ๐
Added in version 0.14.43.
Takes the mandatory parameters folder and device, and returns the list of files which are needed by that remote device in order for it to become in sync with the shared folder ๐ฆ.
The results can be paginated using the common pagination parameters โจ.
{
"files": [
{
"flags": "0755",
"sequence": 6,
"modified": "2015-04-20T23:06:12+09:00",
"name": "ls",
"size": 34640,
"version": [
"5157751870738175669:1"
]
}
],
"page": 1,
"perpage": 100
}
NOTE: This is an expensive call, increasing CPU and RAM usage on the device. Use sparingly ๐.
๐ POST /rest/db/revert ๐
Added in version 0.14.50.
Request revert of a receive-only folder ๐. Reverting a folder means undoing all local changes โ like, undo-undo, bestie ๐ญ. This API call does nothing if the folder is not a receive-only folder.
Takes the mandatory parameter folder (folder ID).
curl -X POST -H X-API-Key:... http://127.0.0.1:8384/rest/db/revert?folder=default
๐ง POST /rest/db/scan ๐ง
Request immediate scan ๐. Takes the optional parameters folder (folder ID), sub (path relative to the folder root) and next (time in seconds). If folder is omitted or empty, all folders are scanned ๐. If sub is given, only this path (and children, in case it's a directory) is scanned ๐ฏ. The next argument delays Syncthing's automated rescan interval for a given amount of seconds โณ.
Requesting a scan of a path that no longer exists, but previously did, is valid and will result in Syncthing noticing the deletion of the path in question ๐โจ.
Returns status 200 and no content upon success, or status 500 and a plain text error if an error occurred during scanning ๐.
curl -X POST http://127.0.0.1:8384/rest/db/scan?folder=default&sub=foo/bar
๐ GET /rest/db/status ๐
Returns information about the current status of a folder ๐ซถ.
Parameters: folder, the ID of a folder.
{
"globalBytes": 0,
"globalDeleted": 0,
"globalDirectories": 0,
"globalFiles": 0,
"globalSymlinks": 0,
"globalTotalItems": 0,
"ignorePatterns": false,
"inSyncBytes": 0,
"inSyncFiles": 0,
"invalid": "",
"localBytes": 0,
"localDeleted": 0,
"localDirectories": 0,
"localFiles": 0,
"localSymlinks": 0,
"localTotalItems": 0,
"needBytes": 0,
"needDeletes": 0,
"needDirectories": 0,
"needFiles": 0,
"needSymlinks": 0,
"needTotalItems": 0,
"pullErrors": 0,
"receiveOnlyChangedBytes": 0,
"receiveOnlyChangedDeletes": 0,
"receiveOnlyChangedDirectories": 0,
"receiveOnlyChangedFiles": 0,
"receiveOnlyChangedSymlinks": 0,
"receiveOnlyTotalItems": 0,
"sequence": 0,
"state": "idle",
"stateChanged": "2018-08-08T07:04:57.301064781+02:00",
"version": 0
}
The various fields have the following meaning ๐
global*โ data in the cluster latest version ๐inSync*โ data that is locally the same as the cluster latest version โlocal*โ data that is locally present, regardless of whether it's the same or a different version as the cluster latest version ๐need*โ data that is needed to become up to date with the cluster latest version (i.e., data that is out of sync) ๐ญreceiveOnlyChanged*โ data that has changed locally in a receive-only folder, and thus has not been sent to the cluster ๐invalidโ deprecated, always empty ๐pullErrorsโ the number of files that failed to sync during the last sync operations ๐ฅsequenceโ the current folder sequence number ๐ขstateโ the current folder state ๐stateChangedโ when the folder state last changed โฐversionโ deprecated, equivalent to the sequence number ๐
NOTE: This is an expensive call, increasing CPU and RAM usage on the device. Use sparingly ๐.
๐ EVENT ENDPOINTS ๐
GET /rest/events
okay so if you want in on the event stream, bestie, you literally just do a HTTP GET of /rest/events. that's it. main character endpoint behavior. ๐
to filter the event list โ like, making a specific subscription for only the event types you actually care about โ you add a parameter events=EventTypeA,EventTypeB,... where the event types can be any of the Event Types. if you don't specify a filter, you get all events except LocalChangeDetected and RemoteChangeDetected. those two are gatekept by default, no cap.
the optional parameter since=<lastSeenID> sets the ID of the last event you've already seen. Syncthing hands back a JSON encoded array of event objects, starting at the event just after the one with this last seen ID. default is 0, which just gives you all the events, the whole timeline, the full lore. โจ
โ ๏ธ there's a limit to how many events get buffered, so if the rate of events is high or the time between your polling calls is long, some events might be missed. you can spot this by noticing a discontinuity in the event IDs. keep an eye out, bestie ๐
if no new events have been produced since <lastSeenID>, the HTTP call blocks and waits for new events to happen before returning. (iconic commitment honestly.) and if <lastSeenID> is a future ID, the HTTP call blocks until such ID is reached or it timeouts. by default it times out after 60 seconds, returning an empty array. you can customize the time out duration with the optional parameter timeout=<seconds>.
want only a limited number of events? add limit=<n> with a suitable value for n and only the last n events will be returned. super handy for catching up with the latest event ID after a disconnection, for example:
/rest/events?since=0&limit=1
GET /rest/events/disk
this convenience endpoint gives you the same event stream, but pre-filtered to show only LocalChangeDetected and RemoteChangeDetected event types. the events parameter is not used here. it's giving "we already did the filtering for you, you're welcome". ๐ซถ
๐ธ STATISTICS ENDPOINTS ๐ธ
GET /rest/stats/device
returns general statistics about devices. currently it only contains the time the device was last seen and the last connection duration. short and sweet.
$ curl -s http://localhost:8384/rest/stats/device | json
{
"P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2": {
"lastSeen" : "2015-04-18T11:21:31.3256277+01:00"
"lastConnectionDurationS": 556335.421708141
}
}
GET /rest/stats/folder
returns general statistics about folders. currently contains the last scan time and the last synced file. ๐๏ธ
{
"folderid": {
"lastScan": "2016-06-02T13:28:01.288181412-04:00",
"lastFile": {
"filename": "file/name",
"at": "2015-04-16T22:04:18.3066971+01:00"
}
}
}
๐ฆ MISC SERVICES ENDPOINTS ๐ฆ
GET /rest/svc/deviceid
verifies and formats a device ID. it accepts all currently valid formats (52 or 56 characters, with or without separators, upper or lower case, with trivial substitutions โ so go ahead and be messy, it'll handle it ๐
). takes one parameter, id, and returns either a valid device ID in modern format, or an error.
$ curl -s http://localhost:8384/rest/svc/deviceid?id=1234 | json
{
"error": "device ID invalid: incorrect length"
}
$ curl -s http://localhost:8384/rest/svc/deviceid?id=p56ioi7m--zjnu2iq-gdr-eydm-2mgtmgl3bxnpq6w5btbbz4tjxzwicq | json
{
"id": "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2"
}
GET /rest/svc/lang
returns a list of canonicalized localization codes, as picked up from the Accept-Language header sent by the browser. multilingual queen behavior. ๐
["sv_sv","sv","en_us","en"]
GET /rest/svc/random/string
returns a strong random generated string (alphanumeric) of the specified length. takes the length parameter. ๐ฅ
{
"random": "FdPaEaZQ56sXEKYNxpgF"
}
GET /rest/svc/report
returns the data sent in the anonymous usage report. the whole receipts, fr fr:
{
"folderMaxMiB" : 0,
"platform" : "linux-amd64",
"totMiB" : 0,
"longVersion" : "syncthing v0.12.2 \"Beryllium Bedbug\" (go1.4.3 linux-amd64 default) unknown-user@build2.syncthing.net 2015-11-09 13:23:26 UTC",
"upgradeAllowedManual" : true,
"totFiles" : 3,
"folderUses" : {
"ignorePerms" : 0,
"autoNormalize" : 0,
"sendonly" : 0,
"ignoreDelete" : 0
},
"memoryUsageMiB" : 13,
"version" : "v0.12.2",
"sha256Perf" : 27.28,
"numFolders" : 2,
"memorySize" : 1992,
"announce" : {
"defaultServersIP" : 0,
"otherServers" : 0,
"globalEnabled" : false,
"defaultServersDNS" : 1,
"localEnabled" : false
},
"usesRateLimit" : false,
"numCPU" : 2,
"uniqueID" : "",
"urVersion" : 2,
"rescanIntvs" : [
60,
60
],
"numDevices" : 2,
"folderMaxFiles" : 3,
"relays" : {
"defaultServers" : 1,
"enabled" : true,
"otherServers" : 0
},
"deviceUses" : {
"compressMetadata" : 1,
"customCertName" : 0,
"staticAddr" : 1,
"compressAlways" : 0,
"compressNever" : 1,
"introducer" : 0,
"dynamicAddr" : 1
},
"upgradeAllowedAuto" : false
}
๐ DEBUG ENDPOINTS ๐
Debug Endpoints
GET /rest/debug/cpuprof
used to capture a profile of what Syncthing is doing on the CPU. see Profiling. ๐ง
GET /rest/debug/heapprof
used to capture a profile of what Syncthing is doing with the heap memory. see Profiling.
GET /rest/debug/support
collects information about the running instance for troubleshooting purposes. returns a "support bundle" as a zipped archive, which you should send to the developers after verifying it contains no sensitive personal information.
๐ good news though: credentials for the web GUI and the API key are automatically redacted already. so that part's handled.
GET /rest/debug/file
shows diagnostics about a certain file in a shared folder. takes the folder (folder ID) and file (folder relative path) parameters.
$ curl -H X-API-Key:... "http://localhost:8384/rest/debug/file?folder=default&file=foo/bar"
the returned object contains the same info as GET /rest/db/file, plus a summary of globalVersions. ๐
๐ NOAUTH ENDPOINTS ๐
calls that do not require authentication. yes bestie, the no-login-needed zone.
GET /rest/noauth/health
returns a {"status": "OK"} object. that's the whole vibe check. โ
{
"status": "OK"
}
๐ AUTHOR ๐
The Syncthing Authors. legends, honestly.
๐ COPYRIGHT ๐
2014-2026, The Syncthing Authors
and that's the whole page, bestie โ you made it to the end! ๐ this was v2.1.0, dated Oct 07, 2026, and it's the SYNCTHING-REST-API(7) manual page. go forth and hit those endpoints, you absolute icon. ๐
โจ
๐ฆ where this page comes from
- arch rolling: syncthing 2.1.7-1 ยท MPL-2.0
- nixos 26.05: syncthing 2.1.7
- nixpkgs unstable: syncthing 2.1.7
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms