Skip to content

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.

FieldMeaning
nameGenerated, not yours to choose: backup-<timestamp>-<suffix>.
resourceTypeAlways pod. Mailbox backups are not listed here - see the note under List all backups.
podIdSource pod. May point at a pod you've since deleted.
podNameThe 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.
typemanual or auto.
statuspending, running, completed, or failed.
errorOnly present on a failed backup, with the reason.
sizeBytesSize of the export. 0 until it completes. Once paid backups start, this is what the backup is billed on, in GB of 1024³ bytes.
completedAtWhen the backup finished. Absent until then, and on older backups that finished before the field existed.
paidtrue 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.
freeUntilRFC 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.
finaltrue on the final backup taken before a pod is deleted over an unpaid invoice (see the billing model). Omitted otherwise.
keptAtWhen you kept the backup. Only on kept backups.
restoreOnRequesttrue 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/backups

Needs 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/backups

Needs 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-schedule

Needs 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 auto backup that you haven't kept is deleted to make room for the new one. keep is 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 their freeUntil date. Kept backups are never pruned. A lower keep takes 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/clone

Copies 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/backups

Needs 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/restore

Needs 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", otherwise 400 "Only pod backups can be restored here".
  • It must be status: "completed", otherwise 400 "Only completed backups can be restored".
  • A backup with restoreOnRequest: true gets 400 "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/keep

Needs 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: true can'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/:backupId

Needs 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 ​

Built for the long tail.