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",
"domain_categories": ["Malware Sites"],
"domain_reputation": 12,
"parent_domain": "ra5xovel.in.net",
"parent_domain_categories": ["Malware Sites"],
"parent_domain_reputation": 40,
"category": "botnet",
"site": "hq",
"public_ip": "87.66.4.201",
"internal_ips": ["192.168.20.72"],
"ip_tags": ["servers"],
"source_devices": ["fdctest01.secutec.local"],
"source_device_type": "server",
"user_names": ["john.doe"],
"log_servers": ["SECDCSRV1001"],
"support_ticket_url": "https://secutec.zendesk.com/hc/requests/452235"
}
],
"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 |
domain_categories |
Security vendor categories of domain, e.g. Malware Sites. Empty when unclassified |
domain_reputation |
Security vendor reputation score of domain, or null when unscored. Lower is worse |
parent_domain |
The registrable parent of domain, when the platform derived one |
parent_domain_categories |
Security vendor categories of parent_domain. Empty when unclassified |
parent_domain_reputation |
Security vendor reputation score of parent_domain, or null when unscored |
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 |
ip_tags |
The IP tags assigned to the internal IP ranges internal_ips fall in — the same tags the portal shows. One combined list for all of internal_ips, not a per-address mapping. Empty when no tag matches |
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 |
user_names |
User names the platform's endpoint telemetry associated with the query, when known |
log_servers |
Names of the servers whose logs recorded the query, when known |
support_ticket_url |
Link to the support ticket this block was escalated under, or null when it was not escalated |
Lists are empty, and nullable fields null, when the platform has nothing to
say — an unclassified domain is not an error. support_ticket_url is an opaque
link: open it, do not parse it, because its host can change when the support
desk moves. These fields were added on /v1 after launch; a consumer that
validated the exact key set must allow them, as the versioning
policy says additive fields can arrive at any time.
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.