Query analytics stats
Query Clics analytics with the TypeScript SDK: metrics, date ranges, dimensions, filters, and pagination.
Runs an analytics query against a project: the same engine that powers the Clics dashboard. This is the primary way to read traffic and conversion data programmatically.
Use it to verify tracking is working, build internal reports, or sync KPIs to other tools.
Parameters
| Field | Required | Description |
|---|---|---|
projectId |
Yes | Project to query |
metrics |
Yes | At least one metric (see below) |
dateRange |
Yes | Preset or [startISO, endISO] custom range |
domain |
No | Filter to one domain. Omit for all production domains. Use "localhost" for dev traffic |
dimensions |
No | Group results (country, page, UTM, time buckets, etc.) |
filters |
No | Narrow results: [operator, dimension, values[]] tuples |
orderBy |
No | Sort: [field, "asc" | "desc"] tuples |
include.previousPeriod |
No | Include comparison to the prior period |
include.totalRows |
No | Return total row count in meta |
pagination.limit |
No | Max rows (1–1000) |
pagination.offset |
No | Skip rows for paging |
timezone |
No | IANA timezone for calendar boundaries and time dimensions. Defaults to UTC |
Metrics
| Query shape | Supported metrics |
|---|---|
| KPI or time series | visitors, visits, pageviews, bounce_rate, visit_duration, views_per_visit |
| Breakdown | visitors, pageviews, conversion_rate, events |
| Real-time window | visitors only |
Use only the metrics supported by the shape you are querying. conversion_rate is a percentage; events is the number of events in an event or breakdown result.
Date range presets
last24h, last7days, last30days, last3months, last12months, monthToDate, quarterToDate, yearToDate, allTime
Dimensions
| Category | Dimensions |
|---|---|
| Content and events | event:page, event:hostname, event:name, event:outbound_url |
| Visitor | visit:country, visit:device, visit:browser, visit:os, visit:referrer, referrer:ai_provider |
| Campaign | visit:utm_source, visit:utm_medium, visit:utm_campaign, visit:utm_term, visit:utm_content |
| Time | time, time:hour, time:day |
Use one time dimension (time, time:hour, or time:day) or up to two non-time breakdown dimensions. Do not combine time and non-time dimensions in one query.
Filter operators
is, is_not, contains, contains_not
The filterable fields are event:page, event:hostname, event:goal, event:name, event:outbound_url, visit:country, visit:device, visit:browser, visit:os, visit:referrer, referrer:ai_provider, and every visit:utm_* dimension. A goal filter uses the goal’s tracked value, for example signup_completed for an event goal.
Signature
clics.stats.queryStats(request, options?)
Examples
All examples use this server-side client. Keep the API key out of browser code.
import { Clics } from "@clicsdev/sdk";
import type { StatsQueryRequest } from "@clicsdev/sdk/models";
const clics = new Clics({ apiKey: process.env.CLICS_API_KEY! });
const projectId = "proj_xxx";
type FilterExpression = NonNullable<StatsQueryRequest["filters"]>[number];
KPI overview
Use no dimensions for one aggregate result for the whole selected scope.
const overview = await clics.stats.queryStats({
projectId,
metrics: [
"visitors",
"visits",
"pageviews",
"bounce_rate",
"visit_duration",
"views_per_visit",
],
dateRange: "last30days",
});
console.log(overview.results[0]?.metricValues);
Every preset date range
The SDK accepts all nine Dashboard presets. This loop shows every valid preset; normally, choose just one for your report.
const presets = [
"last24h",
"last7days",
"last30days",
"last3months",
"last12months",
"monthToDate",
"quarterToDate",
"yearToDate",
"allTime",
] as const;
const resultsByPeriod = await Promise.all(
presets.map((dateRange) =>
clics.stats.queryStats({
projectId,
metrics: ["visitors", "pageviews"],
dateRange,
}),
),
);
Custom calendar range and exact timestamps
Date-only strings query inclusive calendar days. Use full ISO8601 timestamps when you need an exact instant range; the timezone offset is respected.
const julyCalendar = await clics.stats.queryStats({
projectId,
metrics: ["visitors", "pageviews"],
dateRange: ["2026-07-01", "2026-07-31"],
timezone: "Europe/London",
});
const launchWindow = await clics.stats.queryStats({
projectId,
metrics: ["visitors", "visits"],
dateRange: [
"2026-07-31T09:00:00+01:00",
"2026-07-31T17:00:00+01:00",
],
});
Domain scope: production, normalized hostnames, and localhost
Omit domain for all production domains. Clics normalizes schemes, paths, and ports. Use exactly "localhost" for development traffic.
const oneDomain = await clics.stats.queryStats({
projectId,
domain: "https://www.example.com/pricing?source=docs",
metrics: ["visitors", "pageviews"],
dateRange: "last7days",
});
const localDevelopment = await clics.stats.queryStats({
projectId,
domain: "localhost",
metrics: ["visitors"],
dateRange: "last7days",
});
Real-time current visitors
A custom timestamp range of five minutes or less, with no dimensions and only the visitors metric, returns current visitors.
const now = new Date();
const fiveMinutesAgo = new Date(now.getTime() - 5 * 60 * 1000);
const currentVisitors = await clics.stats.queryStats({
projectId,
metrics: ["visitors"],
dateRange: [fiveMinutesAgo.toISOString(), now.toISOString()],
});
console.log(currentVisitors.results[0]?.metricValues.visitors);
Time series: every time dimension
Use a time dimension with KPI/time-series metrics. Do not combine it with a non-time breakdown dimension.
const timeDimensions = ["time", "time:hour", "time:day"] as const;
const timeSeries = await Promise.all(
timeDimensions.map((dimension) =>
clics.stats.queryStats({
projectId,
metrics: ["visitors", "pageviews", "bounce_rate"],
dateRange: "last30days",
timezone: "UTC",
dimensions: [dimension],
orderBy: [[dimension, "asc"]],
}),
),
);
Single breakdowns: every supported dimension
For regular traffic dimensions, use the four breakdown metrics. Custom-event and outbound-link dimensions use the event metrics shown separately.
const trafficBreakdowns = [
"event:page",
"event:hostname",
"visit:country",
"visit:device",
"visit:browser",
"visit:os",
"visit:referrer",
"referrer:ai_provider",
"visit:utm_source",
"visit:utm_medium",
"visit:utm_campaign",
"visit:utm_term",
"visit:utm_content",
] as const;
const allTrafficBreakdowns = await Promise.all(
trafficBreakdowns.map((dimension) =>
clics.stats.queryStats({
projectId,
metrics: ["visitors", "pageviews", "conversion_rate", "events"],
dateRange: "last30days",
dimensions: [dimension],
orderBy: [["visitors", "desc"]],
pagination: { limit: 25, offset: 0 },
}),
),
);
const customEvents = await clics.stats.queryStats({
projectId,
metrics: ["visitors", "events", "conversion_rate"],
dateRange: "last30days",
dimensions: ["event:name"],
orderBy: [["events", "desc"]],
});
const outboundLinks = await clics.stats.queryStats({
projectId,
metrics: ["visitors", "events", "conversion_rate"],
dateRange: "last30days",
dimensions: ["event:outbound_url"],
orderBy: [["events", "desc"]],
});
Two-dimensional breakdown
Use at most two non-time dimensions to compare segments.
const countryByDevice = await clics.stats.queryStats({
projectId,
metrics: ["visitors", "pageviews", "conversion_rate", "events"],
dateRange: "last30days",
dimensions: ["visit:country", "visit:device"],
orderBy: [
["visitors", "desc"],
["visit:country", "asc"],
],
pagination: { limit: 100, offset: 0 },
});
Every filter field and operator
Each entry below is a complete, valid single-filter request. Run one at a time, or combine filters that target different fields (for example country + device + page). contains and contains_not are valid operators; is_not and contains_not exclude matching values.
const filterRequests: FilterExpression[] = [
["is", "event:page", ["/pricing"]],
["is_not", "event:hostname", ["staging.example.com"]],
["contains", "event:name", ["purchase"]],
["contains_not", "event:outbound_url", ["https://untrusted.example"]],
["is", "event:goal", ["signup_completed"]],
["is", "visit:country", ["FR", "MA"]],
["is", "visit:device", ["Desktop"]],
["is", "visit:browser", ["Chrome"]],
["is", "visit:os", ["Windows"]],
["is", "visit:referrer", ["google.com"]],
["is", "referrer:ai_provider", ["chatgpt", "claude"]],
["is", "visit:utm_source", ["newsletter"]],
["is", "visit:utm_medium", ["email"]],
["is", "visit:utm_campaign", ["summer_launch"]],
["is", "visit:utm_term", ["privacy analytics"]],
["is", "visit:utm_content", ["hero_cta"]],
];
for (const filter of filterRequests) {
await clics.stats.queryStats({
projectId,
metrics: ["visitors", "pageviews"],
dateRange: "last30days",
filters: [filter],
});
}
Here is a practical compatible combination:
const frenchPricingTraffic = await clics.stats.queryStats({
projectId,
metrics: ["visitors", "pageviews"],
dateRange: "last30days",
filters: [
["is", "event:page", ["/pricing"]],
["is", "visit:country", ["FR"]],
["is_not", "visit:device", ["Tablet"]],
["contains", "visit:utm_source", ["newsletter"]],
],
});
AI-provider traffic
referrer:ai_provider groups known referrers under provider names, and can also be used as a filter.
const aiTraffic = await clics.stats.queryStats({
projectId,
metrics: ["visitors", "pageviews", "conversion_rate"],
dateRange: "last3months",
dimensions: ["referrer:ai_provider"],
filters: [["is", "referrer:ai_provider", ["chatgpt", "perplexity"]]],
orderBy: [["visitors", "desc"]],
});
Previous-period comparison and total rows
previousPeriod is for KPI queries with no dimensions. totalRows is useful for paged time-series or breakdown queries.
const comparedOverview = await clics.stats.queryStats({
projectId,
metrics: ["visitors", "pageviews", "bounce_rate"],
dateRange: "last30days",
include: { previousPeriod: true },
});
console.log(comparedOverview.comparison?.previousMetricValues.visitors);
console.log(comparedOverview.comparison?.changePercent.visitors);
const firstCountryPage = await clics.stats.queryStats({
projectId,
metrics: ["visitors", "pageviews"],
dateRange: "last30days",
dimensions: ["visit:country"],
include: { totalRows: true },
pagination: { limit: 50, offset: 0 },
});
console.log(firstCountryPage.meta.total_rows);
What it returns
type StatsQueryResponse = {
results: Array<{
// Positional values, aligned with the requested metrics and dimensions.
metrics: Array<number | null>;
dimensions: Array<string | number>;
// Prefer these named maps when reading a result in application code.
metricValues: Record<string, number | null>;
dimensionValues: Record<string, string | number>;
}>;
meta: Record<string, unknown>;
comparison?: {
previousMetricValues: Record<string, number | null>;
changePercent: Record<string, number>;
};
query: StatsQueryRequest; // normalized echo of your request
};
metrics in each result row align with the metrics array in your request. dimensions align with dimensions when provided. For most code, use the named maps instead: result.results[0]?.metricValues.visitors and result.results[0]?.dimensionValues["visit:country"] are clearer and do not depend on array order.
comparison is present only when include.previousPeriod: true is used on a KPI query without dimensions. previousMetricValues contains the previous range; changePercent contains the percentage change for each requested metric.
| Field | When present | Meaning |
|---|---|---|
results |
Always | One aggregate, time bucket, or breakdown row per query result |
results[].metricValues |
Always | Named values keyed by the metrics requested, such as visitors or pageviews |
results[].dimensionValues |
With dimensions |
Named values keyed by each requested dimension |
meta.timezone |
Always | IANA timezone used for calendar boundaries and time dimensions |
meta.total_rows |
With include.totalRows: true |
Total matching breakdown rows before pagination.limit and pagination.offset |
comparison |
With include.previousPeriod: true and no dimensions |
Equivalent metrics for the preceding period plus percentage change |
query |
Always | Normalized echo of the effective SDK request, useful for logging and debugging |
Notes
- Requires a paid plan: returns
403on free tiers - Set
domain: "localhost"to query dev traffic (project must haveallowLocalhost: true) - Use
include.previousPeriod: trueto compare week-over-week or month-over-month
API reference
See Query analytics stats for request and response schemas, status codes, and error types.