← All Docs

API Reference

Complete REST API documentation for IPCraft. Manage folders, subnets, IP addresses, VLANs, VRFs, locations, webhooks, and your team programmatically.

Base URL

https://api.ipcraft.io

All endpoint paths below are relative to this base URL. All resource IDs are UUIDs.

Authentication

All requests except the Network Tools endpoints require authentication via one of two methods:

State-changing requests (POST, PATCH, DELETE) using a session cookie must include the X-Requested-With: XMLHttpRequest header for CSRF protection. API key requests do not need this header.

API keys carry read and/or write permissions (both by default). Two additional restrictions apply to write requests:

Example (API key)
curl https://api.ipcraft.io/api/v1/folders \
  -H "Authorization: Bearer ipc_abc123..."
Example (Session cookie with CSRF)
curl -X POST https://api.ipcraft.io/api/v1/folders \
  -H "Content-Type: application/json" \
  -H "X-Requested-With: XMLHttpRequest" \
  -b "ipcraft_session=..." \
  -d '{"name": "Production"}'

Errors & Plan Limits

All errors return a JSON object with an error key and an appropriate HTTP status code.

{
  "error": "subnet overlaps with existing subnet 10.0.0.0/16"
}
StatusMeaning
400Bad request — invalid input or validation error
401Unauthorized — missing or invalid credentials
402Payment required — plan limit exceeded
403Forbidden — insufficient permissions, CSRF failure, or free-plan API write
404Not found
429Rate limited — too many requests

Plan Limits

LimitFreePro ($29/mo)Teams ($199/mo)Enterprise
Users1210Custom
Folders5Unlimited*Unlimited*Custom
Subnets100Unlimited*Unlimited*Custom
IP Addresses1,5005,00010,000Custom
VLANs10Unlimited*Unlimited*Custom
VRFs5Unlimited*Unlimited*Custom
Locations5Unlimited*Unlimited*Custom
Webhooks25050
API Keys5100100Custom
API AccessRead-onlyRead/WriteRead/WriteRead/Write
Rate Limit (per user)10 req/s50 req/s50 req/sCustom
Activity Log7 days30 days90 daysCustom

* Unlimited features are subject to the fair use limits described in the Terms of Service.

Requests that would exceed your plan limits return 402. Authenticated requests are rate-limited per user based on your plan (enforced over a 1-minute window); responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers.

Auth Endpoints

IPCraft is passwordless. Sign in with a magic link or Google OAuth — there are no register or password endpoints. Accounts and organizations are created automatically on first sign-in. Auth endpoints are rate-limited to 5 requests per minute per IP.

POST /api/v1/auth/send-link

Send a passwordless sign-in link to the given email address. The link expires after 15 minutes. Limited to 3 links per email address per 15 minutes.

Request body
{
  "email": "john@example.com"
}
Response
{
  "message": "sign-in link sent to john@example.com"
}

Verify Magic Link

GET /api/v1/auth/verify?token=<token>

Verify the magic link token from the email. This endpoint is opened by the browser, not called programmatically: on success it sets the ipcraft_session cookie and redirects (307) to the app. On first sign-in it creates the user and their organization. Invalid or expired tokens redirect to the login page with an error.

Google OAuth

GET /api/v1/auth/oauth/google

Start the Google OAuth sign-in flow. Redirects to Google; the callback (/api/v1/auth/oauth/google/callback) sets the session cookie and redirects to the app. Browser-only — not for programmatic use.

Get Current User

GET /api/v1/auth/me

Returns the authenticated user, their organization, and their role (admin, editor, or viewer). Requires auth.

Response
{
  "user": {
    "id": "7f3e9a12-4b6c-4d2e-9f01-8a5b3c7d1e42",
    "email": "john@example.com",
    "name": "John Smith",
    "email_verified": true,
    "created_at": "2026-03-01T12:00:00Z"
  },
  "organization": {
    "id": "c2a8f5d0-1e3b-4a7c-8d9e-0f1a2b3c4d5e",
    "name": "John Smith's Org",
    "slug": "john-smiths-org",
    "plan": "free",
    "created_at": "2026-03-01T12:00:00Z",
    "updated_at": "2026-03-01T12:00:00Z"
  },
  "role": "admin"
}

Logout

POST /api/v1/auth/logout

Destroys the current session and clears the session cookie. No request body required.

Response
200 OK

Delete Account

DELETE /api/v1/auth/account

Permanently deletes the authenticated user's account and clears the session.

Response
{
  "message": "account deleted"
}

Folders

Folders are top-level organizational containers for grouping subnets (e.g., "Production", "Development").

List Folders

GET /api/v1/folders

Returns all folders for the authenticated organization.

Response
[
  {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "Production",
    "description": "Production network ranges",
    "sort_order": 0,
    "created_at": "2026-03-01T12:00:00Z"
  }
]

Create Folder

POST /api/v1/folders

Create a new folder.

Request body
{
  "name": "Production",
  "description": "Production network ranges",
  "sort_order": 0
}

Only name is required. description and sort_order are optional.

Response

Returns 201 with the created folder object.

Get Folder

GET /api/v1/folders/{id}

Returns a single folder by ID.

Update Folder

PATCH /api/v1/folders/{id}

Update a folder. All fields are optional — only provided fields are changed.

Request body
{
  "name": "Prod (US-East)",
  "description": "US East production ranges",
  "sort_order": 1
}
Response
200 OK

Delete Folder

DELETE /api/v1/folders/{id}

Delete a folder. Subnets in the folder are kept and become unfoldered.

Response
204 No Content

Subnets

Subnets represent CIDR network ranges. They form a hierarchy — creating a subnet within an existing range automatically nests it as a child. Overlap detection prevents conflicting ranges within the same VRF.

List Subnets

GET /api/v1/subnets?folder_id={folder_id}

List all subnets, optionally filtered by folder. Includes utilization statistics and resolved VLAN/VRF/location/folder names. Not paginated.

Query parameters
  • folder_id — filter by folder (optional)
Response
[
  {
    "id": "b4c5d6e7-f8a9-0123-bcde-f01234567890",
    "folder_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "network": "10.0.0.0/16",
    "name": "Core network",
    "description": "Main production range",
    "vlan_id": null,
    "vrf_id": null,
    "location_id": null,
    "is_pool": false,
    "gateway": "10.0.0.1",
    "dns_primary": "10.0.0.53",
    "dns_secondary": null,
    "scan_enabled": false,
    "tracked": true,
    "utilization": 0.12,
    "used_count": 7860,
    "total_count": 65534,
    "folder_name": "Production",
    "created_at": "2026-03-01T12:00:00Z",
    "updated_at": "2026-03-01T12:00:00Z"
  }
]

Nullable/empty fields (parent_id, vlan_number, vrf_name, location_name, custom_fields, counts) are omitted when unset.

Create Subnet

POST /api/v1/subnets

Create a new subnet. The parent is auto-detected based on CIDR containment (and existing subnets inside the new range are re-parented under it). Partial overlaps within the same VRF are rejected with 400; parent/child containment is allowed.

Request body
{
  "network": "10.0.1.0/24",
  "name": "Web servers",
  "description": "Web tier subnet",
  "folder_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "vlan_id": null,
  "vrf_id": null,
  "location_id": null,
  "gateway": "10.0.1.1",
  "dns_primary": "10.0.0.53",
  "dns_secondary": null,
  "is_pool": false,
  "tracked": true
}

Only network is required. All other fields are optional. tracked defaults to true when omitted; send false to create a structure-only container subnet that's excluded from utilization stats.

Response

Returns 201 with the created subnet object. If the new subnet (created without a VRF) overlaps ranges that exist in other VRFs, the response is wrapped as {"subnet": {...}, "warnings": [{"id", "network", "name", "vrf_name"}]} — the create still succeeds.

Get Subnet

GET /api/v1/subnets/{id}

Returns a subnet with utilization statistics and its child subnets.

Response
{
  "id": "b4c5d6e7-f8a9-0123-bcde-f01234567890",
  "network": "10.0.0.0/16",
  "name": "Core network",
  "utilization": 0.12,
  "children": [
    {
      "id": "c5d6e7f8-a9b0-1234-cdef-012345678901",
      "network": "10.0.1.0/24",
      "name": "Web servers"
    }
  ]
}

Update Subnet

PATCH /api/v1/subnets/{id}

Update subnet metadata. All fields are optional — only provided fields are changed. The network itself cannot be changed.

Request body
{
  "name": "Web servers (prod)",
  "description": "Production web tier",
  "folder_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "location_id": null,
  "vlan": "120",
  "vrf": "CORP",
  "is_pool": true,
  "tracked": false,
  "gateway": "10.0.1.1",
  "dns_primary": "10.0.0.53",
  "dns_secondary": null,
  "custom_fields": { "rack": "R12" }
}

Note: unlike create, update takes vlan as a VLAN number (1–4094) and vrf as a VRF name; both are auto-created if they don't exist, and an empty string clears the assignment.

Response
200 OK

Delete Subnet

DELETE /api/v1/subnets/{id}

Delete a subnet. Cascades to all child subnets and addresses.

This action is irreversible. All child subnets and their addresses will also be deleted.
Response
204 No Content

Split Subnet

POST /api/v1/subnets/{id}/split

Split a subnet into equal parts. The number of parts must be a power of 2 (2, 4, 8, ... up to 1024). The new subnets inherit the original's folder, parent, name, VRF, and pool flag.

Splitting deletes the original subnet and all of its IP assignments, then creates the new smaller subnets. Export any assignments you need before splitting.
Request body
{
  "parts": 4
}
Response
[
  { "id": "d6e7f8a9-...", "network": "10.0.0.0/26" },
  { "id": "e7f8a9b0-...", "network": "10.0.0.64/26" },
  { "id": "f8a9b0c1-...", "network": "10.0.0.128/26" },
  { "id": "a9b0c1d2-...", "network": "10.0.0.192/26" }
]

Subnet Usage

GET /api/v1/subnets/{id}/usage

Returns utilization statistics for a subnet.

Response
{
  "subnet_id": "c5d6e7f8-a9b0-1234-cdef-012345678901",
  "network": "10.0.1.0/24",
  "used": 42,
  "total": 254,
  "utilization": 0.165
}

First Free IP

GET /api/v1/subnets/{id}/first-free

Returns the first available IP address in the subnet, or null if the subnet is full.

Response
{
  "ip": "10.0.1.5"
}
Response (full subnet)
{
  "ip": null,
  "message": "no available addresses"
}

Free Blocks

GET /api/v1/subnets/{id}/free-blocks?prefix={prefix}&limit={limit}

Finds unallocated child blocks of a requested size inside a subnet — the subnet-level counterpart of First Free IP. Returns the lowest free CIDR blocks of prefix length prefix that don't collide with any existing subnet in the same VRF (nested children are handled; supernets are ignored). prefix must be longer than the subnet's own prefix. limit caps the candidates returned (default 5, max 50); total is the count of all free blocks, and capped is true when the total hit the reporting cap of 1,000,000 (large IPv6 containers).

Response
{
  "subnet_id": "c5d6e7f8-a9b0-1234-cdef-012345678901",
  "network": "10.0.0.0/16",
  "prefix": 24,
  "blocks": ["10.0.4.0/24", "10.0.5.0/24"],
  "total": 253,
  "capped": false
}

List Overlaps

GET /api/v1/subnets/overlaps

Returns all pairs of subnets whose CIDR ranges overlap, classified by overlap type: duplicate (same CIDR in different VRFs), containment (parent/child), or partial.

Response
[
  {
    "subnet_a": { "id": "...", "network": "10.0.0.0/16", "vrf_name": "CORP" },
    "subnet_b": { "id": "...", "network": "10.0.0.0/16" },
    "type": "duplicate"
  }
]

Addresses

Individual IP address records within a subnet. Each address is validated against the parent subnet's CIDR range. Valid status values are assigned, reserved, d-dhcp (dynamic DHCP), m-dhcp (manual DHCP), and gateway.

List Addresses

GET /api/v1/subnets/{id}/addresses?limit={limit}&offset={offset}

List IP addresses in a subnet. Paginated: limit defaults to 1,000 (also the maximum); the total count is returned in the X-Total-Count response header.

Response
[
  {
    "id": "0a1b2c3d-4e5f-6789-0abc-def123456789",
    "subnet_id": "c5d6e7f8-a9b0-1234-cdef-012345678901",
    "ip": "10.0.1.10",
    "hostname": "web-01.prod",
    "mac_address": "00:1a:2b:3c:4d:5e",
    "description": "Primary web server",
    "status": "assigned",
    "owner": "platform-team",
    "created_at": "2026-03-01T12:00:00Z",
    "updated_at": "2026-03-01T12:00:00Z"
  }
]

Optional fields (ptr_record, custom_fields, last_seen) are omitted when unset.

Create Address

POST /api/v1/subnets/{id}/addresses

Assign an IP address within a subnet. The IP must fall inside the subnet's CIDR range and not already be assigned.

Request body
{
  "ip": "10.0.1.10",
  "hostname": "web-01.prod",
  "mac_address": "00:1A:2B:3C:4D:5E",
  "description": "Primary web server",
  "status": "assigned",
  "owner": "platform-team"
}

Only ip is required. status defaults to assigned.

Response

Returns 201 with the created address object.

Batch Create

POST /api/v1/subnets/{id}/addresses/batch-create

Assign up to 500 IPs in one request (e.g., reserve a range). status applies to all IPs and defaults to reserved.

Request body
{
  "ips": ["10.0.1.20", "10.0.1.21", "10.0.1.22"],
  "status": "reserved"
}
Response
{
  "created": 3
}

Get Address

GET /api/v1/addresses/{id}

Returns a single address by ID (same shape as the list items above).

Update Address

PATCH /api/v1/addresses/{id}

Update address metadata. All fields are optional. The IP itself cannot be changed.

Request body
{
  "hostname": "web-01a.prod",
  "mac_address": "00:1A:2B:3C:4D:5F",
  "description": "Migrated web server",
  "status": "assigned",
  "owner": "platform-team",
  "custom_fields": { "asset_tag": "A-1042" }
}
Response
200 OK

Delete Address

DELETE /api/v1/addresses/{id}

Remove an IP address assignment (release the IP).

Response
204 No Content

Batch Update

POST /api/v1/addresses/batch-update

Update the status and/or owner of up to 500 addresses at once.

Request body
{
  "ids": ["0a1b2c3d-...", "1b2c3d4e-..."],
  "status": "reserved",
  "owner": "network-team"
}
Response
{
  "updated": 2
}

Batch Delete

POST /api/v1/addresses/batch-delete

Release up to 500 addresses at once.

Request body
{
  "ids": ["0a1b2c3d-...", "1b2c3d4e-..."]
}
Response
{
  "deleted": 2
}

VLANs

Track VLAN assignments alongside your subnets. Standard CRUD:

GET /api/v1/vlans  ·  POST /api/v1/vlans  ·  GET/PATCH/DELETE /api/v1/vlans/{id}

A VLAN has a vlan_number (1–4094, required on create), optional name, description, and location_id. List responses include a computed subnet_count.

Example object
{
  "id": "2c3d4e5f-6789-0abc-def1-234567890abc",
  "vlan_number": 120,
  "name": "Web tier",
  "description": "Production web VLAN",
  "subnet_count": 3,
  "created_at": "2026-03-01T12:00:00Z"
}

VRFs

VRFs (virtual routing and forwarding instances) let the same CIDR exist more than once — overlap detection is scoped per VRF. Standard CRUD:

GET /api/v1/vrfs  ·  POST /api/v1/vrfs  ·  GET/PATCH/DELETE /api/v1/vrfs/{id}

A VRF has a name (required on create), optional rd (route distinguisher, e.g. 65000:1) and description. List responses include a computed subnet_count.

Example object
{
  "id": "3d4e5f67-890a-bcde-f123-4567890abcde",
  "name": "CORP",
  "rd": "65000:1",
  "subnet_count": 12,
  "created_at": "2026-03-01T12:00:00Z"
}

Locations

Physical sites (offices, datacenters) that subnets and VLANs can be tagged with. Standard CRUD:

GET /api/v1/locations  ·  POST /api/v1/locations  ·  GET/PATCH/DELETE /api/v1/locations/{id}

A location has a name (required on create) and optional address. List responses include computed subnet_count and vlan_count.

Example object
{
  "id": "4e5f6789-0abc-def1-2345-67890abcdef1",
  "name": "Datacenter US-East",
  "address": "Ashburn, VA",
  "subnet_count": 8,
  "vlan_count": 4,
  "created_at": "2026-03-01T12:00:00Z"
}

Webhooks

Subscribe to real-time event notifications. See the Webhooks guide for setup instructions and signature verification.

List Webhooks

GET /api/v1/webhooks

List all webhook subscriptions. The signing secret is never included in list responses.

Response
[
  {
    "id": "5f678901-abcd-ef12-3456-7890abcdef12",
    "url": "https://example.com/webhooks/ipcraft",
    "description": "CMDB sync",
    "event_types": ["subnet.created", "subnet.deleted"],
    "is_active": true,
    "failure_count": 0,
    "created_at": "2026-03-21T16:00:00Z"
  }
]

Create Webhook

POST /api/v1/webhooks

Create a new webhook subscription. The URL must use HTTPS. Returns the signing secret — store it securely, it is only shown once.

Request body
{
  "url": "https://example.com/webhooks/ipcraft",
  "description": "CMDB sync",
  "event_types": ["subnet.created", "subnet.deleted"]
}

Leave event_types empty ([]) to receive all event types.

Response
{
  "id": "5f678901-abcd-ef12-3456-7890abcdef12",
  "url": "https://example.com/webhooks/ipcraft",
  "secret": "whsec_a1b2c3d4e5f6...",
  "description": "CMDB sync",
  "event_types": ["subnet.created", "subnet.deleted"],
  "is_active": true
}
Copy the secret value immediately. It cannot be retrieved again. Use it to verify webhook signatures.

Get Webhook

GET /api/v1/webhooks/{id}

Get a single webhook subscription. The signing secret is not included.

Update Webhook

PATCH /api/v1/webhooks/{id}

Update a webhook's URL, description, event types, or active status.

Request body
{
  "url": "https://new-endpoint.example.com/hook",
  "event_types": ["address.created"],
  "is_active": true
}
Response
200 OK

Delete Webhook

DELETE /api/v1/webhooks/{id}

Permanently delete a webhook subscription and all its delivery history.

Response
204 No Content

List Deliveries

GET /api/v1/webhooks/{id}/deliveries

List recent delivery attempts for a webhook. Supports ?limit= (default 50, max 100) and ?offset= query parameters. Delivery records are retained for 7 days.

Response
[
  {
    "id": "6789012a-bcde-f123-4567-890abcdef123",
    "event_type": "subnet.created",
    "response_status": 200,
    "duration_ms": 142,
    "attempt": 1,
    "delivered_at": "2026-03-21T16:30:00Z"
  }
]

Test Ping

POST /api/v1/webhooks/{id}/test

Send a test ping event to the webhook endpoint. Returns the delivery result so you can verify connectivity.

Response
{
  "event_type": "ping",
  "response_status": 200,
  "duration_ms": 95,
  "attempt": 1
}

Returns 502 if the endpoint is unreachable or returns a non-2xx status.

Rotate Secret

POST /api/v1/webhooks/{id}/rotate-secret

Generate a new signing secret for the webhook. The old secret stops working immediately.

Response
{
  "secret": "whsec_new_secret_value..."
}
Update your endpoint to use the new secret before rotating, or you'll miss events during the transition.

API Keys

Manage API keys for programmatic access. The full key is only shown once at creation time.

List Keys

GET /api/v1/api-keys

List all API keys. Only the key prefix is shown — the full key is never returned after creation.

Response
[
  {
    "id": "789012ab-cdef-1234-5678-90abcdef1234",
    "name": "Ansible automation",
    "key_prefix": "ipc_a1b2c3d4",
    "permissions": ["read", "write"],
    "created_at": "2026-03-01T12:00:00Z",
    "last_used_at": "2026-03-14T09:15:00Z"
  }
]

Create Key

POST /api/v1/api-keys

Generate a new API key. The full key is only returned in this response — store it securely.

Request body
{
  "name": "Ansible automation",
  "permissions": ["read", "write"]
}

permissions is optional and defaults to ["read", "write"]. Use ["read"] for a read-only key.

Response
{
  "id": "789012ab-cdef-1234-5678-90abcdef1234",
  "name": "Ansible automation",
  "key": "ipc_a1b2c3d4e5f6...(68 chars total)",
  "key_prefix": "ipc_a1b2c3d4",
  "permissions": ["read", "write"],
  "created_at": "2026-03-01T12:00:00Z"
}
Copy the key value immediately. It cannot be retrieved again.

Delete Key

DELETE /api/v1/api-keys/{id}

Revoke an API key. Any requests using this key will immediately start returning 401.

Response
204 No Content

Team

Manage organization members and invitations (Teams and Enterprise plans). Roles are admin, editor, and viewer.

List Members

GET /api/v1/team/members
Response
[
  {
    "user_id": "7f3e9a12-4b6c-4d2e-9f01-8a5b3c7d1e42",
    "email": "john@example.com",
    "name": "John Smith",
    "role": "admin",
    "accepted_at": "2026-03-01T12:00:00Z"
  }
]

Update Member Role

PATCH /api/v1/team/members/{user_id}
Request body
{
  "role": "editor"
}
Response
{
  "message": "role updated"
}

Remove Member

DELETE /api/v1/team/members/{user_id}

Remove a member from the organization.

Response
{
  "message": "member removed"
}

Invite User

POST /api/v1/team/invitations

Email an invitation to join your organization. Returns 402 if you're at your plan's seat limit. Invitation creation is limited to 10 per organization per hour. The invitee accepts via the emailed link (GET /api/v1/auth/accept-invite?token=..., browser-only).

Request body
{
  "email": "teammate@example.com",
  "role": "editor"
}
Response

Returns 201 with the invitation object (id, email, role, expires_at, created_at).

List Invitations

GET /api/v1/team/invitations

List pending invitations.

Cancel Invitation

DELETE /api/v1/team/invitations/{id}
Response
{
  "message": "invitation cancelled"
}

Billing

Manage your subscription plan via Stripe integration. Checkout, portal, and org-name endpoints require the admin role.

Get Plan

GET /api/v1/billing/plan

Returns current plan details, usage, and limits.

Response
{
  "plan": "free",
  "org_name": "John Smith's Org",
  "has_billing": false,
  "usage": {
    "folders": 1,
    "subnets": 3,
    "addresses": 42
  },
  "limits": {
    "folders": 5,
    "subnets": 100,
    "addresses": 1500
  }
}

Checkout

POST /api/v1/billing/checkout

Creates a Stripe Checkout session for upgrading to Pro or Teams. Redirect the user to the returned URL. Returns 400 if the organization is already on a paid plan — use the Customer Portal to switch plans instead. For Enterprise, contact us.

Request body
{
  "plan": "pro"
}

plan is "pro" or "teams". Omitting the body defaults to "pro".

Response
{
  "url": "https://checkout.stripe.com/c/pay/cs_live_..."
}

Customer Portal

POST /api/v1/billing/portal

Creates a Stripe Customer Portal session for managing billing, invoices, and cancellation.

Response
{
  "url": "https://billing.stripe.com/p/session/..."
}

Update Organization Name

PATCH /api/v1/billing/org-name

Rename your organization (max 100 characters).

Request body
{
  "name": "Acme Networks"
}
Response
200 OK

Import & Export

Bulk import and CSV export. See the CSV Import & Export guide for the UI workflow, column aliases, and normalization rules. Import request bodies are limited to 10 MB.

Import Subnets

POST /api/v1/import/subnets

Create up to 1,000 subnets per request. Folders, VLANs, VRFs, and locations referenced by name/number are auto-created. Duplicates (same CIDR + VRF) and partial overlaps are skipped, not errored. Rows that would exceed plan limits are reported as errors; successfully imported rows are kept (no rollback).

Request body
{
  "subnets": [
    {
      "network": "10.0.1.0/24",
      "name": "Web servers",
      "folder": "Production",
      "vlan": "120",
      "vrf": "CORP",
      "location": "Datacenter US-East",
      "gateway": "10.0.1.1",
      "is_pool": false
    }
  ]
}

Only network is required per row. Also accepted: description, dns_primary, dns_secondary, scan_enabled, tracked (defaults to true when absent), custom_fields.

Response
{
  "results": [
    { "row": 1, "status": "created", "id": "...", "network": "10.0.1.0/24" }
  ],
  "summary": { "created": 1, "skipped": 0, "errors": 0 }
}

Import Addresses

POST /api/v1/import/addresses

Create up to 1,000 addresses per request. Each row needs ip and subnet (the parent CIDR, which must already exist). Also accepted: hostname, mac_address, description, status, owner, ptr_record, custom_fields. Already-assigned IPs are skipped. Same response shape as subnet import.

Request body
{
  "addresses": [
    {
      "ip": "10.0.1.10",
      "subnet": "10.0.1.0/24",
      "hostname": "web-01.prod",
      "status": "assigned"
    }
  ]
}

Export Subnets

GET /api/v1/export/subnets?folder_id={folder_id}

Download all subnets as CSV (columns: network, name, description, folder, vlan, vrf, location, gateway, dns_primary, dns_secondary, is_pool, scan_enabled, tracked). Folder/VLAN/VRF/location are exported as human-readable names. folder_id optionally limits the export to one folder.

Export Addresses

GET /api/v1/export/addresses?subnet_id={subnet_id}

Download addresses as CSV (columns: ip, subnet, hostname, mac_address, description, status, owner, ptr_record). subnet_id optionally limits the export to one subnet; omit it to export all addresses.

Full-text search across subnets and addresses.

GET /api/v1/search?q={query}

Search by IP address, hostname, network CIDR, name, or description. Returns matching subnets and addresses, up to 50 of each.

Query parameters
  • q — search query (required, max 500 characters)
Response
{
  "subnets": [
    {
      "id": "c5d6e7f8-a9b0-1234-cdef-012345678901",
      "network": "10.0.1.0/24",
      "name": "Web servers"
    }
  ],
  "addresses": [
    {
      "id": "0a1b2c3d-4e5f-6789-0abc-def123456789",
      "ip": "10.0.1.10",
      "hostname": "web-01.prod"
    }
  ]
}

Activity

Audit log of actions performed within your organization. History is retained per your plan (7/30/90 days; see Plan Limits).

GET /api/v1/activity?limit={limit}&offset={offset}

Returns a paginated list of activity entries.

Query parameters
  • limit — entries to return (default 50, max 500)
  • offset — pagination offset
  • action, resource_type, user_id, since, until — optional filters
Response
{
  "entries": [
    {
      "id": 1042,
      "user_id": "7f3e9a12-4b6c-4d2e-9f01-8a5b3c7d1e42",
      "action": "create",
      "resource_type": "subnet",
      "resource_id": "c5d6e7f8-a9b0-1234-cdef-012345678901",
      "changes": { "network": "10.0.1.0/24" },
      "created_at": "2026-03-14T10:30:00Z"
    }
  ],
  "total": 137,
  "users": { "7f3e9a12-...": "john@example.com" }
}

Activity Filters

GET /api/v1/activity/filters

Returns the distinct actions and resource_types present in your activity log, for building filter UIs.

Activity Export

GET /api/v1/activity/export

Download the activity log as CSV (up to 10,000 rows). Accepts the same filter parameters as the list endpoint.

Network Tools

Public endpoints for common network lookups. No authentication required. Rate-limited per IP address: 10 requests per minute for lookups, and 3 requests per minute for active probes (ping, port scan, traceroute). Exceeding a limit returns 429 with a Retry-After header.

DNS Lookup

GET /api/v1/tools/dns?domain={domain}&type={type}

Resolve a domain against 3 independent resolvers: Cloudflare (1.1.1.1), Google (8.8.8.8), and Quad9 (9.9.9.9).

Query parameters
  • domain — domain name to resolve (required)
  • type — record type, e.g. A, AAAA, MX, TXT, NS, CNAME, SOA (default: A)
  • all=true — query all common record types at once
  • authoritative=true — query the domain's own nameservers instead
Response
{
  "domain": "example.com",
  "type": "A",
  "results": [
    {
      "resolver": "1.1.1.1:53",
      "records": ["93.184.216.34"],
      "ttl": 300,
      "response_time_ms": 2.3
    },
    { "resolver": "8.8.8.8:53", "records": ["93.184.216.34"], "ttl": 300, "response_time_ms": 5.4 },
    { "resolver": "9.9.9.9:53", "records": ["93.184.216.34"], "ttl": 37, "response_time_ms": 0.7 }
  ]
}

DNS Propagation

GET /api/v1/tools/dns-propagation?domain={domain}&type={type}

Check how a DNS record has propagated across 8 resolvers around the world (Cloudflare, Google, Quad9, OpenDNS, Yandex, KT, Neustar, 114DNS). Each result includes the resolver's label and location in addition to the fields returned by DNS Lookup.

Reverse DNS

GET /api/v1/tools/rdns?ip={ip}

Perform a reverse DNS (PTR) lookup for an IP address, with forward-confirmation (FCrDNS) of each returned name.

Response
{
  "ip": "8.8.8.8",
  "arpa_query": "8.8.8.8.in-addr.arpa.",
  "names": [
    { "name": "dns.google", "fcrdns_valid": true }
  ],
  "authoritative_ns": "ns1.google.com"
}

WHOIS Lookup

GET /api/v1/tools/whois?query={query}

Perform a WHOIS lookup for a domain or IP address. Returns the raw WHOIS text plus a parsed summary: for domains, registrar, created, expires, expires_in_days, updated, nameservers, status; for IPs, cidr, net_name, organization, abuse_contact.

Response
{
  "query": "example.com",
  "raw": "Domain Name: EXAMPLE.COM\n...",
  "parsed": {
    "registrar": "IANA",
    "created": "1995-08-14T04:00:00Z",
    "expires": "2026-08-13T04:00:00Z",
    "expires_in_days": 40,
    "nameservers": ["a.iana-servers.net", "b.iana-servers.net"]
  }
}

SSL Certificate Check

GET /api/v1/tools/ssl?host={host}

Inspect a host's TLS certificate chain: each certificate's subject, issuer, sans, validity window, and days_remaining, plus chain_valid, tls_version, and cipher_suite.

Response (abridged)
{
  "host": "example.com",
  "tls_version": "TLS 1.3",
  "cipher_suite": "TLS_AES_128_GCM_SHA256",
  "chain_valid": true,
  "certificates": [
    {
      "subject": "example.com",
      "issuer": "DigiCert TLS RSA SHA256 2020 CA1",
      "sans": ["example.com", "*.example.com"],
      "not_after": "2026-08-29T21:41:26Z",
      "days_remaining": 57,
      "is_expired": false
    }
  ]
}

SPF Check

GET /api/v1/tools/spf?domain={domain}

Fetch and validate a domain's SPF record: parsed mechanisms, dns_lookup_count against the 10-lookup limit, plus valid, warnings, and errors.

Response
{
  "domain": "example.com",
  "found": true,
  "valid": true,
  "raw": "v=spf1 include:_spf.example.com ~all",
  "mechanisms": [
    { "qualifier": "+", "mechanism": "include", "value": "_spf.example.com" }
  ],
  "dns_lookup_count": 1,
  "dns_lookup_limit": 10,
  "warnings": ["Using ~all (softfail) instead of -all (hardfail)"]
}

DMARC Check

GET /api/v1/tools/dmarc?domain={domain}

Fetch and validate a domain's DMARC record: policy, subdomain_policy, parsed tags, plus valid, warnings, and errors.

Response
{
  "domain": "example.com",
  "found": true,
  "valid": true,
  "policy": "reject",
  "raw": "v=DMARC1; p=reject; rua=mailto:...",
  "tags": { "v": "DMARC1", "p": "reject" }
}

Blacklist Check

GET /api/v1/tools/blacklist?ip={ip}

Check an IP against 7 DNS blocklists (Spamhaus ZEN, Barracuda, SpamCop, Mailspike, UCEPROTECT L1, PSBL, SpamRATS).

Response
{
  "ip": "203.0.113.42",
  "listed_count": 0,
  "total_checked": 7,
  "results": [
    { "list": "SpamCop", "zone": "bl.spamcop.net", "listed": false }
  ]
}

ASN Lookup

GET /api/v1/tools/asn?query={query}

Look up the ASN, announcing prefix, and registry data for an IP address or AS number.

Response
{
  "query": "8.8.8.8",
  "type": "ip",
  "asn": "15169",
  "as_name": "GOOGLE - Google LLC, US",
  "prefix": "8.8.8.0/24",
  "country": "US",
  "rir": "arin",
  "allocated": "2023-12-28"
}

HTTP/2 Check

GET /api/v1/tools/http2?host={host}

Check which HTTP protocol versions a host supports.

Response
{
  "host": "example.com",
  "http11": true,
  "http2": true,
  "http3_advertised": false,
  "alpn_negotiated": "h2",
  "tls_version": "TLS 1.3"
}

Ping (TCP Check)

GET /api/v1/tools/ping?host={host}

TCP connectivity check to a host. Probes ports 80 and 443 by default; pass ?port= to check a specific port. Rate-limited to 3 requests per minute.

Response
{
  "host": "example.com",
  "ip": "93.184.216.34",
  "results": [
    { "port": 80, "open": true, "latency_ms": 1.3 },
    { "port": 443, "open": true, "latency_ms": 1.0 }
  ],
  "checked_at": "2026-03-21T16:30:00Z"
}

Port Scan

GET /api/v1/tools/ports?host={host}

Scan 19 common ports on a host (FTP, SSH, HTTP, HTTPS, DNS, SMTP, databases, RDP, etc.), or pass ?ports= with a comma-separated list (max 25). Rate-limited to 3 requests per minute.

Response
{
  "host": "example.com",
  "open_count": 2,
  "total": 19,
  "results": [
    { "port": 80, "service": "HTTP", "category": "Web", "open": true, "latency_ms": 2.0 },
    { "port": 443, "service": "HTTPS", "category": "Web", "open": true, "latency_ms": 1.7 },
    { "port": 22, "service": "SSH", "category": "Remote Access", "open": false }
  ]
}

Traceroute

GET /api/v1/tools/traceroute?host={host}

Trace the network path to a host with 3 round-trip measurements per hop. Unresponsive hops have null values. Rate-limited to 3 requests per minute.

Response
{
  "host": "example.com",
  "ip": "93.184.216.34",
  "hops": [
    { "hop": 1, "ip": "172.31.1.1", "rtt1_ms": 2.3, "rtt2_ms": 2.7, "rtt3_ms": 2.8 },
    { "hop": 2, "ip": null, "rtt1_ms": null, "rtt2_ms": null, "rtt3_ms": null }
  ]
}

My IP

GET /api/v1/tools/myip

Returns the client's public IP address, plus country, datacenter (edge location), and hostname (reverse DNS) when available.

Response
{
  "ip": "203.0.113.42",
  "country": "US",
  "datacenter": "DEN"
}

Request Headers

GET /api/v1/tools/headers

Returns the HTTP headers sent by the client. Pass ?url=https://... to instead fetch a remote site and get back its response headers with a security_checklist (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy).

Response
{
  "headers": {
    "User-Agent": "curl/8.1.0",
    "Accept": "*/*"
  },
  "method": "GET",
  "proto": "HTTP/1.1"
}