Skip to content
nsin docs

Run a custom query over your request logs

POST
/analytics/query
curl --request POST \
--url https://api.nsin.ir/analytics/query \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "sql": "SELECT toStartOfHour(event_time) AS h, count() AS c FROM requests WHERE status >= 500 GROUP BY h ORDER BY h" }'

Runs a read-only SQL SELECT against the requests table — your raw request log — for analyses the dedicated endpoints do not cover.

Scoping is enforced by the database engine, not by your query: a filter restricting rows to the domains this key can access is appended to every read of requests. You cannot read another account’s traffic, however the query is written.

Restrictions:

  • A single statement only, starting with SELECT or WITH.
  • Only the requests table may be read. Common table expressions you define yourself are fine; other tables and any db.table reference are rejected.
  • Writes, DDL and settings changes are rejected.
  • Execution is capped at 30 seconds and 10 000 returned rows — truncated tells you when the cap was hit.

Useful requests columns: event_time, domain_id, hostname, method, uri, status, bytesIn, bytesOut, duration (ms), remoteAddr, country, asn, asnOrg, userAgent, cacheStatus, reqStatus, isWS, protocol, referer, originStatus, originAddr, error.

Media type application/json
object
sql
required

The query to run.

string

Query result.

Media type application/json
object
columns

Column names, in result order.

Array<string>
rows

One entry per row, keyed by column name.

Array<object>
object
key
additional properties
any
row_count
integer
truncated

True when the 10 000-row cap was reached and results were cut short.

boolean
Example generated
{
"columns": [
"example"
],
"rows": [
{}
],
"row_count": 1,
"truncated": true
}

The query was rejected by validation, or the database refused it. detail carries the underlying message when the engine rejected it.

Media type application/json
object
error
string
detail
string
Examples
Example disallowedTable
{
"error": "querying \"system.parts\" is not allowed; only the 'requests' table may be read"
}

Missing, malformed, revoked or expired API key. (A valid key whose account has been deactivated gets 403 account is inactive instead.)

Media type application/json

The single error shape used by every endpoint.

object
error
required

Human-readable description of what went wrong.

string
Examples
Example invalidKey
{
"error": "invalid API key"
}

The account has no domains whose logs could be queried. (Read-only keys may call this endpoint: it is the one POST they are allowed, because it writes nothing.)

Media type application/json

The single error shape used by every endpoint.

object
error
required

Human-readable description of what went wrong.

string
Examples
Example noDomains
{
"error": "you have no domains to query"
}

The key exceeded its request budget (300 requests per minute by default).

Media type application/json

The single error shape used by every endpoint.

object
error
required

Human-readable description of what went wrong.

string
Examples
Example limited
{
"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.

Media type application/json
object
error
required

Human-readable description of what went wrong.

string
pending

true when the query is still running rather than failing. Retry after Retry-After seconds.

boolean
Examples
{
"error": "analytics unavailable"
}
Retry-After
integer
Example
5

Seconds to wait before retrying. Sent only with pending.