Analytics aggregate.
const url = 'https://api.opdns.io/v1/profiles/example/analytics/counts_by_status?limit=100';const options = {method: 'GET', headers: {cookie: 'opdns_session=<opdns_session>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url 'https://api.opdns.io/v1/profiles/example/analytics/counts_by_status?limit=100' \ --cookie opdns_session=<opdns_session>Token scope: analytics:read. Same routing by log destination as
/v1/profiles/{id}/logs, with one exception: counts_by_status and
timeline filtered by time only (no device, status, qname,
qtype, list or client_ip) always come from ClickHouse
(source = clickhouse), also for none and self-hosted profiles.
The cloud keeps hourly totals of every profile’s queries, counted by the
edge even when no per-query row is kept, so these answer counter-derived
totals (whole hours: a sub-hour range or bucket reports the enclosing
hours). With any other filter they, and top_* and destinations
always, follow the destination: relayed to the node for self-hosted,
empty for none. Without ClickHouse configured, those two shapes also
fall back to the destination’s routing.
bucket applies to timeline (default 3600).
In top_devices, device_name is the display name when one is set; rows
stay grouped by device id.
top_reasons groups queries by what decided them: columns reason_code
(list, rule, operator:<code>, rewrite, none), list_id (set for
list only), reason (the list name for list, the rule or operator text
for the others, "" for none) and count, most frequent first, ties by
reason_code, list_id, reason. Without a status filter it counts
blocked and rewritten queries.
destinations groups allowed queries (without a status filter) by answer
address: columns ip, count, country (ISO 3166-1 alpha-2 of the address
from the server’s GeoIP database, "" when unknown or when no database is
configured; see geoip_attribution in GET /v1/meta) and owner (the
company whose address it is: google, apple, meta, amazon,
microsoft, or the CDNs cloudflare, akamai, fastly; "" for any
other). top_owners counts queries by the company they reached: columns
owner and count, most first, ties by owner; a query’s owner is its
name’s (a curated domain suffix list) or else its first classified answer
address’s; unclassified queries are left out; no default status filter.
Nodes older than top_owners answer 501 shape_unsupported.
top_pops counts queries by the PoP that answered them: columns pop (the
PoP id as in GET /v1/pops, e.g. ams; queries a self-hosted node answered
itself are node) and count, most first, ties by pop; no default status
filter. Nodes older than top_pops answer 501 shape_unsupported.
Guardrails, per organisation and api replica: each shape
covers at most a fixed range, by plan (free: search, top_devices,
destinations and top_pops 31 days; the hourly shapes 400 days; tail
uncapped): a wider from..to is 422 range_too_large, and without
from the range starts that long before to. Queries draw on a cost
budget (two units per day of raw rows, one per week of hourly totals;
1,200 a minute) and at most four run at
once; a query waits up to five seconds for a slot, then is 429
too_many_queries with Retry-After, as is one over budget. A cloud
query reading more rows or memory than one query may is 422
query_too_expensive.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Public 6-character id; never 000000.
Query Parameters
Section titled “Query Parameters”Inclusive start, RFC 3339 or Unix milliseconds.
Exclusive end, RFC 3339 or Unix milliseconds; default now.
Device id; repeat for several.
Repeat for several.
Substring match for search, exact name otherwise.
Numeric DNS type; repeat for several.
Blocking list id; repeat for several.
Opaque, from a previous result’s next_cursor.
Timeline bucket width in seconds.
Responses
Section titled “Responses”Result.
object
The fixed query shapes both ClickHouse and self-hosted nodes implement.
The node that answered, for source = node.
object
One array per row, cells in column order; time as RFC 3339, bytes as base64, null for NULL.
Always sent (false when complete). True when the rows are fewer than the
query would give: the node cut its
answer short (partial_reason timeout near its query deadline, or
overload), or a self-hosted profile’s node is reconnecting
(node_reconnecting, no rows; a tail keeps its cursor). The rows present
are valid and next_cursor continues after them.
Set when partial is true.
Example
{ "shape": "tail", "source": "clickhouse", "columns": [ { "type": "string" } ], "partial_reason": "timeout"}Malformed request.
object
Stable machine code.
object
Seconds, repeating the Retry-After header (rate limits, offline nodes).
Example
{ "type": "https://opdns.io/problems/bad_request", "title": "Bad Request", "status": 400, "code": "bad_request", "detail": "invalid JSON body", "request_id": "5f2c9a0e7b1d4c38"}Not authenticated.
object
Stable machine code.
object
Seconds, repeating the Retry-After header (rate limits, offline nodes).
Example
{ "type": "https://opdns.io/problems/unauthenticated", "title": "Unauthorized", "status": 401, "code": "unauthenticated", "detail": "authentication required", "request_id": "5f2c9a0e7b1d4c38"}Authenticated but not allowed: insufficient_scope, session_required, csrf_rejected, mfa_enrolment_required (restricted session), mfa_required (an operator action, or an admin-scoped token, from a session signed in with the password alone; not in dev), reauth_required (sign in again within five minutes), account_pending_deletion (the account is in its deletion cooling-off).
object
Stable machine code.
object
Seconds, repeating the Retry-After header (rate limits, offline nodes).
Example
{ "type": "https://opdns.io/problems/insufficient_scope", "title": "Forbidden", "status": 403, "code": "insufficient_scope", "detail": "the token lacks the profiles:write scope", "request_id": "5f2c9a0e7b1d4c38"}Not found (also for resources of another organisation).
object
Stable machine code.
object
Seconds, repeating the Retry-After header (rate limits, offline nodes).
Example
{ "type": "https://opdns.io/problems/not_found", "title": "Not Found", "status": 404, "code": "not_found", "detail": "resource not found", "request_id": "5f2c9a0e7b1d4c38"}The query was refused: validation_failed (a bad parameter), range_too_large (from..to is wider than the shape allows; errors names from), or query_too_expensive (the query would read more rows or memory than one query may; narrow the range or add filters).
object
Stable machine code.
object
Seconds, repeating the Retry-After header (rate limits, offline nodes).
Example
{ "type": "https://opdns.io/problems/range_too_large", "title": "Unprocessable Content", "status": 422, "code": "range_too_large", "detail": "search queries cover at most 31 days; narrow from and to", "errors": [ { "field": "from", "message": "at most 31 days before to" } ], "request_id": "5f2c9a0e7b1d4c38"}Too many log queries of this organisation are running, or its query budget is spent (too_many_queries); retry after Retry-After seconds.
object
Stable machine code.
object
Seconds, repeating the Retry-After header (rate limits, offline nodes).
Example
{ "type": "https://opdns.io/problems/too_many_queries", "title": "Too Many Requests", "status": 429, "code": "too_many_queries", "detail": "too many log queries are running for this organisation; retry shortly", "request_id": "5f2c9a0e7b1d4c38", "retry_after": 1}Headers
Section titled “Headers”not_implemented (cloud log queries not configured on this server) or shape_unsupported (node too old).
object
Stable machine code.
object
Seconds, repeating the Retry-After header (rate limits, offline nodes).
Example
{ "type": "https://opdns.io/problems/not_implemented", "title": "Not Implemented", "status": 501, "code": "not_implemented", "detail": "cloud log queries are not configured on this server", "request_id": "5f2c9a0e7b1d4c38"}node_error: the node failed to answer.
object
Stable machine code.
object
Seconds, repeating the Retry-After header (rate limits, offline nodes).
Example
{ "type": "https://opdns.io/problems/node_error", "title": "Bad Gateway", "status": 502, "code": "node_error", "detail": "the node failed to answer", "request_id": "5f2c9a0e7b1d4c38"}node_offline: the profile’s logs live on a self-hosted node and no node is
connected; node_busy: the node has too many queries in flight;
relay_unavailable. Retry after Retry-After (also retry_after in the body).
object
Stable machine code.
object
Seconds, repeating the Retry-After header (rate limits, offline nodes).
Example
{ "type": "https://opdns.io/problems/node_offline", "title": "Service Unavailable", "status": 503, "code": "node_offline", "detail": "no node of this profile is connected", "request_id": "5f2c9a0e7b1d4c38", "retry_after": 30}Headers
Section titled “Headers”node_timeout: the node did not answer within 10 seconds; query_timeout: a cloud (ClickHouse) log query ran too long.
object
Stable machine code.
object
Seconds, repeating the Retry-After header (rate limits, offline nodes).
Example
{ "type": "https://opdns.io/problems/node_timeout", "title": "Gateway Timeout", "status": 504, "code": "node_timeout", "detail": "the node did not answer within 10 seconds", "request_id": "5f2c9a0e7b1d4c38"}