Everything in Nutanix is an API call. Whether youâre clicking buttons in Prism or running Terraform, it all comes down to REST. This week weâll learn the fundamentals â authentication, endpoints, and practical curl examples you can run today.
Why Learn the API?
Let me tell you about a failed DR test.
We had retired some servers â deleted the VMs, closed the tickets, moved on. Except those VMs were still in our DR recovery plans. Still tagged with replication policies. The data was technically still replicating to our DR site, protecting ghosts.
When we ran our DR failover test, it failed. We worked with Nutanix support (who were excellent), but we had to reschedule. Dozens of people lost their weekends â twice â because of orphaned entries we didnât know existed.
Hereâs the thing: this information exists in Nutanix, but the UI doesnât surface it as a single report. You can see VMs. You can see protection policies. You can see recovery plans. But thereâs no âshow me the mismatchesâ button.
Thatâs where the API comes in. By querying multiple endpoints and cross-referencing the data, we built daily reporting that catches these orphans before they blow up a DR test. The API lets you build the reports that donât exist in the UI.
Before we dive in: read the Nutanix API documentation and get comfortable with a tool like Postman. Experiment there before writing scripts.
Table of Contents
Open Table of Contents
Prism Central vs Prism Element
Before making API calls, you need to know which endpoint to hit.
Prism Element manages a single cluster. Itâs the local management interface running on your Nutanix cluster. Use it for cluster-specific operations like storage containers and host management.
Prism Central manages multiple clusters from a single pane of glass. Itâs where youâll do most of your automation work â VM management, policies, and cross-cluster operations.
| Feature | Prism Element | Prism Central |
|---|---|---|
| Scope | Single cluster | Multiple clusters |
| API Versions | v2.0 only | v3, v4 |
| Port | 9440 | 9440 |
| Use Case | Storage, hosts, local ops | VMs, policies, automation |
Key takeaway: If youâre automating VM operations, you want Prism Central. If youâre managing storage containers, you need Prism Element.
API Versions: v2, v3, and v4
Nutanix has evolved its APIs over time. Hereâs what you need to know:
v2.0 API (Prism Element only)
- Standard RESTful GET/POST/PUT/DELETE
- Used for cluster-local resources
- Deprecating after Q4 2026
GET https://<prism-element>:9440/api/nutanix/v2.0/storage_containers
v3 API (Prism Central only)
- Uses POST for list operations (intentful design)
- Requires JSON body with
kindparameter - Still supported but migration to v4 recommended
POST https://<prism-central>:9440/api/nutanix/v3/vms/list
Body: {"kind":"vm"}
v4 API (Prism Central only â Recommended)
- Proper RESTful semantics
- Organized by namespace (vmm, clustermgmt, storage)
- Supports API key authentication
- Use this for all new development
GET https://<prism-central>:9440/api/vmm/v4.0/vms
Recommendation: Use v4 APIs for new projects. Theyâre cleaner, better documented, and wonât be deprecated anytime soon.
Authentication Methods
Basic Authentication (All Versions)
The simplest method â works everywhere.
curl -X GET "https://192.168.1.100:9440/api/clustermgmt/v4.0/clusters" \
-H "Accept: application/json" \
--basic \
--user "admin:YourPassword123" \
--insecure
The --insecure flag bypasses certificate verification. In production, use proper certificates.
API Key Authentication (v4 only)
Better for automation â no passwords in scripts.
- Create a service account in Prism Central
- Generate an API key (save it immediately â shown only once)
- Use the
X-Ntnx-Api-Keyheader
curl -X GET "https://192.168.1.100:9440/api/vmm/v4.0/vms" \
-H "Accept: application/json" \
-H "X-Ntnx-Api-Key: your-api-key-here" \
--insecure
Your First API Call
Letâs verify connectivity by listing clusters. Set up your environment first:
# Set your variables
export PC_IP="192.168.1.100"
export PC_USER="admin"
export PC_PASS="YourPassword123"
Now list your clusters:
curl -s -X GET "https://$PC_IP:9440/api/clustermgmt/v4.0/clusters" \
-H "Accept: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure | jq '.data[] | {name: .name, uuid: .extId}'
If that works, youâre connected. If not, check the Troubleshooting Guide.
Common Operations with curl
List All VMs (v3 API)
The v3 API uses POST for list operations:
curl -s -X POST "https://$PC_IP:9440/api/nutanix/v3/vms/list" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure \
-d '{
"kind": "vm",
"length": 100,
"offset": 0
}' | jq '.entities[] | {name: .spec.name, uuid: .metadata.uuid, power: .spec.resources.power_state}'
List All VMs (v4 API)
The v4 API uses proper GET:
curl -s -X GET "https://$PC_IP:9440/api/vmm/v4.0/vms" \
-H "Accept: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure | jq '.data[] | {name: .name, uuid: .extId}'
Filter VMs by Name
Find VMs matching a pattern (v3 API):
curl -s -X POST "https://$PC_IP:9440/api/nutanix/v3/vms/list" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure \
-d '{
"kind": "vm",
"filter": "name==prod-*"
}' | jq '.entities[] | .spec.name'
Get VM Details
Retrieve a specific VM by UUID:
VM_UUID="your-vm-uuid-here"
curl -s -X GET "https://$PC_IP:9440/api/nutanix/v3/vms/$VM_UUID" \
-H "Accept: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure | jq '.spec'
Power Operations
Power on a VM:
VM_UUID="your-vm-uuid-here"
# First, get the current VM spec
CURRENT_SPEC=$(curl -s -X GET "https://$PC_IP:9440/api/nutanix/v3/vms/$VM_UUID" \
-H "Accept: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure)
# Update power state
echo "$CURRENT_SPEC" | jq '.spec.resources.power_state = "ON"' | \
curl -s -X PUT "https://$PC_IP:9440/api/nutanix/v3/vms/$VM_UUID" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure \
-d @-
List Storage Containers (Prism Element)
Storage containers require the v2 API on Prism Element:
PE_IP="192.168.1.50" # Prism Element IP
curl -s -X GET "https://$PE_IP:9440/api/nutanix/v2.0/storage_containers" \
-H "Accept: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure | jq '.entities[] | {name: .name, uuid: .storage_container_uuid}'
List Images
curl -s -X GET "https://$PC_IP:9440/api/vmm/v4.0/content/images" \
-H "Accept: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure | jq '.data[] | {name: .name, type: .type, sizeBytes: .sizeBytes}'
List Clusters with Details
curl -s -X GET "https://$PC_IP:9440/api/clustermgmt/v4.0/clusters" \
-H "Accept: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure | jq '.data[] | {
name: .name,
uuid: .extId,
hypervisor: .config.hypervisorTypes[0],
nodes: .config.nodes | length
}'
Real-World Example: DR Orphan Detection
Remember that failed DR test I mentioned? Hereâs how to catch those orphans before they bite you.
The goal: cross-reference VMs, protection policies, and recovery plans to find mismatches.
List Protection Policies
curl -s -X POST "https://$PC_IP:9440/api/nutanix/v3/protection_rules/list" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure \
-d '{"kind": "protection_rule", "length": 100}' | \
jq '.entities[] | {name: .spec.name, uuid: .metadata.uuid, categories: .spec.resources.categories}'
List Recovery Plans
curl -s -X POST "https://$PC_IP:9440/api/nutanix/v3/recovery_plans/list" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure \
-d '{"kind": "recovery_plan", "length": 100}' | \
jq '.entities[] | {name: .spec.name, uuid: .metadata.uuid}'
Find VMs Protected by a Specific Category
Protection policies use categories to determine which VMs to protect. Query VMs by category:
# Find VMs with category "DR-Tier=Gold"
curl -s -X POST "https://$PC_IP:9440/api/nutanix/v3/vms/list" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure \
-d '{
"kind": "vm",
"filter": "categories.DR-Tier==Gold",
"length": 500
}' | jq '.entities[] | {name: .spec.name, uuid: .metadata.uuid}'
The Orphan Detection Script
This script identifies VMs referenced in protection policies that no longer exist:
#!/bin/bash
# dr_orphan_check.sh - Find orphaned DR configurations
PC_IP="your-prism-central-ip"
PC_USER="admin"
PC_PASS="your-password"
echo "=== DR Orphan Detection Report ==="
echo "Generated: $(date)"
echo ""
# Get all existing VM UUIDs
echo "Fetching VM inventory..."
curl -s -X POST "https://$PC_IP:9440/api/nutanix/v3/vms/list" \
-H "Content-Type: application/json" \
--basic --user "$PC_USER:$PC_PASS" --insecure \
-d '{"kind":"vm","length":500}' | \
jq -r '.entities[] | .metadata.uuid' | sort > /tmp/existing_vms.txt
VM_COUNT=$(wc -l < /tmp/existing_vms.txt | tr -d ' ')
echo "Found $VM_COUNT VMs in cluster"
echo ""
# Get protection policies and their categories
echo "Checking protection policies..."
curl -s -X POST "https://$PC_IP:9440/api/nutanix/v3/protection_rules/list" \
-H "Content-Type: application/json" \
--basic --user "$PC_USER:$PC_PASS" --insecure \
-d '{"kind":"protection_rule","length":100}' > /tmp/policies.json
POLICY_COUNT=$(jq '.entities | length' /tmp/policies.json)
echo "Found $POLICY_COUNT protection policies"
echo ""
# Check each policy for orphaned references
echo "=== Potential Issues ==="
jq -r '.entities[] | "\(.spec.name)|\(.metadata.uuid)"' /tmp/policies.json | \
while IFS='|' read -r POLICY_NAME POLICY_UUID; do
# Get category filter for this policy
CATEGORIES=$(curl -s -X GET "https://$PC_IP:9440/api/nutanix/v3/protection_rules/$POLICY_UUID" \
-H "Accept: application/json" \
--basic --user "$PC_USER:$PC_PASS" --insecure | \
jq -r '.spec.resources.categories // empty | to_entries[] | "\(.key)=\(.value[])"' 2>/dev/null)
if [ -n "$CATEGORIES" ]; then
echo "Policy: $POLICY_NAME"
echo " Categories: $CATEGORIES"
fi
done
echo ""
echo "=== Summary ==="
echo "Review any policies with categories that no longer match active VMs."
echo "Run this script daily to catch orphans before DR tests."
Why This Matters
The Prism UI shows you:
- A list of VMs
- A list of protection policies
- A list of recovery plans
What it doesnât show you: the gaps between them. A deleted VM might still be:
- Referenced in a recovery plan
- Tagged with a category that a protection policy is watching
- Generating replication traffic to your DR site
The API lets you connect these dots. Run this check daily, and youâll never fail a DR test because of orphaned entries again.
Error Handling
Common HTTP Status Codes
| Code | Meaning | What to Do |
|---|---|---|
| 200 | Success | Youâre good |
| 201 | Created | Resource created successfully |
| 400 | Bad Request | Check your JSON syntax |
| 401 | Unauthorized | Check credentials |
| 403 | Forbidden | Check permissions or wrong endpoint (v3 on PE) |
| 404 | Not Found | Check UUID or endpoint path |
| 409 | Conflict | Resource already exists or state conflict |
| 429 | Rate Limited | Slow down, implement backoff |
| 500 | Server Error | Check Prism Central logs |
Debugging Failed Requests
Add -v for verbose output:
curl -v -X GET "https://$PC_IP:9440/api/clustermgmt/v4.0/clusters" \
-H "Accept: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure
Check the HTTP status code separately:
curl -s -o /dev/null -w "%{http_code}" \
-X GET "https://$PC_IP:9440/api/clustermgmt/v4.0/clusters" \
-H "Accept: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure
Rate Limiting and Best Practices
Rate Limits by Prism Central Size
| PC Size | Rate Limit |
|---|---|
| X-Small (18GB, 4 CPU) | 30 req/sec |
| Small (26GB, 6 CPU) | 40 req/sec |
| Large (44GB, 10 CPU) | 60 req/sec |
| X-Large (60GB, 14 CPU) | 80 req/sec |
Implement Retry with Backoff
#!/bin/bash
# retry_request.sh - Retry with exponential backoff
make_request() {
local url=$1
local max_attempts=5
local attempt=1
local backoff=1
while [ $attempt -le $max_attempts ]; do
response=$(curl -s -w "\n%{http_code}" -X GET "$url" \
-H "Accept: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure)
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" == "200" ]; then
echo "$body"
return 0
elif [ "$http_code" == "429" ]; then
echo "Rate limited. Waiting ${backoff}s..." >&2
sleep $backoff
backoff=$((backoff * 2))
attempt=$((attempt + 1))
else
echo "Error: HTTP $http_code" >&2
echo "$body" >&2
return 1
fi
done
echo "Failed after $max_attempts attempts" >&2
return 1
}
make_request "https://$PC_IP:9440/api/clustermgmt/v4.0/clusters"
Best Practices
- Use pagination for large result sets â donât fetch 10,000 VMs at once
- Cache cluster UUIDs â they donât change often
- Use API keys for automation instead of passwords
- Implement backoff for 429 responses
- Log your requests for debugging
Hands-On Lab
Letâs put it all together. Youâll need:
- Access to Prism Central
curlandjqinstalled- Admin credentials
Step 1: Set Up Environment
export PC_IP="your-prism-central-ip"
export PC_USER="admin"
export PC_PASS="your-password"
Step 2: Verify Connectivity
curl -s -X GET "https://$PC_IP:9440/api/clustermgmt/v4.0/clusters" \
-H "Accept: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure | jq '.data | length'
This should return the number of clusters.
Step 3: List VMs and Their Power States
curl -s -X POST "https://$PC_IP:9440/api/nutanix/v3/vms/list" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure \
-d '{"kind":"vm","length":500}' | \
jq -r '.entities[] | [.spec.name, .spec.resources.power_state] | @tsv' | \
sort | column -t
Step 4: Count VMs by Power State
curl -s -X POST "https://$PC_IP:9440/api/nutanix/v3/vms/list" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure \
-d '{"kind":"vm","length":500}' | \
jq -r '.entities[] | .spec.resources.power_state' | sort | uniq -c
Step 5: Find VMs Without a Specific Tag
curl -s -X POST "https://$PC_IP:9440/api/nutanix/v3/vms/list" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure \
-d '{"kind":"vm","length":500}' | \
jq -r '.entities[] | select(.metadata.categories == null or .metadata.categories.Environment == null) | .spec.name'
Step 6: Export VM Inventory to CSV
curl -s -X POST "https://$PC_IP:9440/api/nutanix/v3/vms/list" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--basic \
--user "$PC_USER:$PC_PASS" \
--insecure \
-d '{"kind":"vm","length":500}' | \
jq -r '["Name","UUID","Power","vCPUs","Memory_MB"], (.entities[] | [.spec.name, .metadata.uuid, .spec.resources.power_state, .spec.resources.num_vcpus_per_socket, .spec.resources.memory_size_mib]) | @csv' > vm_inventory.csv
Troubleshooting Guide
âConnection refusedâ on port 9440
- Verify the IP address is correct
- Check firewall rules allow HTTPS/9440
- Confirm Prism Central/Element is running
401 Unauthorized
- Double-check username and password
- Ensure the account isnât locked
- Try logging into the web UI with the same credentials
403 Forbidden with v3 APIs
- Youâre likely hitting Prism Element instead of Prism Central
- v3 APIs only work on Prism Central
- Verify youâre using the PC IP, not PE IP
400 Bad Request on v3 list operations
- v3 requires a JSON body with
kindparameter - Donât use GET â use POST for list operations
# Wrong
curl -X GET "https://$PC_IP:9440/api/nutanix/v3/vms/list"
# Correct
curl -X POST "https://$PC_IP:9440/api/nutanix/v3/vms/list" \
-H "Content-Type: application/json" \
-d '{"kind":"vm"}'
SSL Certificate Errors
For testing, use --insecure. For production:
curl --cacert /path/to/ca-bundle.crt \
-X GET "https://$PC_IP:9440/api/clustermgmt/v4.0/clusters"
Slow Responses
- Reduce
lengthparameter in list requests - Add filters to narrow results
- Check Prism Central resource utilization
Resources
- Nutanix API Developer Portal
- Nutanix.dev API Reference
- The Nutanix Bible - REST APIs
- Nutanix Code Samples (GitHub)
Happy automating!