Get the access log profile of one IP
Returns totals, status classes, latency, first and last seen, the top 5 hosts and paths, the top 3 user agents and a hit series for one client IP. The series has hourly buckets for ranges up to 7 days and daily buckets above, and tz sets local days at any range length. It reads the raw rows, which go back only ANALYTICS_RAW_RETENTION_DAYS days, 180 by default. An IP without requests in the range gets zero request counts and empty lists, not a 404.
session<token>Session cookie authentication.
startDate?|Start of the range, inclusive, as ISO 8601 with a timezone, such as 2026-11-01T00:00:00Z. Requires endDate; omit with window.
endDate?|End of the range, exclusive, as ISO 8601 with a timezone, such as 2026-11-02T00:00:00Z. Requires startDate; omit with window.
window?|A rolling window ending now, such as 15m, 24h or 7d, up to 365 days. Use positive whole minutes, hours or days. Cannot be combined with explicit time bounds.
ipAddress*stringThe client IP to profile. A value that is not an IP gets a 400.
tz?|An IANA timezone such as Europe/Oslo. Daily buckets then start at midnight in that zone for ranges of 30 days or less, and longer ranges keep UTC days unless the endpoint's description says otherwise. Hourly buckets ignore it, so on ranges that get hourly buckets it changes nothing. An unknown zone gets a 400. Defaults to UTC.
Request fulfilled, document follows
application/json- response
The access log profile of one client IP for a time range.
An IP without requests in the range gets zero request counts and empty lists rather than a 404. Request times are in seconds over the timed requests.
asn*|The ASN of the IP's newest request in the range that had one.
asnCategory*|drop when the ASN is on the Spamhaus ASN-DROP list, hosting when it is on the bad-asn-list of hosting and datacenter networks, and other for every other ASN. other does not mean residential. Null without ASN data.
"drop""hosting""other"nullasnOrganization*|The network's name from the GeoLite2 ASN database. Null when it has none.
avgRequestTime*number|nulldistinctPaths*integerHow many different request targets the IP sent.
endDate*stringThe request's endDate, echoed as ISO 8601.
errorRate*numberThe share of requests with a 4xx or 5xx status, from 0 to 1.
firstSeen*|The IP's first request in the range, as ISO 8601 in UTC.
granularity*stringhourly for ranges up to 7 days, daily above.
"hourly""daily"hosts*array<>Up to 5 hosts, most hits first.
ipAddress*stringlastSeen*|The IP's last request in the range, as ISO 8601 in UTC.
malformedRequests*integerLines from the IP that GeoMetrikks flagged as malformed, counted by when it stored them rather than by their log time.
p95RequestTime*number|nullpaths*array<>Up to 5 host and path pairs, most hits first.
peak*|The bucket with the most hits, the earliest one on a tie.
series*array<>Hits per bucket, oldest first. Buckets without hits are left out.
startDate*stringThe request's startDate, echoed as ISO 8601.
status2xx*integerstatus3xx*integerstatus4xx*integerstatus5xx*integertimedRequests*integerRequests with a measured request time, leaving out WebSocket upgrades and status 0 lines. Aggregated buckets older than the raw retention window may predate that rule. Those still count upgrades and status 0 lines, and the oldest count every request, timed or not.
totalBytes*integertotalRequests*integeruserAgents*array<>Up to 3 user agents, most hits first.
curl -X GET 'https://geometrikks.example.com/api/v1/analytics/ip-profile?startDate=2026-11-01T00%3A00%3A00Z&endDate=2026-11-02T00%3A00%3A00Z&ipAddress=string&tz=Europe%2FOslo'{ "asn": 0, "asnCategory": "drop", "asnOrganization": "string", "avgRequestTime": 0, "distinctPaths": 0, "endDate": "string", "errorRate": 0, "firstSeen": "string", "granularity": "hourly", "hosts": [ { "errorHits": 0, "hits": 0, "host": "string" } ], "ipAddress": "string", "lastSeen": "string", "malformedRequests": 0, "p95RequestTime": 0, "paths": [ { "errorHits": 0, "hits": 0, "host": "string", "url": "string" } ], "peak": { "errorHits": 0, "hits": 0, "timestamp": "string" }, "series": [ { "errorHits": 0, "hits": 0, "timestamp": "string" } ], "startDate": "string", "status2xx": 0, "status3xx": 0, "status4xx": 0, "status5xx": 0, "timedRequests": 0, "totalBytes": 0, "totalRequests": 0, "userAgents": [ { "hits": 0, "userAgent": "string" } ]}Get geo event counts per hour or day GET
Returns geo events and unique IPs, countries and cities for each bucket that has events, oldest first. It reads the hourly or daily aggregates at any range length, so the unique counts are estimates, and those reads count whole hours or UTC days, from the one that holds `startDate` through the last one that starts before `endDate`. With `granularity=daily` and a `tz` other than UTC on a range of 30 days or less, those reads count local days instead. They start at the local midnight of the day holding `startDate` and run through the last hour that starts before `endDate`.
Get the exact traffic summary from the raw rows GET
Returns the same figures as `/api/v1/analytics/summary`, counted from the raw access log and geo event rows of the exact range, so the unique counts are exact. It gets slower as the range grows, and the raw rows go back only ANALYTICS_RAW_RETENTION_DAYS days, 180 by default. `percentChanges.malformedRate` is always null here.