DNS blocks
GET /v1/sdns/blocks returns the DNS queries Secure DNS blocked for your
organization — one item per refused query, oldest first — so you can feed them
into your SIEM or reporting without going through the portal.
curl -s -H "Authorization: Bearer $TOKEN" \
"https://api.secutec.com/v1/sdns/blocks?limit=2&categories=botnet&categories=phishing"
{
"blocks": [
{
"id": "I-X6S58BTnuzxlBYsmdO",
"blocked_at": "2026-07-10T12:22:24.471000Z",
"domain": "stab7-sheet.ra5xovel.in.net",
"parent_domain": "ra5xovel.in.net",
"category": "botnet",
"site": "hq",
"public_ip": "87.66.4.201",
"internal_ips": ["192.168.20.72"],
"source_devices": ["fdctest01.secutec.local"],
"source_device_type": "server"
}
],
"pagination": {
"next_cursor": "eyJ2IjoxLCJrIjoiYmxvY2tlZF9hdCIs...",
"page_size": 1
}
}
The envelope and the cursor work exactly as described in
Pagination & deltas: follow next_cursor until it is null,
keep the last non-null cursor, poll again later with it. This page covers what
is specific to blocks.
A block is an event
A block is written once, when the query is refused, and never changes. The
stream is therefore ordered by blocked_at ascending (ties broken by
id) — blocked_at plays the role updated_at plays for threats and assets.
De-duplicate on id, as everywhere.
blocked_at is the moment the query was blocked, not the moment the record
reached the platform. Records arrive a few seconds after the event, so the API
withholds the most recent 60 seconds: a block appears about a minute after
it happened, and a cursor never advances into records that may still be
arriving. A record that arrives later than that will not be delivered by a
delta poll whose cursor has already passed its blocked_at. If you need to
reconcile, re-read the period with start and end.
Time window
| Parameter | Default | Meaning |
|---|---|---|
start |
30 days before the request | Return blocks with blocked_at at or after this instant |
end |
the request time | Return blocks with blocked_at at or before this instant (minus the 60-second hold-back) |
Both are ISO 8601 datetimes with a UTC offset — 2026-09-01T00:00:00Z or
2026-09-01T02:00:00+02:00. A value without an offset, a start at or after
end, or a start in the future is a 400 with type
request/invalid-filter.
For a first backfill, set start to how far back you want history; the
default keeps a first poll bounded. Once you are following cursors the window
is re-anchored at the last block you received, so you keep passing the same
start (or none) and simply follow next_cursor.
Pausing for more than 30 days
The default start is always 30 days before the current request, and
the window never reaches back further than that unless you say so. If you
stop polling for more than 30 days and resume with a cursor but no start,
the blocks between your cursor and 30 days ago are skipped silently.
Pass an explicit start at or before your cursor's position (the
blocked_at of the last block you received) to close the gap.
Filters
| Parameter | Matches |
|---|---|
categories |
the security category that triggered the block, e.g. botnet, malware, phishing |
sites |
the customer site the query came from |
source_devices |
the device or agent name that issued the query |
internal_ips |
the internal (private) address of the requesting host |
domains |
the blocked domain name |
Every filter is repeatable: pass the key once per value
(categories=botnet&categories=phishing), not a comma-separated list. Several
values for one filter match any of them; several different filters must
all match. Values are matched exactly. At most 50 values per filter.
The cursor encodes only your position in the stream, not your filters. Send the same filters on every page of a stream; changing them mid-stream is not an error, but the next page is then the new filter's rows from that position onward.
The block
| Field | Meaning |
|---|---|
id |
Stable identifier of the blocked query — the de-duplication key |
blocked_at |
When the query was blocked, ISO 8601 UTC |
domain |
The fully qualified domain name that was requested and refused |
parent_domain |
The registrable parent of domain, when the platform derived one |
category |
The security category that triggered the block. New categories can be added on /v1; treat unknown values as "other" |
site |
The customer site the query originated from |
public_ip |
The public IP address the query reached the resolver from |
internal_ips |
Internal addresses of the requesting host, when known |
source_devices |
Names of the devices or agents that issued the query, when known |
source_device_type |
agent for a managed Secure DNS agent on a device, server for a site resolver, or null |
Errors specific to this endpoint
| Status | type |
When |
|---|---|---|
| 400 | request/invalid-filter |
limit out of range, a datetime without an offset, start not before end, start in the future, an empty filter value, more than 50 values for one filter |
| 400 | request/invalid-cursor |
a malformed cursor, or a cursor issued by another endpoint (a threats cursor cannot resume a blocks stream) |
Everything else — authentication, rate limiting, server errors — is as in Errors.