---
title: "Backups API Reference"
description: "Silly Development API reference for backups: create a backup of your bot's files, download it, lock it, restore it or delete it from code."
url: "https://sillydev.co.uk/docs/panel/backups"
updated: "2026-10-10"
---

API reference

# Backups

Make, download, restore and lock backups of your server's files.

[← API docs](https://sillydev.co.uk/docs/panel) · 7 endpoints · Updated 10 October 2026

**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.create Limited 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_field` names the field.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, 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. `detail` says 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.restore Limited 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_field` names the field.
- 429 Too many requests. Wait for the number of seconds in the `Retry-After` header, then try again.
