Appearance
Backups API
All endpoints need a bearer token:
http
Authorization: Bearer <jwt-or-apikey>A backup is an off-pod export of a pod. Restoring one builds a brand-new pod and leaves the original completely alone. That's the whole difference from a snapshot, which restores in place. If you're deciding which you want, read Snapshots vs backups.
Because a restore or clone creates a pod, both need at least $3 of credit and count against your pod limit.
Backups are free until paid backups start, on a date we announce by email at least 30 days ahead and show on the pricing page. From then, each new backup is billed by the hour for its size, per GB a month (see the pricing page), and the per-pod backup limit goes away. Paid backups explains the change; this page covers the fields and the keep endpoint for following it from code. The start date itself is billingStartsAt in the backup block of GET /open/pricing.
The backup object
json
{
"id": "6a4d5b83b7006ccae6391e63",
"name": "backup-1757980800-h4m9td",
"resourceType": "pod",
"podId": "6a4d5b83b7006ccae6391810",
"podName": "my-app",
"accountId": "6a4d5b83b7006ccae639100a",
"type": "manual",
"status": "completed",
"sizeBytes": 734003200,
"completedAt": "2026-09-16T03:04:11Z",
"paid": false,
"createdAt": "2026-09-16T03:00:00Z",
"updatedAt": "2026-09-16T03:04:11Z"
}That is a backup as it looks while no start date for paid backups has been announced. Once a date is announced, a free backup like this one also carries freeUntil.
| Field | Meaning |
|---|---|
name | Generated, not yours to choose: backup-<timestamp>-<suffix>. |
resourceType | Always pod. Mailbox backups are not listed here - see the note under List all backups. |
podId | Source pod. May point at a pod you've since deleted. |
podName | The pod's name when the backup was taken. Still there after the pod is deleted, which makes it the easy way to tell what an orphaned backup was. |
type | manual or auto. |
status | pending, running, completed, or failed. |
error | Only present on a failed backup, with the reason. |
sizeBytes | Size of the export. 0 until it completes. Once paid backups start, this is what the backup is billed on, in GB of 1024³ bytes. |
completedAt | When the backup finished. Absent until then, and on older backups that finished before the field existed. |
paid | true when the backup is billed, or will be once it completes: anything taken from the start date on (final backups aside), and anything you kept. false on free backups, which is every backup until a start date is announced. |
freeUntil | RFC 3339 timestamp, on free backups once a start date is announced: when the backup is deleted unless you keep it. For a backup taken before the start date, that is 60 days after the start date; for a final backup taken after it, 60 days after the backup. |
final | true on the final backup taken before a pod is deleted over an unpaid invoice (see the billing model). Omitted otherwise. |
keptAt | When you kept the backup. Only on kept backups. |
restoreOnRequest | true on backups made before our move to new backup storage. They can't be restored or kept through the API: open a support ticket and we restore them for you. Once their deletion date is announced, freeUntil carries it. Omitted otherwise. |
Back up a pod
http
POST /api/pods/:id/backupsNeeds backups:create and at least $3 of account credit (402 otherwise). No body. Response 202 with the backup in pending, running in the background.
bash
curl -X POST https://cloud-api.microapps.io/api/pods/6a4d5b83b7006ccae6391810/backups \
-H "Authorization: Bearer $MICROAPPS_API_KEY"json
{
"id": "6a4d5b83b7006ccae6391e63",
"name": "backup-1757980800-h4m9td",
"resourceType": "pod",
"podId": "6a4d5b83b7006ccae6391810",
"podName": "my-app",
"accountId": "6a4d5b83b7006ccae639100a",
"type": "manual",
"status": "pending",
"sizeBytes": 0,
"paid": false,
"createdAt": "2026-09-16T03:00:00Z",
"updatedAt": null
}Until paid backups start, you can keep 4 manual backups per pod - the fifth slot is always held back for the auto schedule. At the limit you get 400 "Backup limit reached. Maximum 4 manual backups per pod (1 slot reserved for automatic backups)", so delete one first. Failed backups do not occupy a slot. The same number is on GET /api/user/account as maxBackupsPerPod if you would rather read it than hardcode it.
Once paid backups start there is no limit, and a new backup comes back with "paid": true: it is billed from the moment it completes until you delete it.
List a pod's backups
http
GET /api/pods/:id/backupsNeeds backups:read. Plain array of that pod's backups, in no guaranteed order - sort on createdAt if order matters.
bash
curl https://cloud-api.microapps.io/api/pods/6a4d5b83b7006ccae6391810/backups \
-H "Authorization: Bearer $MICROAPPS_API_KEY"Set the auto backup schedule
http
PUT /api/pods/:id/backup-scheduleNeeds backups:update.
json
{ "schedule": "daily", "keep": 7 }schedule is "" (disabled), "daily", "weekly", or "monthly" - same cadences as snapshot schedules, all at 3:00 AM UTC. Anything else is 400 "Invalid schedule: <value>". It is optional: leave it out to change only keep. A body with neither field is 400 "Send a schedule, a keep count, or both".
keep is how many automatic backups the pod keeps once paid backups start, from 1 to 365; anything else is 400 "Keep must be between 1 and 365 backups". It is optional. Leave it out and the pod's stored number stays as it is; a pod that has never had one keeps 5. The pod reports it as backupKeep.
bash
curl -X PUT https://cloud-api.microapps.io/api/pods/6a4d5b83b7006ccae6391810/backup-schedule \
-H "Authorization: Bearer $MICROAPPS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"schedule":"daily","keep":7}'Response 200:
json
{ "message": "Backup schedule updated" }Which automatic backups go depends on whether paid backups have started:
- Until then, automatic backups rotate among themselves once the pod is at its limit: the oldest
autobackup that you haven't kept is deleted to make room for the new one.keepis stored but not used yet. - From the start date, each time a scheduled backup finishes, the pod's automatic backups taken since the start date are trimmed to the newest
keep. Automatic backups from before the start date are not counted; they stay free until theirfreeUntildate. Kept backups are never pruned. A lowerkeeptakes effect after the next scheduled run, and turning the schedule off leaves the automatic backups you already have, billed until you delete them.
Your manual backups are never rotated away.
Clone a pod
http
POST /api/pods/:id/cloneCopies a pod straight into a new pod on the same Region, network, and firewall, with the same compute and storage sizes. Optional body:
json
{ "name": "my-app-clone" }Blank or omitted name gets a random one; a name already on your account returns 409. Response 202 with the new pod object in Cloning.
bash
curl -X POST https://cloud-api.microapps.io/api/pods/6a4d5b83b7006ccae6391810/clone \
-H "Authorization: Bearer $MICROAPPS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "my-app-clone" }'json
{
"id": "6a4d5b83b7006ccae6391f04",
"name": "my-app-clone",
"os": "ubuntu",
"version": "24.04",
"storage": "tiny",
"compute": "small",
"username": "ubuntu",
"privateIp": "",
"hostname": "i-6a4d5b83b7006ccae6391f04",
"domainName": "your-pod.microapps.io",
"privatePort": 22,
"publicPort": 3005,
"status": "Cloning",
"type": "instance",
"networkId": "6a4d5b83b7006ccae6391a22",
"serverId": "6a4d5b83b7006ccae639170f",
"firewallId": "6a4d5b83b7006ccae6391b07",
"accountId": "6a4d5b83b7006ccae639100a",
"createdAt": "2026-09-16T09:12:00Z",
"updatedAt": null
}The source pod is untouched and does not need to be stopped. The clone copies the disk only, never the running memory state, so treat it as a pod that was powered off at that instant.
Clone needs pod-create permission
Cloning makes a pod, so it's gated on pods:create (not a backup permission), needs $3 of credit, and consumes a slot from your pod limit.
List all backups on the account
http
GET /api/backupsNeeds backups:read. Plain array of every backup on the account, across all pods, including pods you have since deleted.
bash
curl https://cloud-api.microapps.io/api/backups \
-H "Authorization: Bearer $MICROAPPS_API_KEY"json
[
{
"id": "6a4d5b83b7006ccae6391e63",
"name": "backup-1757980800-h4m9td",
"resourceType": "pod",
"podId": "6a4d5b83b7006ccae6391810",
"podName": "my-app",
"accountId": "6a4d5b83b7006ccae639100a",
"type": "manual",
"status": "completed",
"sizeBytes": 734003200,
"completedAt": "2026-09-16T03:04:11Z",
"paid": false,
"createdAt": "2026-09-16T03:00:00Z",
"updatedAt": "2026-09-16T03:04:11Z"
},
{
"id": "6a4d5b83b7006ccae6391e7a",
"name": "backup-1789614000-x7k2pd",
"resourceType": "pod",
"podId": "6a4d5b83b7006ccae6391877",
"podName": "my-app-staging",
"accountId": "6a4d5b83b7006ccae639100a",
"type": "auto",
"status": "completed",
"sizeBytes": 524288000,
"completedAt": "2026-09-17T03:02:37Z",
"paid": false,
"createdAt": "2026-09-17T03:00:00Z",
"updatedAt": "2026-09-17T03:02:37Z"
}
]Mailbox backups are not in this list
The platform backs up every mailbox for you, and those backups stay free. They are not listed here: everything this endpoint returns is a pod backup.
A backup outlives its pod. The second entry above belongs to a pod that has since been deleted, and podName still says what it was. The console's Backups page shows this same list, with Restore and Delete on every row and Keep on free ones.
Restore a backup into a new pod
http
POST /api/backups/:backupId/restoreNeeds backups:update, plus $3 of credit and a free pod slot. Optional body { "name": "..." } for the new pod's name. Response 202 with the new pod object in Restoring.
bash
curl -X POST https://cloud-api.microapps.io/api/backups/6a4d5b83b7006ccae6391e63/restore \
-H "Authorization: Bearer $MICROAPPS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "my-app-restored" }'Rules:
- The backup must be
resourceType: "pod", otherwise400 "Only pod backups can be restored here". - It must be
status: "completed", otherwise400 "Only completed backups can be restored". - A backup with
restoreOnRequest: truegets400 "This backup was made before backups moved to new storage, so it can be restored on request only. Open a support ticket and we'll restore it for you." - The private network the pod was on must still exist, otherwise
400 "The backup's network no longer exists; restoring requires the original network". The firewall is more forgiving: if the original is gone, the new pod falls back to one of yours. - The restore creates a new pod, so it uses a slot from your pod limit and needs $3 credit.
- Your original pod (if it still exists) is untouched.
Keep a free backup
http
POST /api/backups/:backupId/keepNeeds backups:update and at least $3 of account credit (402 otherwise). No body. Turns a free backup - one taken before paid backups start, or a final backup - into a paid one that stays until you delete it. Billing for it starts at the later of the start date and the moment you kept it. Response 200 with the updated backup.
bash
curl -X POST https://cloud-api.microapps.io/api/backups/6a4d5b83b7006ccae6391e63/keep \
-H "Authorization: Bearer $MICROAPPS_API_KEY"json
{
"id": "6a4d5b83b7006ccae6391e63",
"name": "backup-1757980800-h4m9td",
"resourceType": "pod",
"podId": "6a4d5b83b7006ccae6391810",
"podName": "my-app",
"accountId": "6a4d5b83b7006ccae639100a",
"type": "manual",
"status": "completed",
"sizeBytes": 734003200,
"completedAt": "2026-09-16T03:04:11Z",
"keptAt": "2026-10-14T08:30:00Z",
"paid": true,
"createdAt": "2026-09-16T03:00:00Z",
"updatedAt": "2026-10-14T08:30:00Z"
}freeUntil is gone and paid is true: the backup no longer has a deletion date.
Rules:
- Only completed pod backups can be kept, otherwise
400 "Only completed pod backups can be kept". - A backup with
restoreOnRequest: truecan't be kept:400 "This backup was made before backups moved to new storage, so it can be restored on request only and can't be kept". - The backup must still be free. One that is already paid, because you kept it before or it was taken after paid backups start, gets
400 "This backup is already kept until you delete it". So does every backup while no start date has been announced, since nothing expires then.
Delete a backup
http
DELETE /api/backups/:backupIdNeeds backups:delete. Response 200:
json
{ "message": "Backup deleted" }If the backup is still running, you get 400 "Backup is in progress; try again once it finishes" - the console disables the action for the same reason. A pending or failed backup deletes fine.
Deleting a paid backup stops its charge from that moment. Deleting the pod does not: its backups, and their charges, stay until you delete them.
See also
- Backups concept, including paid backups
- Snapshots API - the in-place cousin
- Snapshots & backups guide
GET /open/pricing- the backup rate and start date