Skip to content

REST API Reference

Ferrous DNS exposes a REST API for managing all aspects of the server. The API is served on the same port as the web dashboard (web_port, default 8080).


Base URL

Mode Base URL
Normal http://<server>:8080/api
Pi-hole compat http://<server>:8080/ferrous/api

When pihole_compat = true, the Ferrous API moves to /ferrous/api/* and the Pi-hole v6 API occupies /api/*.


Authentication

When authentication is enabled ([auth] section in config), all API endpoints require either a valid session cookie or an API token — except the public auth endpoints listed below.

Session Authentication

Authenticate via the login endpoint to receive a session cookie:

POST /api/auth/login
Content-Type: application/json

{
  "username": "admin",
  "password": "your-password"
}

The server sets a ferrous_session cookie on successful login. The cookie is sent automatically with subsequent requests from the dashboard.

API Token Authentication

For programmatic access, include an API token in the X-Api-Key header:

X-Api-Key: your-api-token

Create and manage tokens via the API Token endpoints below.

Both methods accepted

The auth guard accepts either a valid session cookie or an X-Api-Key header. You do not need both.


Response Format

All responses are JSON. Successful mutations return:

{
  "success": true,
  "message": "Operation completed successfully"
}

Errors return an appropriate HTTP status code with:

{
  "error": "Description of the error"
}

Health & System

Health Check

GET /api/health

Returns server health status.

System Info

GET /api/system/info

Returns system information: kernel version, load averages, memory usage.

Hostname

GET /api/hostname

Returns the server hostname.


Statistics

Summary Stats

GET /api/stats

Returns aggregated query statistics: total queries, blocked queries, block rate.

Query Rate

GET /api/stats/rate?unit=second

Returns the current query rate. Supports unit=second or unit=minute.

Query Timeline

GET /api/queries/timeline

Returns query volume over time for dashboard graphs.

Top Blocked Domains

GET /api/stats/top_blocked

Top Clients

GET /api/stats/top_clients

Query Log

List Queries

GET /api/queries?limit=100&offset=0

Returns recent DNS queries with filtering support.

Parameter Type Description
limit integer Max results (default: 100)
offset integer Pagination offset

Configuration

Get Config

GET /api/config

Returns the full current configuration including server, DNS, blocking, logging, and database settings.

Update Config

POST /api/config

Partial update — only include the sections you want to change:

{
  "dns": {
    "cache_enabled": true,
    "cache_max_entries": 200000
  },
  "blocking": {
    "enabled": true
  }
}

Server settings (require restart):

{
  "server": {
    "pihole_compat": true
  }
}

Reload Config

POST /api/config/reload

Reloads the configuration from the TOML file without restarting the server. DNS, blocking, and cache settings take effect immediately. Server-level settings (ports, pihole_compat) require a full restart.

Get Settings

GET /api/settings

Returns DNS-specific settings (non-FQDN blocking, PTR blocking, local domain).

Update Settings

POST /api/settings
{
  "never_forward_non_fqdn": true,
  "never_forward_reverse_lookups": true,
  "local_domain": "lan",
  "local_dns_server": "192.168.1.1:53"
}

Auth Endpoints

Auth Status

GET /api/auth/status

Returns whether authentication is enabled and whether a password has been configured. Public — no auth required.

{
  "auth_enabled": true,
  "password_configured": true
}

First-Run Setup

POST /api/auth/setup

Sets the admin password on first run (when no password is configured). Public — no auth required.

{
  "password": "your-new-password"
}

Warning

This endpoint is only available when password_hash is empty. Once a password is set, it returns 403 Forbidden.

Login

POST /api/auth/login

Authenticates with username and password. Returns a session cookie (ferrous_session).

{
  "username": "admin",
  "password": "your-password",
  "remember_me": false
}
Field Type Default Description
username str Admin username
password str Admin password
remember_me bool false Extend session lifetime to remember_me_days

Logout

POST /api/auth/logout

Invalidates the current session. Public — no auth required (clears session if present).

Change Password

POST /api/auth/change-password

Changes the admin password. Protected — requires valid session or API token.

{
  "current_password": "old-password",
  "new_password": "new-password"
}

List Sessions

GET /api/auth/sessions

Returns all active sessions. Protected.

Revoke Session

DELETE /api/auth/sessions/{id}

Revokes a specific session by ID. Protected.


API Tokens

Named API tokens for programmatic access. Tokens are stored as SHA-256 hashes — the full token is only shown once at creation.

List Tokens

GET /api/api-tokens

Returns all tokens. Only the token prefix is shown in the listing.

Create Token

POST /api/api-tokens
{
  "name": "Grafana Integration"
}

Response includes the full token value — save it immediately:

{
  "id": 1,
  "name": "Grafana Integration",
  "token": "fdns_a1b2c3d4e5f6..."
}

Update Token

PUT /api/api-tokens/{id}

Update the token name or import a custom key:

{
  "name": "New Name",
  "key": "custom-imported-key"
}

Pi-hole migration

Use the key field to import existing API keys from Pi-hole or other tools.

Delete Token

DELETE /api/api-tokens/{id}

User Management

List Users

GET /api/users

Returns all users. Protected.

Create User

POST /api/users
{
  "username": "operator",
  "password": "secure-password"
}

Delete User

DELETE /api/users/{id}

Cache

Cache Stats

GET /api/cache/stats

Returns cache hit/miss counts, hit rate, and total entries.

Cache Metrics

GET /api/cache/metrics

Returns detailed cache metrics: hits, misses, evictions, insertions, optimistic refreshes, lazy deletions, compactions, hit rate.


Upstream Health

Health Summary

GET /api/upstream/health

Returns health status per upstream server (Healthy / Unhealthy).

Health Detail

GET /api/upstream/health/detail

Returns detailed health information per upstream: pool name, strategy, latency metrics, failure counts.


Clients

List Clients

GET /api/clients?limit=1000

Returns all detected clients with IP, MAC, hostname, group, query count, and last seen.

Client Stats

GET /api/clients/stats

Returns per-client query statistics.

Create Manual Client

POST /api/clients
{
  "name": "Living Room TV",
  "ip": "192.168.1.50"
}

Update Client

PATCH /api/clients/{id}
{
  "name": "New Name"
}

Delete Client

DELETE /api/clients/{id}

Assign Client to Group

PUT /api/clients/{id}/group
{
  "group_id": 2
}

Client Subnets

Subnets auto-assign clients matching a CIDR range to a group.

List Subnets

GET /api/client-subnets

Create Subnet

POST /api/client-subnets
{
  "cidr": "192.168.1.0/24",
  "group_id": 2
}

Delete Subnet

DELETE /api/client-subnets/{id}

Groups

List Groups

GET /api/groups

Create Group

POST /api/groups
{
  "name": "Kids",
  "description": "Children's devices"
}

Get Group

GET /api/groups/{id}

Update Group

PUT /api/groups/{id}

Delete Group

DELETE /api/groups/{id}

Get Group Clients

GET /api/groups/{id}/clients

Blocklist Sources

List Sources

GET /api/blocklist-sources

Create Source

POST /api/blocklist-sources
{
  "name": "HaGeZi Pro",
  "url": "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/domains/pro.txt",
  "enabled": true
}

Get Source

GET /api/blocklist-sources/{id}

Update Source

PUT /api/blocklist-sources/{id}

Delete Source

DELETE /api/blocklist-sources/{id}

Whitelist Sources

List Sources

GET /api/whitelist-sources

Create Source

POST /api/whitelist-sources
{
  "name": "My Allowlist",
  "url": "https://example.com/allowlist.txt",
  "enabled": true
}

Get / Update / Delete

GET    /api/whitelist-sources/{id}
PUT    /api/whitelist-sources/{id}
DELETE /api/whitelist-sources/{id}

Managed Domains

Individual domains added to the blocklist or allowlist via the dashboard.

List Domains

GET /api/managed-domains?limit=100&offset=0

Create Domain

POST /api/managed-domains
{
  "domain": "ads.example.com",
  "list_type": "block",
  "comment": "Annoying popup ads"
}

Get / Update / Delete

GET    /api/managed-domains/{id}
PUT    /api/managed-domains/{id}
DELETE /api/managed-domains/{id}

Regex Filters

List Filters

GET /api/regex-filters

Create Filter

POST /api/regex-filters
{
  "pattern": "^ads\\d+\\.example\\.com$",
  "list_type": "block",
  "enabled": true
}

Get / Update / Delete

GET    /api/regex-filters/{id}
PUT    /api/regex-filters/{id}
DELETE /api/regex-filters/{id}

Block Filter Stats

GET /api/block-filter/stats

Returns blocking engine statistics: total domains in blocklist, total in allowlist, filter size.


Blocklist & Allowlist (Compiled)

Get Active Blocklist

GET /api/blocklist

Returns the full compiled blocklist currently in memory.

Get Active Allowlist

GET /api/whitelist

Returns the full compiled allowlist currently in memory.


Services (1-Click Blocking)

Service Catalog

GET /api/services/catalog

Returns all available service categories (built-in + custom).

GET /api/services/catalog/{id}

Returns a specific service definition with its domain list.

Blocked Services

GET /api/services?group_id=1

Returns services currently blocked for a group.

Block Service

POST /api/services
{
  "service_id": "facebook",
  "group_id": 1
}

Unblock Service

DELETE /api/services/{service_id}/groups/{group_id}

Custom Services

Define your own blockable service categories.

List / Create

GET  /api/custom-services
POST /api/custom-services
{
  "name": "My Custom Tracker",
  "domains": ["tracker1.example.com", "tracker2.example.com"],
  "category": "tracking"
}

Get / Update / Delete

GET    /api/custom-services/{id}
PATCH  /api/custom-services/{id}
DELETE /api/custom-services/{id}

Get Configs

GET /api/safe-search/configs
GET /api/safe-search/configs/{group_id}
POST /api/safe-search/configs/{group_id}
{
  "platform": "google",
  "enabled": true
}

Delete Configs

DELETE /api/safe-search/configs/{group_id}

Local DNS Records

Static A/AAAA records served directly from cache.

List Records

GET /api/local-records

Create Record

POST /api/local-records
{
  "hostname": "nas",
  "domain": "home.local",
  "ip": "192.168.1.10",
  "record_type": "A",
  "ttl": 300
}

Update / Delete

PUT    /api/local-records/{id}
DELETE /api/local-records/{id}

Schedule Profiles

Time-based blocking profiles for parental controls.

List / Create Profiles

GET  /api/schedule-profiles
POST /api/schedule-profiles
{
  "name": "School Hours",
  "description": "Block social media during school"
}

Get / Update / Delete Profile

GET    /api/schedule-profiles/{id}
PUT    /api/schedule-profiles/{id}
DELETE /api/schedule-profiles/{id}

Manage Time Slots

POST   /api/schedule-profiles/{id}/slots
DELETE /api/schedule-profiles/{id}/slots/{slot_id}
{
  "day_of_week": 1,
  "start_time": "08:00",
  "end_time": "15:00"
}

Assign Schedule to Group

GET    /api/groups/{id}/schedule
PUT    /api/groups/{id}/schedule
DELETE /api/groups/{id}/schedule
{
  "profile_id": 1
}

Pi-hole v6 Compatibility API

When pihole_compat = true, the following Pi-hole v6 endpoints are available at /api/*:

Method Endpoint Description
POST /api/auth Pi-hole v6 login (session-based, same flow as Pi-hole)
GET /api/auth Pi-hole v6 session status
DELETE /api/auth Pi-hole v6 logout
GET /api/stats/summary Dashboard summary stats
GET /api/stats/history Query history timeline
GET /api/stats/top_blocked Top blocked domains
GET /api/stats/top_clients Top querying clients
GET /api/stats/query_types Query type distribution

See Pi-hole Compatibility for details.