API reference
Every action, scriptable.
Deploy, resize, and manage compute, storage, and IP addresses — everything the control panel can do, your own code can do too. Authenticate with a single API key.
Authentication
API keys
Every request is authenticated with an API key sent in the Authorization header. There's no separate signing step, no OAuth flow — generate a key from the dashboard, send it on every request.
Authorization: Api-Key YOUR_API_KEY
Create and manage keys from Infrastructure → API Keys in the Iotamine control panel. Each key can optionally be locked to a single source IP — set allowed_ip and requests from anywhere else are rejected before authentication even runs.
Every key is Read-only by default — it can see everything your account owns, but any request other than GET/HEAD/OPTIONS is rejected with a 403 until you switch it to Read & write. Beyond that toggle there's no finer-grained scoping today — a read & write key can manage everything your account owns. Treat it like a password: revoke a key (set it inactive or delete it) the moment it's no longer needed.
Not every endpoint accepts an API key. Compute, storage, IP, and reference endpoints do (marked API key throughout this page) — billing, invoices, and account-profile endpoints are dashboard-session only for now (see Dashboard-only endpoints). A key also can't create or manage other keys — that always requires being logged into the dashboard itself.
Quickstart
List your VMs in one request
curl https://iotamine.com/api/vps/ \ -H "Authorization: Api-Key YOUR_API_KEY"
This one's paginated (5 per page by default) — see the response shape in VPS below, or add ?page_size=100 to get more back at once.
Compute
VPS
Full lifecycle for your virtual machines — deploy, resize, power control, console access, backups, and the disks/IPs bundled onto one.
/api/vps/paginatedAPI keyList every VPS you own — paginated, 5 per page by default.
Query params
searchstringFilter by hostname/IP.pageintPage number.page_sizeintResults per page — up to 100000, so a large value effectively disables pagination.Example response · 200
{
"count": 1, "next": null, "previous": null,
"results": [
{
"id": 412, "hostname": "web-01", "status": "active",
"machine_status": "Running", "cores": 4, "ram": 8,
"primary_disk": 160, "traffic": 5, "hourly_rate": 0.0248,
"vps_country": "Germany", "vps_city": "Frankfurt",
"os_name": "ubuntu", "ip_address": [
{ "id": 88, "ip": "198.51.100.42", "is_primary": true }
],
"is_building": false, "is_failed": false,
"created_at": "2026-07-02T10:15:00Z"
}
]
}/api/vps/API keyDeploy a new VM — a fresh disk + OS template, or booted from an existing volume you already own.
Request body
hostname *stringServer display name.password *stringRoot/administrator password.pop *intRegion id (see GET /api/pop/).cores *intvCPU count — one of the allowed values (1–32).ram *intRAM in GB — one of the allowed values (2–64).diskintBoot disk size in GB. Required unless existing_boot_volume is set.operating_systemintOS template id (see GET /api/os/). Required unless existing_boot_volume is set.existing_boot_volumeintA volume you already own — boots the VM from it instead of provisioning a fresh disk.existing_ipintAn IP address you already own — used instead of auto-assigning a new one.ssh_keyintOne of your saved SSH key ids.disable_pwd_authboolDisable SSH password login (key-only).Example response · 201
{
"id": 413, "hostname": "web-02", "status": "active",
"cores": 4, "ram": 8, "primary_disk": 160,
"vid": "b6e2...", "created_at": "2026-08-17T09:00:00Z"
}/api/vps/{id}/API keyRetrieve one VPS, including a live-checked machine_status.
Example response · 200
{ "id": 412, "hostname": "web-01", "machine_status": "Running", "...": "..." }/api/vps/{id}/API keyResize (cores/ram — must be one of the allowed tier values) or change hostname/password. Hostname and password changes require the VPS to be stopped first.
Request body
coresintNew vCPU count.ramintNew RAM in GB.hostnamestringNew hostname — requires the VPS to be stopped.passwordstringNew root password — requires the VPS to be stopped.Example response · 200
{ "id": 412, "cores": 8, "ram": 16, "...": "..." }/api/vps/{id}/API keyDestroy a VPS (dispatches an async terminate task). Owned disks/IPs survive detached by default — list ids you want released instead.
Request body
release_ip_idsint[]Owned IPs to fully release (not just detach) along with the VPS.release_disk_idsint[]Owned volumes to fully release along with the VPS.Example response · 204
{ "message": "VPS deleted successfully." }/api/vps/{id}/start/ · stop/ · restart/ · poweroff/API keyPower actions — synchronous, plain GET requests (not POST). Blocked while a backup/restore task is already running.
Example response · 200
{ "message": "VPS started successfully." }/api/vps/{id}/vnc/API keyOpen a browser console session. Returns a WebSocket URL (and password, Virtualizor nodes only).
Example response · 200
{ "ws_url": "wss://iotamine.com/control/websockify/8123", "password": null }/api/vps/{id}/stats/API keyLive CPU/RAM/bandwidth usage from the hypervisor, cached briefly.
Example response · 200
{ "cpu": 12.4, "used_ram": 2048, "ram": 8192, "bandwidth": 5120, "used_bandwidth": 340 }/api/vps/{id}/bandwidth_history/ · metrics_history/API keyHistorical bandwidth / CPU+RAM time series for graphing.
Example response · 200
[ { "timestamp": "2026-08-17T09:00:00Z", "value": 12.4 } ]/api/vps/{id}/billing/ · getpricing/API keyThis VM's current billing breakdown, and its live hourly rate at the current spec.
Example response · 200
{ "hourly_rate": 0.0248, "monthly_estimate": 17.86 }/api/vps/{id}/rebuild/API keyReinstall the OS, wiping the boot disk. Requires the VPS to be stopped first.
Request body
osid *intOS template id to install.new_pass *stringNew root/administrator password.conf_pass *stringMust match new_pass.Example response · 200
{ "message": "VPS rebuild has been started with Ubuntu 24.04." }/api/vps/{id}/available_os/API keyOS templates installable on this VPS's region/node.
Example response · 200
[ { "id": 3, "name": "Ubuntu 24.04", "distro": "ubuntu" } ]/api/vps/{id}/build_log/API keyTail-pollable step-by-step provisioning log (regional-backed nodes only).
Query params
afterstringCursor from the previous poll — omit for the first call.Example response · 200
{ "entries": [ { "step": "Allocating disk", "status": "done" } ], "cursor": "18" }/api/vps/{id}/listbackup/ · getbackupcost/API keyList existing backups and their storage cost. Backups are supported on Virtualizor-backed nodes only — regional-backed VMs return a 400 explaining this.
Example response · 200
[ { "id": 9, "created_at": "2026-08-01T00:00:00Z", "size_gb": 40 } ]/api/vps/{id}/createbackup/ · restorebackup/ · deletebackup/API keyCreate a new backup, or restore/delete an existing one (restorebackup and deletebackup take backup_id). Async — dispatches a task.
Request body
backup_idintRequired for restorebackup and deletebackup.Example response · 200
{ "message": "Restore process has been started." }/api/vps/{id}/list_disk/ · add_disk/ · delete_disk/{disk_uuid}/API keyBundled disk management. add_disk/delete_disk are blocked (400) on regional-backed VMs — use the standalone Volumes endpoints below instead for those.
Example response · 200
{ "message": "Use the Volumes page to manage disks on this VM." }/api/vps/{id}/add_ip/ · delete_ip/{ip_addr}/API keyBundled IP management — same regional restriction as add_disk above; use the standalone IP Addresses endpoints for regional-backed VMs.
Example response · 200
{ "message": "IP added." }/api/vps/{id}/attachable_ips/API keyYour own unattached IPs eligible to attach to this VPS.
Example response · 200
[ { "id": 91, "ip": "198.51.100.7" } ]/api/vps/{id}/ip_address/{ip}/API keySet reverse DNS (rDNS) for one of this VPS's addresses.
Request body
rdns *stringThe hostname to point rDNS at.Example response · 200
{ "message": "rDNS updated." }Compute
SSH Keys
Public keys saved to your account, reusable across any VPS you deploy.
/api/sshkey/API keyList your saved SSH keys.
Example response · 200
[ { "id": 5, "title": "laptop", "ssh_key": "ssh-ed25519 AAAA...", "created_at": "2026-05-01" } ]/api/sshkey/API keySave a new public key. Validated as a real SSH public key; duplicate titles on your account are rejected.
Request body
title *stringA name for this key, unique to your account.ssh_key *stringThe public key contents (e.g. id_ed25519.pub).Example response · 201
{ "id": 6, "title": "deploy-key", "ssh_key": "ssh-ed25519 AAAA...", "created_at": "2026-08-17" }/api/sshkey/{id}/API keyRemove a saved key.
Example response · 204
{}Compute
Firewall Rules
Per-VPS firewall rules, nested under the VPS resource.
/api/vps/{vps_id}/firewall_rules/API keyList the custom rules on one VPS.
Example response · 200
[ { "id": 2, "direction": "in", "ip_type": "ipv4", "decision": "accept", "protocol": "tcp", "src_port": "any", "dest_port": "22", "source": "0.0.0.0/0" } ]/api/vps/{vps_id}/firewall_rules/updateRules/API keyReplace the full rule set for this VPS in one call — rules present in the array are kept/created, anything missing from it is deleted (a diff, not an append).
Request body
(array body) *arrayEach item: { direction, ip_type, decision, protocol, src_port, dest_port, source }.Example response · 200
{ "message": "Firewall rules updated." }Networking
IP Addresses
Standalone IPv4 addresses — purchased independently of any VM, attached or detached whenever you need to.
/api/ip-addresses/API keyList every IP you own.
Query params
popintFilter to one region.unattachedbooltrue/false — filter to addresses with (or without) a VPS attached.Example response · 200
[ { "id": 91, "ip": "198.51.100.7", "vps": null, "status": "active", "purchased_at": "2026-06-01T00:00:00Z" } ]/api/ip-addresses/available/API keyAvailability and price for purchasing new addresses in a region — the picker source before calling purchase.
Query params
pop *intRegion id.Example response · 200
{ "pop": 2, "pop_city": "Frankfurt", "monthly_price": 1.6, "available_count": 42, "remaining_quota": 8 }/api/ip-addresses/purchase/API keyBuy one or more addresses in a region. Synchronous — addresses are reserved and returned in the same response. Max 20 per call.
Request body
pop *intRegion id.quantity *int1–20.Example response · 201
[ { "id": 104, "ip": "198.51.100.88", "status": "active" } ]/api/ip-addresses/{id}/attachable_vps/API keyYour VMs in the same region as this IP, eligible to attach it to.
Example response · 200
[ { "id": 412, "hostname": "web-01", "is_stopped": false } ]/api/ip-addresses/{id}/attach/API keyAttach this address to one of your VMs (same region only). Synchronous.
Request body
vps *intTarget VPS id.Example response · 200
{ "id": 91, "ip": "198.51.100.7", "vps": 412 }/api/ip-addresses/{id}/detach/API keyDetach this address from whatever VM it's attached to. Synchronous.
Example response · 200
{ "id": 91, "ip": "198.51.100.7", "vps": null }/api/ip-addresses/{id}/API keyRelease an address for good — must be detached first.
Example response · 204
{ "message": "IP address released." }Storage
Volumes
Standalone block storage — including boot volumes. Create one, install an OS on it, set it as a VM's boot disk, or move it to a different VM entirely.
Every mutating volume action below is asynchronous — it returns 202 with a task_id immediately, not the finished result. Poll task-status until status is "completed" (or "failed").
/api/volumes/API keyList every volume you own.
Query params
popintFilter to one region.unattachedboolFilter to volumes with (or without) a VPS attached.Example response · 200
[ { "id": 55, "name": "vol-a83f", "size": 160, "kind": "data", "vps": null, "os_name": null } ]/api/volumes/available/API keyPrice and eligibility for purchasing in a region — including whether this region requires naming an existing VPS at purchase time (non-fleet regions).
Query params
pop *intRegion id.Example response · 200
{ "pop": 2, "monthly_price_per_gb": 0.0144, "allowed_sizes_gb": [20, 40, 80, 160, 320, 520], "is_fleet_backed": true, "eligible_vps": [] }/api/volumes/purchase/API keyCreate a new volume — a data disk, or a boot volume with nothing installed on it yet.
Request body
pop *intRegion id.size_gb *intOne of the allowed sizes for this region.kindstring"data" (default) or "boot".vps_idintRequired for a data disk in a non-fleet region — pins the volume to that VM's own node.Example response · 202
{ "task_id": 881, "status": "pending" }/api/volumes/task-status/{task_id}/API keyPoll the result of purchase/attach/detach/destroy/install_os/resize/set_as_boot.
Example response · 200
{ "task_id": 881, "type": "volume_create", "status": "completed", "volume": { "id": 55, "size": 160 } }/api/volumes/{id}/attachable_vps/API keyYour VMs in the same region as this volume, annotated with whether each already has a boot volume.
Example response · 200
[ { "id": 412, "hostname": "web-01", "is_stopped": true, "has_boot_volume": false } ]/api/volumes/{id}/available_os/API keyOS templates installable on this volume (fleet-backed volumes only). Always a plain list, empty if nothing's installable right now.
Example response · 200
[ { "id": 3, "name": "Ubuntu 24.04", "distro": "ubuntu" } ]/api/volumes/{id}/install_os/API keyInstall an OS onto this volume, overwriting whatever was on it. Must be detached first. Windows requires a password; others need an SSH key, a password, or both.
Request body
operating_system *intOS id from available_os.ssh_public_keystringPublic key to install (non-Windows).root_passwordstringRoot/administrator password.Example response · 202
{ "task_id": 882, "status": "pending" }/api/volumes/{id}/resize/API keyGrow a volume live — works whether it's attached or not. Grow-only, next size up from the allowed tiers.
Request body
size_gb *intMust be larger than the current size.Example response · 202
{ "task_id": 883, "status": "pending" }/api/volumes/{id}/attach/API keyAttach this volume to a VM as a plain (non-boot) data disk. Same region only.
Request body
vps *intTarget VPS id.Example response · 202
{ "task_id": 884, "status": "pending" }/api/volumes/{id}/set_as_boot/API keySet this volume as a VM's boot disk — swaps out whatever boot volume it currently has, if any. Fleet-backed volumes only; the target VM must be stopped.
Request body
vps *intTarget VPS id.Example response · 202
{ "task_id": 885, "status": "pending" }/api/volumes/{id}/detach/API keyDetach this volume from whatever VM it's on — including a boot volume.
Example response · 202
{ "task_id": 886, "status": "pending" }/api/volumes/{id}/API keyRelease a volume for good — must be detached first.
Example response · 202
{ "task_id": 887, "status": "pending" }Reference data
Regions & Operating Systems
Read-only catalogs — the region and OS ids every create/deploy request above needs, plus any maintenance affecting your own infrastructure.
/api/pop/API keyList every region, with per-resource hourly pricing (vCPU/RAM/disk/traffic hourly, IP monthly).
Example response · 200
[ { "id": 2, "city": "Frankfurt", "country": "Germany", "cpu_price": 0.004, "ram_price": 0.00035, "disk_price": 0.00002, "ip_price": 1.6 } ]/api/os/API keyList every installable operating system template.
Example response · 200
[ { "id": 3, "name": "Ubuntu 24.04", "distro": "ubuntu", "virt_type": "kvm" } ]/api/maintenance-events/paginatedAPI keyPublished maintenance events relevant to you — sitewide notices, or ones touching a region/VM you own. Recently-ended events stay visible for 24h.
Example response · 200
{
"count": 1, "next": null, "previous": null,
"results": [ { "id": 12, "title": "Frankfurt network maintenance", "event_type": "network", "status": "scheduled", "start_time": "2026-08-20T02:00:00Z", "end_time": "2026-08-20T04:00:00Z", "scope": "pop", "affected_pops": [ { "id": 2, "city": "Frankfurt", "country": "Germany" } ], "affected_vps": [], "is_published": true } ]
}Account
Account, Billing & Activity
Your plan tier and quota, profile, invoices, transactions, usage cost breakdown, and account activity log.
/api/account/quota/API keyYour plan tier, per-resource limits, current usage, balance, and what the next tier unlocks. See the full tier ladder →
Example response · 200
{
"current_tier": "starter", "is_custom": false,
"limits": { "max_cores": 32, "max_ram_gb": 64, "max_disk_gb": 1040, "max_ips": 8 },
"usage": { "cores": 4, "ram_gb": 8, "disk_gb": 160, "ips": 1 },
"next_tier": "growth", "balance": 42.18,
"currency": "USD", "currency_symbol": "$"
}/api/users/me/API keyYour account profile and balance.
Example response · 200
{
"id": 88, "email": "[email protected]", "name": "Jane Doe",
"balance": 42.18, "currency": { "short_name": "USD", "symbol": "$" },
"country": "Germany", "company_name": null, "is_verified": true,
"kyc_verified": false, "is_2fa": false
}/api/users/me/API keyUpdate your profile. A read-only key can GET this but not PATCH it.
Request body
namestringDisplay name.addressstringBilling address.phone_numberstringcountrystringcitystringzip_codestringcompany_namestringtax_idstringExample response · 200
{ "id": 88, "name": "Jane Doe", "...": "..." }/api/invoices/paginatedAPI keyList your invoices, paginated 5 per page.
Query params
searchstringSearch by id, status, amount, or line-item name/hostname.orderingstringe.g. -created_at, total_amount.Example response · 200
{
"count": 1, "next": null, "previous": null,
"results": [
{ "id": 501, "created_at": "2026-08-01", "due_date": "2026-08-08",
"status": "paid", "total_amount": "17.86",
"currency": { "symbol": "$", "short_name": "USD" },
"items": [ { "id": 1, "name": "web-01 compute", "hours": 720, "price": "17.86", "resource_type": "compute" } ] }
]
}/api/transactions/paginatedAPI keyList your balance transactions (top-ups, invoice payments, refunds), paginated 5 per page. balance_before/balance_after are staff-only, not shown to a customer's own key.
Example response · 200
{
"count": 1, "next": null, "previous": null,
"results": [
{ "id": "b6e2...", "transaction_type": "topup", "currency": { "short_name": "USD" },
"amount": "50.00", "source": "razorpay", "status": "completed", "created_at": "2026-08-01T10:00:00Z" }
]
}/api/usage-billing/overview/API keyLive unbilled usage (compute/storage/network/bandwidth/backups) plus already-billed cost by month and category over a date range.
Query params
start / enddateYYYY-MM-DD, both optional, both required if either is given. Bounds the billed section only — unbilled is always "as of now." Defaults to the trailing 6 months.Example response · 200
{
"currency": { "symbol": "$", "short_name": "USD" },
"unbilled": { "summary": { "compute": 4.2, "storage": 0.6, "network": 0.1, "bandwidth": 0, "backups": 0, "total": 4.9 }, "usage_rows": [], "backup_rows": [], "bandwidth_rows": [], "error_rows": [] },
"billed": { "start": "2026-03-01", "end": "2026-08-17", "by_category": { "compute": 82.1, "storage": 6.4, "network": 3.2, "other": 0, "total": 91.7 }, "by_month": [], "invoice_count": 5 }
}/api/usage-billing/line-items/API keyRow-by-row usage line items backing the overview above, in either direction.
Query params
statusstring"unbilled" (default) or "billed".start / enddateYYYY-MM-DD — billed only, both required if either given. Defaults to the trailing 6 months.categorystring"all" (default), "compute", "storage", or "network".searchstringHostname substring.page / page_sizeintpage_size default 10, capped at 100 — a different pagination shape than the DRF-paginated endpoints elsewhere on this page.Example response · 200
{
"count": 1, "page": 1, "page_size": 10, "num_pages": 1,
"results": [ { "id": "usage-9001", "type": "Compute", "category": "compute", "hostname": "web-01", "cost": 4.2, "date": "2026-08-17", "billed": false } ]
}/api/activity/paginatedAPI keyYour account activity log, paginated 5 per page.
Query params
searchstringSearch action/description/ip_address/user_agent.Example response · 200
{
"count": 1, "next": null, "previous": null,
"results": [ { "id": 9001, "action": "vps.create", "description": "Created VPS web-01", "vps": 412, "ip_address": "198.51.100.10", "timestamp": "2026-08-17T09:00:00Z" } ]
}/api/api_keys/Dashboard onlyList or create your API keys. Dashboard session only — a key can't be used to manage keys.
Request body
name *stringA label for this key.scopestring"read_only" (default) or "read_write".allowed_ipstringOptional — lock this key to one source IP.Example response · 200
{ "id": 4, "name": "ci-deploy", "key": "b0e2b6b0-...-9f21", "scope": "read_only", "is_active": true, "allowed_ip": null, "created_at": "2026-08-17T09:00:00Z" }/api/api_keys/{id}/Dashboard onlyUpdate (e.g. deactivate, widen scope, set allowed_ip) or permanently delete a key. Dashboard session only.
Example response · 200
{ "id": 4, "is_active": false }Support
Support Tickets
Open and reply to support tickets — the same conversation the dashboard shows, reachable from your own code too.
/api/departments/API keyList support departments, to pick one when opening a ticket.
Example response · 200
[ { "id": 1, "name": "Billing" }, { "id": 2, "name": "Technical" } ]/api/tickets/paginatedAPI keyList your tickets, paginated 5 per page.
Example response · 200
{
"count": 1, "next": null, "previous": null,
"results": [ { "id": 77, "subject": "VM unreachable", "priority": "high", "department": { "id": 2, "name": "Technical" }, "vps": null, "status": "open", "assigned_to": null, "created_at": "2 hours ago" } ]
}/api/tickets/API keyOpen a new ticket.
Request body
subject *stringdepartment_id *intFrom GET /api/departments/.messagestringOpening message.prioritystringe.g. "low" / "medium" / "high".vps_idintOptional — associate this ticket with one of your VMs.Example response · 201
{ "id": 78, "subject": "VM unreachable", "status": "open", "...": "..." }/api/tickets/{id}/API keyRetrieve one ticket, including its full reply thread embedded (unpaginated).
Example response · 200
{ "id": 77, "subject": "VM unreachable", "replies": [ { "id": 1, "message": "...", "replied_by": "...", "created_at": "2 hours ago" } ] }/api/tickets/{ticket_id}/replies/paginatedAPI keyThe reply thread, paginated — the same data as the ticket detail's embedded replies field, in pages.
Example response · 200
{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "message": "We're looking into it.", "replied_by": "...", "created_at": "1 hour ago", "images": [] } ] }/api/tickets/{ticket_id}/replies/API keyPost a reply on a ticket you have access to.
Request body
message *stringuploaded_imagesfile[]Up to 5 images, 5 MB each — send as multipart/form-data, not JSON, when attaching any.Example response · 201
{ "id": 2, "message": "Thanks, still seeing it.", "created_at": "just now" }Conventions
Errors & status codes
Most errors return a plain {"message": "..."} body. A few validation failures on POST/PATCH instead return Django REST Framework's own per-field shape — {"field_name": ["error"]} — worth handling both rather than assuming one is universal.
Most list endpoints aren't paginated — a GET to a list route returns the full plain array of everything you own. A handful (marked paginated below — VPS, Invoices, Transactions, Activity Log, Tickets) return DRF's wrapped {"count", "next", "previous", "results"} shape instead, page_size=5 by default — pass ?page_size= (up to 100000) to get everything back in one call.
Scope
Dashboard-only endpoints
These exist in the same API but only accept a logged-in dashboard session today, not an API key — listed here so a script doesn't waste time on a silent 401.
Get started
Generate your first API key
$10 free credit on signup. Create a key from the dashboard and start scripting in minutes.