Proxmox VEXX · CLI & AutomationREST API automation
PVE REST API: tokens, authentication, and the API viewer
What you'll learn
- Use the PVE REST API directly with curl
- Create and manage API tokens with the right privileges
- Navigate the API viewer to find endpoints
- Handle tickets, CSRF prevention, and async operations
Prerequisites
Verified against Proxmox VE 9.2.4 · Proxmox Backup Server 4.2.5 · Ceph Squid / Tentacle · Debian 13 (Trixie) · Linux kernel 7.0 (PVE 9.2 default) · 2026-08-07
PVE REST API: tokens, authentication, and the API viewer
pvesh is a CLI wrapper around the PVE HTTP API. Anything you can
do with pvesh, you can do with curl. This lesson shows when and
how to use the REST API directly.
When to use the API directly
pvesh is fine for ad-hoc commands and simple scripts. Use the REST
API directly when:
- You’re integrating with a non-shell language (Python, Go, Java)
- You need streaming responses (large data sets, real-time updates)
- You want fine-grained control over HTTP behaviour (retries, timeouts)
- You’re building a library
Authentication: tickets vs API tokens
The PVE API supports two auth methods:
Tickets (session-based)
# Step 1: Get a ticket (session)
TICKET=$(curl -sk -X POST 'https://pve-01:8006/api2/json/access/ticket' \
-H 'Content-Type: application/json' \
-d '{"username":"root@pam","password":"secret"}' | \
jq -r '.data.ticket')
# Step 2: Use the ticket in subsequent requests
curl -sk -X GET 'https://pve-01:8006/api2/json/nodes' \
-H "Cookie: PVEAuthCookie=$TICKET"
# Tickets expire in 2 hours
Tickets are useful for browser sessions but expire, so they’re not ideal for automation.
API tokens (recommended for automation)
# Step 1: Create a token (one-time, on the PVE host)
pvesh create /access/users/admin@pve/token/automation --privsep 0
# Returns the secret — save it!
# Step 2: Use the token in API requests
TOKEN="admin@pve!automation=12345678-1234-1234-1234-123456789abc"
curl -sk -X GET 'https://pve-01:8006/api2/json/nodes' \
-H "Authorization: PVEAPIToken=$TOKEN"
API tokens don’t expire by default (configurable), can be revoked individually, and don’t require a session.
The API viewer
The PVE documentation includes a complete interactive API viewer at pve.proxmox.com/pve-docs/api-viewer.html. It shows every endpoint with parameters, expected output, and an executable example.
For local installation, the same viewer is at:
https://pve-01:8006/pve-docs/api-viewer.html
Use the API viewer to discover endpoints and their parameters.
Common API patterns
List VMs
TOKEN="admin@pve!automation=..."
curl -sk 'https://pve-01:8006/api2/json/cluster/resources?type=vm' \
-H "Authorization: PVEAPIToken=$TOKEN" | \
jq '.data[] | {vmid, name, node, status}'
Create a VM
VMID=100
curl -sk -X POST "https://pve-01:8006/api2/json/nodes/pve-01/qemu/$VMID/clone" \
-H "Authorization: PVEAPIToken=$TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"newid": 200,
"name": "web-01",
"full": false,
"storage": "local-zfs"
}'
Update VM config
curl -sk -X POST "https://pve-01:8006/api2/json/nodes/pve-01/qemu/100/config" \
-H "Authorization: PVEAPIToken=$TOKEN" \
-H 'Content-Type: application/json' \
-d '{"memory": 4096, "cores": 4}'
Start, stop, shutdown
curl -sk -X POST "https://pve-01:8006/api2/json/nodes/pve-01/qemu/100/status/start" \
-H "Authorization: PVEAPIToken=$TOKEN"
curl -sk -X POST "https://pve-01:8006/api2/json/nodes/pve-01/qemu/100/status/stop" \
-H "Authorization: PVEAPIToken=$TOKEN"
Async tasks
Some operations return a task UPID instead of immediate results. The task runs in the background; you can poll for completion:
# Start an async task — returns a UPID
UPID=$(curl -sk -X POST "https://pve-01:8006/api2/json/nodes/pve-01/qemu/100/snapshot" \
-H "Authorization: PVEAPIToken=$TOKEN" \
-H 'Content-Type: application/json' \
-d '{"snapname": "pre-upgrade"}' | jq -r '.data')
# Poll for completion
while true; do
STATUS=$(curl -sk "https://pve-01:8006/api2/json/nodes/pve-01/tasks/$UPID/status" \
-H "Authorization: PVEAPIToken=$TOKEN" | jq -r '.data.status')
echo "Status: $STATUS"
if [ "$STATUS" = "stopped" ]; then break; fi
sleep 2
done
CSRF prevention
The PVE API requires a CSRF prevention token for all mutating requests (POST, PUT, DELETE) when using ticket-based auth:
# Get ticket + CSRF token together
RESP=$(curl -sk -X POST 'https://pve-01:8006/api2/json/access/ticket' \
-H 'Content-Type: application/json' \
-d '{"username":"admin@pam","password":"secret"}')
TICKET=$(echo "$RESP" | jq -r '.data.ticket')
CSRF=$(echo "$RESP" | jq -r '.data.CSRFPreventionToken')
# Include CSRF token in mutating requests
curl -sk -X POST "https://pve-01:8006/api2/json/nodes/pve-01/qemu/100/status/start" \
-H "Cookie: PVEAuthCookie=$TICKET" \
-H "CSRFPreventionToken: $CSRF"
API tokens don’t need CSRF prevention — they have their own auth.
Error handling
The API returns standard HTTP status codes:
| Status | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad request — invalid parameters |
| 401 | Unauthorized — bad credentials |
| 403 | Forbidden — credentials valid but privileges insufficient |
| 404 | Not found |
| 500 | Server error — check the task log |
| 596 | PVE-specific error (e.g., snapshot in use) |
Errors include a JSON body with details:
{
"errors": {
"vmid": "VM 100 does not exist"
},
"data": null
}
Always parse the errors object for useful diagnostics.
Production considerations
- Rate limiting. The API has soft limits; bulk operations should
include
sleep 0.1between requests. - TLS verification. In production, set
insecure=Falseand use proper certificates. Self-signed certs require explicit trust. - Timeouts. Always set curl timeout (
--max-time 30) so a hung API call doesn’t block your script forever. - Retries. Transient errors (503, network) are common. Use exponential backoff: 1s, 2s, 4s, 8s. Up to 5 retries.
Common mistakes
- Using the root user. Always create a dedicated user with the minimum required privileges.
- Tickets in scripts. Tokens are better — they don’t expire during a long script run.
- Forgetting CSRF. With ticket auth, mutating requests fail with 401 unless you include the CSRF token.
- Not handling 5xx. Transient errors are common; a script that fails on the first 503 is fragile.
Key takeaways
- Use API tokens for automation, tickets for browser sessions.
- Use the API viewer to discover endpoints.
- Handle async operations with task UPIDs and polling.
- Add retries with exponential backoff for transient errors.
Knowledge check
Knowledge check · 4 questions
Q1. Which HTTP header carries a PVE API token?
Q2. A Proxmox API token stays valid indefinitely unless an expiry is set when it is created.
Q3. Which of these are good practices for REST API automation? (Select all that apply)
Q4. Name the API endpoint that lists cluster resources.
Passing score: 75%. Answers are checked in this browser.