Automating IP Allocation with the IPCraft API
Every time someone provisions a new server, spins up a VM (virtual machine), or deploys a container, it needs an IP address. In small environments, a person picks the next available address from a spreadsheet or memory. In larger environments, that approach falls apart. Addresses get reused accidentally, documentation falls behind, and the gap between what the network looks like on paper and what it looks like in reality grows wider every week. The fix is to make IP allocation part of the same automated pipeline that provisions everything else. IPCraft's REST API (Representational State Transfer Application Programming Interface) makes that straightforward.
This tutorial walks through the building blocks: authenticating with an API key, querying subnets, finding available addresses, and registering new allocations. We'll start with plain curl commands so the mechanics are clear, then show how the same operations look inside Terraform and Ansible, two of the most widely used IaC (Infrastructure as Code) tools.
Before anything else, you'll need an API key. In the IPCraft app, go to Settings, scroll to API Keys, and click Generate Key. The key starts with ipc_ and is shown only once, so copy it somewhere safe. Every API request includes this key in an Authorization header. All endpoints live under https://api.ipcraft.io/api/v1.
The simplest useful operation is listing your subnets. This tells your automation what address space is available to work with. The response includes each subnet's ID, CIDR (Classless Inter-Domain Routing) block, name, and folder.
curl https://api.ipcraft.io/api/v1/subnets \
-H "Authorization: Bearer ipc_your_key_here"
The response is a JSON (JavaScript Object Notation) array of subnet objects. Each one has an id field you'll use in subsequent calls, a network field with the CIDR notation (like 10.0.1.0/24), and optional metadata like name, description, and gateway.
Once you know which subnet you want to allocate from, the next step is finding a free address. The first-free endpoint does exactly what it sounds like: it scans the subnet and returns the first IP address that hasn't been assigned yet. If the subnet is full, it returns null.
curl https://api.ipcraft.io/api/v1/subnets/{subnet_id}/first-free \
-H "Authorization: Bearer ipc_your_key_here"
# Response:
# { "ip": "10.0.1.5" }
With a free address in hand, you register it by creating an address record. The only required field is ip, but you'll typically want to include a hostname and description so the allocation is self-documenting. The status field accepts values like active, reserved, or dhcp. The owner field is free-text, useful for tagging which team or service owns the address.
curl -X POST https://api.ipcraft.io/api/v1/subnets/{subnet_id}/addresses \
-H "Authorization: Bearer ipc_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"ip": "10.0.1.5",
"hostname": "web-03.prod",
"description": "Web server, us-east-1a",
"status": "active",
"owner": "platform-team"
}'
That three-step pattern, list subnets, find a free IP, register it, is the core workflow. Everything else builds on top of it. You can also create subnets programmatically if your automation needs to carve out new address blocks. The only required field is network (the CIDR block). IPCraft automatically detects the parent subnet based on containment and rejects overlapping ranges.
curl -X POST https://api.ipcraft.io/api/v1/subnets \
-H "Authorization: Bearer ipc_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"network": "10.0.5.0/24",
"name": "Staging servers",
"section_id": "sec_001",
"gateway": "10.0.5.1"
}'
Now let's put this into a Terraform workflow. Terraform doesn't have a native IPCraft provider (yet), but it doesn't need one. The http data source and restapi provider handle REST APIs cleanly. The pattern below uses Terraform's http data source to fetch the next free IP, then passes it to a cloud provider resource. This means your Terraform plan always allocates a fresh, conflict-free address from IPCraft before creating the VM.
variable "ipcraft_api_key" {
type = string
sensitive = true
}
variable "subnet_id" {
type = string
default = "your-subnet-uuid"
}
# Step 1: Get the next available IP from IPCraft
data "http" "next_ip" {
url = "https://api.ipcraft.io/api/v1/subnets/${var.subnet_id}/first-free"
request_headers = {
Authorization = "Bearer ${var.ipcraft_api_key}"
}
}
locals {
next_ip = jsondecode(data.http.next_ip.response_body).ip
}
# Step 2: Register the address in IPCraft
resource "restapi_object" "ip_registration" {
path = "/api/v1/subnets/${var.subnet_id}/addresses"
data = jsonencode({
ip = local.next_ip
hostname = "web-${random_id.server.hex}.prod"
description = "Managed by Terraform"
status = "active"
owner = "platform-team"
})
}
# Step 3: Use the IP in your cloud resource
resource "hcloud_server" "web" {
name = "web-${random_id.server.hex}"
server_type = "cx22"
image = "ubuntu-24.04"
network {
network_id = hcloud_network.main.id
ip = local.next_ip
}
}
The Ansible approach is similar in spirit but uses a playbook instead of a plan. Ansible's uri module handles HTTP requests natively, so there's nothing extra to install. The playbook below fetches a free IP, registers it, then uses the address in a subsequent task. You could plug this into a larger playbook that configures the server, updates DNS, or notifies a monitoring system.
---
- name: Allocate IP from IPCraft
hosts: localhost
vars:
ipcraft_api: "https://api.ipcraft.io/api/v1"
ipcraft_key: "{{ vault_ipcraft_api_key }}"
subnet_id: "your-subnet-uuid"
tasks:
- name: Get next free IP
ansible.builtin.uri:
url: "{{ ipcraft_api }}/subnets/{{ subnet_id }}/first-free"
method: GET
headers:
Authorization: "Bearer {{ ipcraft_key }}"
return_content: true
register: free_ip_result
- name: Register the address
ansible.builtin.uri:
url: "{{ ipcraft_api }}/subnets/{{ subnet_id }}/addresses"
method: POST
headers:
Authorization: "Bearer {{ ipcraft_key }}"
Content-Type: "application/json"
body_format: json
body:
ip: "{{ free_ip_result.json.ip }}"
hostname: "db-replica-03.prod"
description: "Managed by Ansible"
status: "active"
owner: "dba-team"
status_code: 201
- name: Use the allocated IP
ansible.builtin.debug:
msg: "Allocated {{ free_ip_result.json.ip }} for db-replica-03"
A few practical notes worth keeping in mind. The first-free endpoint returns the next available address at the time of the request. If two automation runs hit it simultaneously, they could receive the same IP. The second one to call POST /addresses will get a 400 error because the address is already taken. The clean way to handle this is a retry loop: if the create fails with a duplicate, call first-free again and try the next one. In practice, this race condition is rare unless you're provisioning dozens of machines in parallel, but it's worth coding defensively.
API keys created on the free plan have read-only access, so you can experiment with the list and first-free endpoints without any risk of modifying data. Write operations (creating subnets, registering addresses) require a Pro plan or above. Rate limits vary by plan, from 10 requests per second on free up to 100 on enterprise. The API returns a 429 status code if you exceed the limit, and a 402 if you hit a plan limit like the maximum number of subnets or addresses.
The real payoff of API-driven IP management shows up over time. Every address that gets allocated through automation is automatically documented in IPCraft with a hostname, description, and owner. The activity log records who created it, when, and from which IP. When someone asks "what's using 10.0.1.47?" six months from now, the answer is already there. That feedback loop, where provisioning and documentation happen in the same step, is what closes the gap between the network as it exists and the network as it's documented. The full API reference, including endpoints for VLANs, VRFs (Virtual Routing and Forwarding instances), webhooks, and search, is available at ipcraft.io/docs/api.