A DNS record group is a set of DNS records monitored together, such as the records of one zone, one service, or one customer. Its settings, such as which name servers to query and how often, apply to every DNS record in it.
Endpoints
| Request | Required access | What it does |
|---|---|---|
GET /api |
Read | Lists the account's record groups, oldest first. Query parameters: status (passing, failing, or unknown, ignoring case), name (part of the name, ignoring case), and limit and cursor for paging (see Lists). For example: /api/v2/record-groups?status=failing&name=mail.
For a loop that reads every page, see Finding Failing DNS Records. |
POST /api |
Read and write | Creates a record group, returning it with 201. |
GET /api |
Read | Gets one record group, with its DNS record counts by status. |
PATCH /api |
Read and write | Changes the fields sent, and leaves the rest as they are. |
DELETE /api |
Read and write | Deletes the record group and every DNS record in it, returning 204. It counts as one write against the write budget, regardless of how many DNS records it contains. |
The DNS records in a record group are listed and added through /api/v2/record-groups/{id}/dns-records, described on the DNS Records API page. To add or export many at once, see the Zone Files API.
Reading a Record Group
This reads the example record group. It works without the Authorization header too, but your own record groups need it, with your API key in $DNSCHECK_API_KEY:
curl https://www.dnscheck.co/api/v2/record-groups/ea883d67-d9f6-45e3-b3a1-844dd1857824 \
-H "Authorization: Bearer $DNSCHECK_API_KEY"
The response is the record group, with a 200 status. records counts its DNS records by status, and Fields describes the rest:
{
"id": "ea883d67-d9f6-45e3-b3a1-844dd1857824",
"object": "record_group",
"name": "Example DNS Check",
"description": "",
"name_servers": [],
"check_frequency": 300,
"public": true,
"notifications": true,
"status": "failing",
"records": {
"total": 38,
"passing": 36,
"failing": 2,
"unknown": 0
},
"created_at": "2015-04-11T00:38:46Z",
"updated_at": "2016-03-19T18:29:48Z",
"web_url": "https://www.dnscheck.co/tests/ea883d67-d9f6-45e3-b3a1-844dd1857824",
"api_url": "https://www.dnscheck.co/api/v2/record-groups/ea883d67-d9f6-45e3-b3a1-844dd1857824"
}
Creating a Record Group
A create sends only the fields it sets. This one names the record group and makes it private:
curl https://www.dnscheck.co/api/v2/record-groups \
-H "Authorization: Bearer $DNSCHECK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Mail", "public": false}'
This uses the requests library (pip install requests):
import os
import requests
response = requests.post(
"https://www.dnscheck.co/api/v2/record-groups",
headers={
"Authorization": f"Bearer {os.environ['DNSCHECK_API_KEY']}",
},
json={"name": "Mail", "public": False},
timeout=30,
)
if response.headers.get("Content-Type", "").startswith("application/problem+json"):
problem = response.json()
print(f"{problem['code']}: {problem['detail']}")
for error in problem.get("errors", []):
print(error["message"])
raise SystemExit(1)
response.raise_for_status()
group = response.json()
print(group["id"])
This needs Node.js 18 or later. Save it in a .mjs file, so top-level await works:
const response = await fetch("https://www.dnscheck.co/api/v2/record-groups", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.DNSCHECK_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ name: "Mail", public: false }),
signal: AbortSignal.timeout(30000),
});
if (response.headers.get("Content-Type")?.startsWith("application/problem+json")) {
const problem = await response.json();
console.error(`${problem.code}: ${problem.detail}`);
for (const error of problem.errors ?? []) console.error(error.message);
process.exit(1);
}
if (!response.ok) throw new Error(`HTTP ${response.status} ${response.statusText}`);
const group = await response.json();
console.log(group.id);
Using only the standard library. Save it as create-group.go and run it with go run create-group.go:
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"strings"
"time"
)
func main() {
body, err := json.Marshal(map[string]any{"name": "Mail", "public": false})
if err != nil {
log.Fatal(err)
}
req, err := http.NewRequest("POST", "https://www.dnscheck.co/api/v2/record-groups", bytes.NewReader(body))
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("DNSCHECK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second}
response, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer response.Body.Close()
if response.StatusCode >= 400 && !strings.HasPrefix(response.Header.Get("Content-Type"), "application/problem+json") {
log.Fatalf("HTTP %s", response.Status)
}
var group struct {
ID, Code, Detail string
Errors []struct{ Message string }
}
if err := json.NewDecoder(response.Body).Decode(&group); err != nil {
log.Fatal(err)
}
if response.StatusCode >= 400 {
fmt.Fprintf(os.Stderr, "%s: %s\n", group.Code, group.Detail)
for _, e := range group.Errors {
fmt.Fprintln(os.Stderr, e.Message)
}
os.Exit(1)
}
fmt.Println(group.ID)
}
The response is the new record group, with a 201 status and its api_url in the Location header. Every field left out takes its default, so this one is checked every 300 seconds and sends notifications. To make a retry safe, send an Idempotency-Key: see Idempotency Keys. Getting Started walks through creating a record group, adding a DNS record to it, and reading its status.
Updating a Record Group
An update sends only the fields it changes, and null clears description or name_servers. To keep it from overwriting someone else's edit, send the ETag header from reading the record group as If-Match: if the record group has been edited since, the update gets a 412 with the code precondition_failed. The example below turns off notifications for the record group in $GROUP_ID, reading it first for its ETag:
The first command reads the record group and saves its ETag in $ETAG, and the second sends it as If-Match:
curl -s -o /dev/null --etag-save etag.txt https://www.dnscheck.co/api/v2/record-groups/$GROUP_ID \
-H "Authorization: Bearer $DNSCHECK_API_KEY"
ETAG=$(cat etag.txt)
rm etag.txt
curl -X PATCH https://www.dnscheck.co/api/v2/record-groups/$GROUP_ID \
-H "Authorization: Bearer $DNSCHECK_API_KEY" \
-H "Content-Type: application/json" \
-H "If-Match: $ETAG" \
-d '{"notifications": false}'
This uses the requests library (pip install requests):
import os
import requests
url = f"https://www.dnscheck.co/api/v2/record-groups/{os.environ['GROUP_ID']}"
headers = {"Authorization": f"Bearer {os.environ['DNSCHECK_API_KEY']}"}
current = requests.get(url, headers=headers, timeout=30)
current.raise_for_status()
response = requests.patch(
url,
headers={**headers, "If-Match": current.headers["ETag"]},
json={"notifications": False},
timeout=30,
)
if response.headers.get("Content-Type", "").startswith("application/problem+json"):
problem = response.json()
print(f"{problem['code']}: {problem['detail']}")
for error in problem.get("errors", []):
print(error["message"])
raise SystemExit(1)
response.raise_for_status()
group = response.json()
print(group["notifications"])
This needs Node.js 18 or later. Save it in a .mjs file, so top-level await works:
const url = `https://www.dnscheck.co/api/v2/record-groups/${process.env.GROUP_ID}`;
const auth = { Authorization: `Bearer ${process.env.DNSCHECK_API_KEY}` };
const current = await fetch(url, { headers: auth, signal: AbortSignal.timeout(30000) });
if (!current.ok) throw new Error(`HTTP ${current.status} ${current.statusText}`);
const response = await fetch(url, {
method: "PATCH",
headers: { ...auth, "Content-Type": "application/json", "If-Match": current.headers.get("ETag") },
body: JSON.stringify({ notifications: false }),
signal: AbortSignal.timeout(30000),
});
if (response.headers.get("Content-Type")?.startsWith("application/problem+json")) {
const problem = await response.json();
console.error(`${problem.code}: ${problem.detail}`);
for (const error of problem.errors ?? []) console.error(error.message);
process.exit(1);
}
if (!response.ok) throw new Error(`HTTP ${response.status} ${response.statusText}`);
const group = await response.json();
console.log(group.notifications);
Using only the standard library. Save it as update-group.go and run it with go run update-group.go:
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"strings"
"time"
)
func main() {
url := "https://www.dnscheck.co/api/v2/record-groups/" + os.Getenv("GROUP_ID")
auth := "Bearer " + os.Getenv("DNSCHECK_API_KEY")
client := &http.Client{Timeout: 30 * time.Second}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", auth)
current, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
current.Body.Close()
if current.StatusCode != http.StatusOK {
log.Fatalf("HTTP %s", current.Status)
}
body, err := json.Marshal(map[string]any{"notifications": false})
if err != nil {
log.Fatal(err)
}
req, err = http.NewRequest("PATCH", url, bytes.NewReader(body))
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", auth)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("If-Match", current.Header.Get("ETag"))
response, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer response.Body.Close()
if response.StatusCode >= 400 && !strings.HasPrefix(response.Header.Get("Content-Type"), "application/problem+json") {
log.Fatalf("HTTP %s", response.Status)
}
var group struct {
Notifications bool
Code, Detail string
Errors []struct{ Message string }
}
if err := json.NewDecoder(response.Body).Decode(&group); err != nil {
log.Fatal(err)
}
if response.StatusCode >= 400 {
fmt.Fprintf(os.Stderr, "%s: %s\n", group.Code, group.Detail)
for _, e := range group.Errors {
fmt.Fprintln(os.Stderr, e.Message)
}
os.Exit(1)
}
fmt.Println(group.Notifications)
}
The response is the updated record group, with a 200 status and its new ETag.
Fields
Fields marked as settable can be sent when creating or updating a record group; the rest are set by DNS Check. A create needs only name, and every field left out takes the default shown.
| Field | Type | Description |
|---|---|---|
id |
string | The record group's UUID, the same one its web page's URL and API v1 use |
object |
string | Always "record_group" |
name |
string | Settable, required on create. Up to 64 letters, numbers, spaces, hyphens, underscores, periods, ampersands, and apostrophes, unique within the account, ignoring case. |
description |
string | Settable. Up to 256 characters. null clears it, as "" does. Default: empty. |
name_servers |
array of strings | Settable, Professional and Enterprise. Up to 10 name servers to query, one per entry, by name or IP address. Default: empty, which uses DNS Check's own name servers. null clears them, as [] does. See the FAQ for querying private name servers. |
check_frequency |
integer | Settable, Professional and Enterprise. Seconds between scheduled checks: 300 or 3600, or 0 to pause them. More choices may be added. Default: 300. |
public |
boolean | Settable. Whether anyone with the record group's URL can view it and check its DNS records without logging in. Default: true. |
notifications |
boolean | Settable. Whether status updates to its DNS records trigger notifications. Default: true. |
status |
string | failing if any DNS record is failing; passing if there's at least one DNS record and all are passing; otherwise unknown |
records |
object | How many DNS records it has: total, passing, failing, and unknown |
created_at |
string | When it was created, in UTC |
updated_at |
string | When its own settings last changed, in UTC. Adding, changing, or checking its DNS records doesn't change it. |
web_url |
string | Its web page |
api_url |
string | Its URL in API v2 |
Plan Requirements
On a Basic account, sending name_servers or check_frequency with anything but its current value (or, on a create, its default) gets a 403 with the code feature_not_in_plan.
Creating a record group beyond the plan's limit gets a 403 with the code plan_limit_reached, and an expired account can only delete. See API Errors for details on each error.
Protect your DNS infrastructure with automated monitoring
Get notified immediately when DNS records change. Start monitoring your critical DNS infrastructure for free in under 5 minutes.
No credit card required • Cancel anytime