Raw request logs
const url = 'https://api.nsin.ir/analytics/logs?domain=example.com&period=1h&limit=100&offset=0&cache=hit&reqStatus=cache&sort=time&dir=asc&count=exact&fields=list&errFault=origin';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
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.nsin.ir/analytics/logs?domain=example.com&period=1h&limit=100&offset=0&cache=hit&reqStatus=cache&sort=time&dir=asc&count=exact&fields=list&errFault=origin' \ --header 'Authorization: Bearer <token>'Individual request records, newest first unless sort says otherwise,
with every filter applied as an AND. Requires analytics.view and a
plan including the logs feature.
from/to narrow the rows to an exact window inside period (a
chart bucket, say); period still sets the query’s time budget. The
list and its total are computed separately: has_more says whether
another page exists without needing the total, count controls how
hard the total works, and fields=list returns only the columns a list
row needs (fetch the rest for one row by rayId). Sorting by anything
but time needs a window of at most 24 hours (7 days on one domain).
Header and body fields are retained for a shorter window than the rest of the row, so older entries return them empty.
When your origin answers with a 5xx, the body it sent is kept exactly
and returned as origin_error_body for 3 days. A page of rows carries
the first 2 KB of each body; ask for the request by rayId to get all
of it. origin_error_body_state says whether the body is whole, cut,
empty or already deleted.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Query Parameters
Section titled “Query Parameters ”The domain name (for example example.com). These endpoints take the
domain as a query parameter rather than a path segment.
Example
example.comTime window, ending now. Time series are bucketed by minute for 1h,
by hour up to 48h, and by day for 7d and 30d. Buckets are cut on
Asia/Tehran (UTC+03:30) boundaries and emitted as UTC instants, so an
hourly bucket starts at :30 UTC. An unrecognised value falls back to
24h.
Rows per page, 1–500. Values outside the range fall back to 100.
Exact HTTP status code.
HTTP method — case-insensitive.
URI substring match.
Cache status: hit, miss or bypass.
Serving path: cache, proxied or direct.
Exact ray id of a single request. A query with rayId is the one that returns the whole origin_error_body.
Exact host, a subdomain of it, or a bare subdomain label.
Host header sent to the origin.
SNI presented to the origin.
Origin address the edge connected to.
Comma-separated list of origin addresses.
Client IP address.
Client ISO country code.
ISO country of the edge node that served the request.
Edge node name.
Threat category.
Action a detection rule took on the request.
Classified bot kind.
A CRS rule id that fired.
Substring searched across the captured headers.
Comma-separated URI patterns.
Start of an exact window inside period: RFC 3339, unix seconds, or
YYYY-MM-DD HH:MM:SS read as Asia/Tehran time.
End of the window, in the same forms as from.
Column to order by, optionally with the direction as key:dir.
Default time:desc. Anything else is a 400.
Sort direction.
exact (default) counts every matching row; capped stops at
10 000 (total_state: capped then means “10 000 or more”); 0
skips the count (total_state: skipped) — use it for page 2 and on.
list returns only the columns a list row shows.
Failure reason, e.g. origin_timeout (the reason keys of /analytics/unreachable-reasons).
Who was responsible for a failure.
Which hop failed (see err_leg on the log entry).
The NSIN edge address that received the request. Exact match.
Responses
Section titled “ Responses ”A page of request logs.
object
One request. Header and body fields are retained for a shorter window than the rest of the row, so older entries return them empty.
object
HTTP method.
Percent-encoded exactly as the client sent it.
Status returned to the visitor.
Client IP address, as the edge saw it.
Client country (ISO code), resolved from the IP.
Total request duration in ms. For WebSockets this spans the whole connection.
Bytes received from the client.
Bytes sent to the client.
Cache outcome for the request.
Why the cache was bypassed, when it was.
Serving path: cache, proxied (through the edge proxy) or direct.
The client’s User-Agent.
Request headers as captured by the edge.
Headers the edge sent to the origin.
Headers the origin returned.
Headers returned to the visitor.
Body captured by the edge, when one was. Retained for a shorter window than the rest of the row.
The request was a connection upgrade (WebSocket or similar).
What kind of upgrade is_ws was: a real WebSocket handshake,
httpupgrade (Upgrade: websocket without the WebSocket key
exchange), or other. Empty when unknown.
Response Content-Type.
The raw error text when the request failed.
Why the request failed, as a stable key — origin_timeout,
origin_refused, origin_tls, client_closed, nsin_edge, …, or
origin_status_NNN. The same keys as /analytics/unreachable-reasons.
Empty when the request did not fail.
Who was responsible for the failure.
Which hop failed: client_edge, edge, edge_origin, or — for
traffic carried over NSIN’s internal tunnel — edge_mafar,
mafar_routing, mafar_tunnel, mafar_egress, egress_origin.
Empty on older rows.
The three-digit status shown for the failure.
How the failure was classified (core_typed, core_string, mafar_connect, mafar_sideband, edge_local). Empty on older rows.
Short extra detail about the failure.
Error code from NSIN’s internal tunnel; 0 when it was not involved.
A stale cached copy was served.
Edge node that served the request.
Unique id for this request.
Client-to-edge protocol, e.g. HTTP/2.0.
Edge-to-origin protocol. Empty on a cache hit.
Status the origin returned. 0 on a cache hit.
The body your origin sent with a 5xx, exactly as it sent it (a compressed body is decoded), whatever page the visitor was shown. Kept for 3 days. In a list this is the first 2 KB and origin_error_body_preview is true when there is more; query the request by rayId for the whole body. Empty when the body is not UTF-8: see origin_error_body_base64.
The same body, base64-encoded, when it is not valid UTF-8 (a binary body, or a page in another charset). Only returned for a query by rayId.
Size of the stored body in bytes.
What is known about the origin’s error body. full: the whole body is stored. truncated: only its first part is, because it was larger than the edge keeps or the origin stopped sending it. empty: the origin sent the error with no body. not_read: the origin sent the status and the body never arrived. expired: the body was kept and has been deleted after 3 days. Absent when the origin did not answer with a 5xx.
true when origin_error_body holds only the start of the body. Query the request by rayId for all of it.
Origin IP:port the edge connected to.
TLS version the client connected with.
TLS cipher suite the client connected with.
The client resumed an earlier TLS session.
Seconds the served object had been cached.
Bot classification, when the request was identified as one.
Whether the bot’s identity was verified, rather than merely claimed.
Action a detection rule took.
True when the rule was in dry-run, so nothing was enforced.
WAF anomaly score.
CRS rule ids that fired.
The rule that set detect_action. Absent when none (or on rows before 2026-10).
The rate-limit key that tripped, short and readable (ja4h=ge20cn09…|path=/ajax.php).
The request’s cookie names, sorted and comma-separated. Never values.
12 hex characters naming the request’s set of header names; requests from one client build share it.
The edge’s bot score, 1 (bot) to 99 (human-like). Absent when the request was not scored.
The response was served as Markdown.
Token count of the Markdown served.
Token count of the original HTML.
Why a Markdown conversion was not served, when it was asked for.
The asset optimization applied to the response, if any.
Size before the optimization, uncompressed.
Size after it, uncompressed.
The edge reused an open connection to the origin.
Time to connect to the origin, in ms. Absent when no new connection was made.
TLS handshake time with the origin, in ms.
Time to the origin’s first response byte, in ms.
TLS version used with the origin (tls_version is the visitor’s).
Rows matching the filters, before paging — meaningful only when total_state is exact or capped.
exact — total is the count. capped — more than total
(10 000) rows match. skipped — count=0 was sent. failed — the
count timed out; the rows are still valid.
At least one more row matches past this page.
The order the rows are in, as key:dir.
list when the rows carry only the list columns.
Example
{ "data": [ { "cache_status": "hit", "req_status": "cache", "upgrade": "websocket", "err_fault": "origin", "origin_error_body_state": "full", "opt_kind": "webp" } ], "total_state": "exact", "sort": "time:desc"}The domain query parameter is missing, sort/dir is not a known
value, or the sort needs a shorter window.
The single error shape used by every endpoint.
object
Human-readable description of what went wrong.
Example
{ "error": "read-only API key"}Missing, malformed, revoked or expired API key. (A valid key whose
account has been deactivated gets 403 account is inactive instead.)
The single error shape used by every endpoint.
object
Human-readable description of what went wrong.
Examples
{ "error": "invalid API key"}The domain has no active plan at all, so no plan feature is available
on it. Buy or renew a plan for the domain. (A plan that is active but
does not include the feature answers 403 instead.)
The single error shape used by every endpoint.
object
Human-readable description of what went wrong.
Examples
{ "error": "This domain does not have an active plan."}The domain’s plan does not include the feature this endpoint needs
(monitoring for most sections, logs for raw and top-N request data).
The single error shape used by every endpoint.
object
Human-readable description of what went wrong.
Example
{ "error": "read-only API key"}No such domain, or it is not visible to this account. Domains you cannot access are reported as not found rather than forbidden.
The single error shape used by every endpoint.
object
Human-readable description of what went wrong.
Example
{ "error": "read-only API key"}The key exceeded its request budget (300 requests per minute by default).
The single error shape used by every endpoint.
object
Human-readable description of what went wrong.
Examples
{ "error": "rate limit exceeded"}Either the analytics backend is temporarily unreachable (retry; no data
is lost), or the answer is still being computed. The per-domain
sections and the cached account-wide views (domains-overview,
global-summary, global-summary/peak, requests-compare,
bandwidth-overview, nodes-overview) wait at most about 25 seconds
for their query; past that they answer 503 with pending: true and
a Retry-After header, while the query keeps running and caches its
result, so the retry is served from that cache instead of starting the
same scan again. Identical concurrent requests share one computation.
object
Human-readable description of what went wrong.
true when the query is still running rather than
failing. Retry after Retry-After seconds.
Examples
{ "error": "analytics unavailable"}{ "error": "still computing — try again in a few seconds", "pending": true}Headers
Section titled “Headers ”Example
5Seconds to wait before retrying. Sent only with pending.