API reference
Backups
Make, download, restore and lock backups of your server's files.
← API docs7 endpointsUpdated
Browse the docs
Backups
Make, download, restore, lock and delete backups of a server's files. A server may
keep as many backups as its backup limit (feature_limits.backups on the server)
allows; failed backups do not count towards it. A server with a limit of 0 cannot
make backups.
A backup is in progress while completed_at is null. Once it finishes,
is_successful says whether it worked. Watch the server's websocket or poll
getBackup to see when it is done.
While a server is installing, suspended, being transferred, restoring a backup or being deleted, every endpoint here answers 409.
List backups
GET/servers/{server}/backups
Permission backup.read
Returns the server's backups in the order they were made, including ones still in progress and ones that failed. meta.backup_count is how many count towards the backup limit, that is every backup that has not failed.
Parameters
| Name | Type | Description |
|---|---|---|
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |
| page | integer | The page of results to return, starting at 1. |
| per_page | integer | How many backups to return on each page. Values above 50 are treated as 50. |
Example
curl "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/backups" \ -H "Authorization: Bearer $SILLYDEV_API_KEY" \ -H "Accept: application/json"
Response 200A page of backups.
{
"object": "list",
"data": [
{
"object": "backup",
"attributes": {
"uuid": "0f2a6c1e-8d3b-4f7a-9c5e-2b1d4e6f8a90",
"is_successful": true,
"is_locked": false,
"name": "Before discord.py upgrade",
"ignored_files": [
"node_modules",
"*.log"
],
"checksum": "sha1:3f786850e387550fdab836ed7e6dc881de23001b",
"bytes": 5242880,
"created_at": "2026-10-10T12:00:00+00:00",
"completed_at": "2026-10-10T12:00:41+00:00"
}
}
],
"meta": {
"pagination": {
"total": 1,
"count": 1,
"per_page": 20,
"current_page": 1,
"total_pages": 1,
"links": []
},
"backup_count": 1
}
}Every field in the response
| Name | Type | Description |
|---|---|---|
| objectrequired | string | |
| datarequired | array of object | |
| data[].objectrequired | string | |
| data[].attributesrequired | object | |
| data[].attributes.uuidrequired | string (uuid) | The backup's ID, used in the URLs of the other backup endpoints. |
| data[].attributes.is_successfulrequired | boolean | True once the backup has finished successfully. False while it is in progress (completed_at is null) and when it failed. |
| data[].attributes.is_lockedrequired | boolean | A locked backup cannot be deleted. |
| data[].attributes.namerequired | string | |
| data[].attributes.ignored_filesrequired | array of string | The ignore patterns given when the backup was made, one per item. |
| data[].attributes.checksumrequired | string | null | The archive's checksum as <algorithm>:<hex>, e.g. sha1:.... Null until it succeeds. |
| data[].attributes.bytesrequired | integer | The archive's size in bytes. 0 until it completes. |
| data[].attributes.created_atrequired | string (date-time) | |
| data[].attributes.completed_atrequired | string (date-time) | null | When the backup finished, successfully or not. Null while in progress. |
| metarequired | object | |
| meta.paginationrequired | object | |
| meta.pagination.total | integer | |
| meta.pagination.count | integer | |
| meta.pagination.per_page | integer | |
| meta.pagination.current_page | integer | |
| meta.pagination.total_pages | integer | |
| meta.pagination.links | object | |
| meta.pagination.links.previous | string (uri) | |
| meta.pagination.links.next | string (uri) | |
| meta.backup_countrequired | integer | How many of the server's backups count towards its limit: those in progress or successful, on every page. |
Errors
- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
Start a backup
POST/servers/{server}/backups
Permission backup.createLimited to 2 backups per 10 minutes per server
Starts a backup of the server's files and returns it at once, still in progress
(completed_at is null). The server keeps running while it is backed up.
Answers 400 when the server has reached its backup limit (delete an old backup
first) and 429 when it has already started 2 backups in the last 10 minutes; deleted
backups still count towards that. is_locked is only applied if you also have the
backup.delete permission, and is ignored otherwise.
Parameters
| Name | Type | Description |
|---|---|---|
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |
Body
| Name | Type | Description |
|---|---|---|
| name | string | null | A name for the backup. Defaults to Backup at <date and time>. |
| is_locked | boolean | null | Lock the backup so it cannot be deleted until it is unlocked. Needs the backup.delete permission. |
| ignored | string | null | Files and folders to leave out, one pattern per line in .gitignore syntax. At most 32 KiB, 256 patterns and 16 * wildcards per pattern. When empty, the node uses the .backupignore file in the server's root, if there is one. |
Example
curl -X POST "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/backups" \
-H "Authorization: Bearer $SILLYDEV_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"name":"Before discord.py upgrade","is_locked":false,"ignored":"node_modules\n*.log"}'Response 200The backup, now in progress.
{
"object": "backup",
"attributes": {
"uuid": "0f2a6c1e-8d3b-4f7a-9c5e-2b1d4e6f8a90",
"is_successful": false,
"is_locked": false,
"name": "Before discord.py upgrade",
"ignored_files": [
"node_modules",
"*.log"
],
"checksum": null,
"bytes": 0,
"created_at": "2026-10-10T12:00:00+00:00",
"completed_at": null
}
}Every field in the response
| Name | Type | Description |
|---|---|---|
| objectrequired | string | |
| attributesrequired | object | |
| attributes.uuidrequired | string (uuid) | The backup's ID, used in the URLs of the other backup endpoints. |
| attributes.is_successfulrequired | boolean | True once the backup has finished successfully. False while it is in progress (completed_at is null) and when it failed. |
| attributes.is_lockedrequired | boolean | A locked backup cannot be deleted. |
| attributes.namerequired | string | |
| attributes.ignored_filesrequired | array of string | The ignore patterns given when the backup was made, one per item. |
| attributes.checksumrequired | string | null | The archive's checksum as <algorithm>:<hex>, e.g. sha1:.... Null until it succeeds. |
| attributes.bytesrequired | integer | The archive's size in bytes. 0 until it completes. |
| attributes.created_atrequired | string (date-time) | |
| attributes.completed_atrequired | string (date-time) | null | When the backup finished, successfully or not. Null while in progress. |
Errors
- 400 The server has reached its backup limit, or the node refused the ignore list.
- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 422 Some fields were missing or invalid. There is one error per problem, and
meta.source_fieldnames the field. - 429 Too many requests. Wait for the number of seconds in the
Retry-Afterheader, then try again.
Get a backup
GET/servers/{server}/backups/{backup}
Permission backup.read
Returns one backup. Poll this to see when a backup in progress finishes: completed_at is set and is_successful says whether it worked.
Parameters
| Name | Type | Description |
|---|---|---|
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |
| backuprequired | string (uuid) | The backup's UUID, as uuid in the backup's attributes. |
Example
curl "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/backups/{backup}" \
-H "Authorization: Bearer $SILLYDEV_API_KEY" \
-H "Accept: application/json"Response 200The backup.
{
"object": "backup",
"attributes": {
"uuid": "0f2a6c1e-8d3b-4f7a-9c5e-2b1d4e6f8a90",
"is_successful": true,
"is_locked": true,
"name": "Before discord.py upgrade",
"ignored_files": [
"node_modules",
"*.log"
],
"checksum": "sha1:3f786850e387550fdab836ed7e6dc881de23001b",
"bytes": 5242880,
"created_at": "2026-10-10T12:00:00+00:00",
"completed_at": "2026-10-10T12:00:41+00:00"
}
}Every field in the response
| Name | Type | Description |
|---|---|---|
| objectrequired | string | |
| attributesrequired | object | |
| attributes.uuidrequired | string (uuid) | The backup's ID, used in the URLs of the other backup endpoints. |
| attributes.is_successfulrequired | boolean | True once the backup has finished successfully. False while it is in progress (completed_at is null) and when it failed. |
| attributes.is_lockedrequired | boolean | A locked backup cannot be deleted. |
| attributes.namerequired | string | |
| attributes.ignored_filesrequired | array of string | The ignore patterns given when the backup was made, one per item. |
| attributes.checksumrequired | string | null | The archive's checksum as <algorithm>:<hex>, e.g. sha1:.... Null until it succeeds. |
| attributes.bytesrequired | integer | The archive's size in bytes. 0 until it completes. |
| attributes.created_atrequired | string (date-time) | |
| attributes.completed_atrequired | string (date-time) | null | When the backup finished, successfully or not. Null while in progress. |
Errors
- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
Delete a backup
DELETE/servers/{server}/backups/{backup}
Permission backup.delete
Deletes a backup and its archive. This cannot be undone. A locked backup that completed successfully cannot be deleted (400) until it is unlocked with toggleBackupLock; failed backups can always be deleted.
Parameters
| Name | Type | Description |
|---|---|---|
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |
| backuprequired | string (uuid) | The backup's UUID, as uuid in the backup's attributes. |
Example
curl -X DELETE "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/backups/{backup}" \
-H "Authorization: Bearer $SILLYDEV_API_KEY" \
-H "Accept: application/json"Response 204Done. There is no response body.
Errors
- 400 The backup is locked.
- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
Get a download link for a backup
GET/servers/{server}/backups/{backup}/download
Permission backup.download
Returns a signed URL from which the backup's .tar.gz archive can be downloaded.
This is step one of two: then send a plain GET to the returned URL, with no API
key. For backups kept on the node, the URL is on the node, works once and expires
after 15 minutes. For backups kept in S3 storage, it is a pre-signed S3 URL that
expires after 5 minutes.
Only ask for the link once the backup has completed successfully. Asking for it is recorded in the server's activity log.
Parameters
| Name | Type | Description |
|---|---|---|
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |
| backuprequired | string (uuid) | The backup's UUID, as uuid in the backup's attributes. |
Example
curl "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/backups/{backup}/download" \
-H "Authorization: Bearer $SILLYDEV_API_KEY" \
-H "Accept: application/json"Response 200A download link for the backup.
{
"object": "signed_url",
"attributes": {
"url": "https://node1.sillydev.co.uk:8080/download/backup?token=eyJhbGciOi..."
}
}Every field in the response
| Name | Type | Description |
|---|---|---|
| objectrequired | string | |
| attributesrequired | object | |
| attributes.urlrequired | string (uri) | Where to download the archive: a single-use URL on the node valid for 15 minutes, or a pre-signed S3 URL valid for 5 minutes. |
Errors
- 400 The request could not be carried out as sent.
detailsays why. - 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
Lock or unlock a backup
POST/servers/{server}/backups/{backup}/lock
Permission backup.delete
Flips the backup's lock: a locked backup becomes unlocked and an unlocked one becomes locked. A locked backup cannot be deleted. There is no body; read is_locked in the response to see the new state.
Parameters
| Name | Type | Description |
|---|---|---|
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |
| backuprequired | string (uuid) | The backup's UUID, as uuid in the backup's attributes. |
Example
curl -X POST "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/backups/{backup}/lock" \
-H "Authorization: Bearer $SILLYDEV_API_KEY" \
-H "Accept: application/json"Response 200The backup with its new lock state.
{
"object": "backup",
"attributes": {
"uuid": "0f2a6c1e-8d3b-4f7a-9c5e-2b1d4e6f8a90",
"is_successful": true,
"is_locked": true,
"name": "Before discord.py upgrade",
"ignored_files": [
"node_modules",
"*.log"
],
"checksum": "sha1:3f786850e387550fdab836ed7e6dc881de23001b",
"bytes": 5242880,
"created_at": "2026-10-10T12:00:00+00:00",
"completed_at": "2026-10-10T12:00:41+00:00"
}
}Every field in the response
| Name | Type | Description |
|---|---|---|
| objectrequired | string | |
| attributesrequired | object | |
| attributes.uuidrequired | string (uuid) | The backup's ID, used in the URLs of the other backup endpoints. |
| attributes.is_successfulrequired | boolean | True once the backup has finished successfully. False while it is in progress (completed_at is null) and when it failed. |
| attributes.is_lockedrequired | boolean | A locked backup cannot be deleted. |
| attributes.namerequired | string | |
| attributes.ignored_filesrequired | array of string | The ignore patterns given when the backup was made, one per item. |
| attributes.checksumrequired | string | null | The archive's checksum as <algorithm>:<hex>, e.g. sha1:.... Null until it succeeds. |
| attributes.bytesrequired | integer | The archive's size in bytes. 0 until it completes. |
| attributes.created_atrequired | string (date-time) | |
| attributes.completed_atrequired | string (date-time) | null | When the backup finished, successfully or not. Null while in progress. |
Errors
- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
Restore a backup
POST/servers/{server}/backups/{backup}/restore
Permission backup.restoreLimited to 3 per 15 minutes per server
Restores the backup's files over the server's files. The request returns at once and
the restore runs on the node: the server is stopped, its files are deleted first if
truncate is true, and the archive is unpacked. Without truncate, files that are
not in the backup are left as they are and files that are in it are overwritten.
While the restore runs, the server's other endpoints answer 409. Only a backup that completed successfully can be restored (400 otherwise). Limited to 3 restores per 15 minutes per server, shared by everyone using it.
Parameters
| Name | Type | Description |
|---|---|---|
| serverrequired | string | The server's 8-character identifier, as shown in the panel, or its full UUID. |
| backuprequired | string (uuid) | The backup's UUID, as uuid in the backup's attributes. |
Body
| Name | Type | Description |
|---|---|---|
| truncaterequired | boolean | Delete all of the server's files before restoring, so the result matches the backup exactly. |
Example
curl -X POST "https://sillydev.co.uk/panel/api/client/servers/1a7ce997/backups/{backup}/restore" \
-H "Authorization: Bearer $SILLYDEV_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"truncate":true}'Response 204The restore has started. There is no response body.
Errors
- 400 The backup is still in progress or failed, or the server is not in a state that allows a restore.
- 401 No API key was sent, or it is wrong, expired or deleted.
- 403 The key is valid but may not do this: it lacks the permission or account scope, it is limited to other servers or IP addresses, or the account may not do it at all.
- 404 There is no such resource, or the key cannot see it. Features that are switched off answer 404 too.
- 409 The resource is busy or in a state that does not allow this right now.
- 422 Some fields were missing or invalid. There is one error per problem, and
meta.source_fieldnames the field. - 429 Too many requests. Wait for the number of seconds in the
Retry-Afterheader, then try again.