Skip to content
Start in Cloud

Server-Side Tracking

Server-side tracking lets a backend, CMS plugin, reverse proxy, or import job send pageviews and events to HitKeep. Use it when the server already sees the original request and you need HitKeep dashboards to use the original visit time.

Use these endpoints for server-originated records. Browser pages keep using hk.js. Each request contains one pageview or event and requires an API client token.

AI crawler fetches use a separate site-scoped endpoint. Use this guide for trusted pageviews and events; use AI Fetch on AWS when you are forwarding GPTBot, ClaudeBot, PerplexityBot, or other crawler requests from edge or origin logs.

Use server-side tracking for:

  • replaying historical web server, reverse proxy, or CMS logs
  • forwarding live pageviews from an application server
  • recording server-confirmed events such as purchases, trial starts, webhook outcomes, or account upgrades
  • building first-party integrations where the backend has the original request URL, visitor IP, user agent, and event time
EndpointUse it forSite resolutionTime and visitor contextAuthentication
POST /ingestBrowser pageviews sent by hk.jsBrowser Origin headerHitKeep uses request time and derives the visitor IP from the HTTP request and trusted proxy settingsPublic tracker endpoint with CORS
POST /api/ingest/server/pageviewTrusted server-side pageviews, live forwarding, CMS plugins, reverse proxy forwarding, and historical pageview replayHostname in the submitted urlCaller sends timestamp, visitor_ip, and user_agent from the original request or log recordAPI client token with site.manage_data
POST /api/ingest/server/eventTrusted server-side custom events such as purchases, signup milestones, webhook outcomes, and account upgradesHostname in the submitted urlCaller sends timestamp, visitor_ip, and user_agent from the original request or source systemAPI client token with site.manage_data

The server-side pageview endpoint is beneficial because it has a contract for trusted non-browser clients. You can preserve the original analytics timestamp during log replay, pass transient visitor IP context for exclusions and geolocation, and send requests from jobs or backend services that do not have browser Origin headers.

Create an API client with site.manage_data for the site you want to write to. Team API clients are a good fit for shared integrations because they are not tied to one user’s account.

See API Clients for token creation and rotation.

Use the token as a bearer token:

Authorization: Bearer hk_live_...

Keep this token on the server. Store it in an environment variable or secrets manager.

The timestamp field is the analytics timestamp. For historical replay, send the time from the original log entry or source system.

If your customer imports logs on May 10, 2026 for traffic that happened on April 3, 2026, send the April timestamp. HitKeep places the pageview or event in April dashboard buckets.

{
"timestamp": "2026-04-03T12:30:45Z"
}

Use RFC3339 timestamps. UTC timestamps ending in Z are the simplest option.

HitKeep reads the hostname from the submitted url, normalizes www., finds the configured site, and then checks that the API client can manage data for that site.

For a configured site example.com, all of these resolve to that site:

https://example.com/docs
https://www.example.com/pricing?utm_source=newsletter

The url should be the visitor-facing page URL, not the HitKeep API URL.

Pageviews and events both require:

FieldMeaning
urlAbsolute visitor-facing URL. HitKeep uses the hostname for site resolution and the path/query for page context.
timestampCanonical analytics time in RFC3339 format. Historical replays should use the original visit time.
visitor_ipTrusted transient visitor IP context from the original request.
user_agentUser agent from the original visitor request.

Events also require name.

For pageviews, HitKeep reads utm_source, utm_medium, utm_campaign, utm_term, and utm_content from the query string in url.

HitKeep uses visitor_ip during ingest for IP exclusions, spam filtering, and pageview geolocation. It stores the derived analytics fields, not the visitor IP.

Server-side pageview and event ingest use the ingest rate limiter, not the general API limiter. The default is 20 requests per second per IP with a burst of 40. Increase HITKEEP_INGEST_RATE_LIMIT and HITKEEP_INGEST_BURST if a log forwarder, CMS plugin, or edge worker sends many records from one IP.

In clustered deployments, any node can receive these requests. Followers forward server-side ingest to the leader before API client authentication and queueing. The leader validates the token, resolves the site, and publishes accepted records to the embedded NSQ ingest pipeline.

Send one record per request. HitKeep does not expose a batch request format for these endpoints yet.

This example replays a pageview from April 3, 2026. The dashboard buckets the pageview at 2026-04-03T12:30:45Z, even if the replay job runs later.

Terminal window
curl -X POST "https://analytics.example.com/api/ingest/server/pageview" \
-H "Authorization: Bearer $HITKEEP_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://www.example.com/docs/start?utm_source=newsletter",
"timestamp": "2026-04-03T12:30:45Z",
"visitor_ip": "203.0.113.42",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15",
"referrer": "https://search.example/",
"language": "en-US",
"session_id": "018f5b11-43cb-7c0e-9d2d-3f0e7c68a001",
"page_id": "018f5b11-43cb-7c0e-9d2d-3f0e7c68a002"
}'

Successful ingest returns 202 Accepted with an empty response body. HitKeep queues accepted records through its embedded NSQ pipeline before DuckDB writes happen.

If a request exceeds the ingest limiter, HitKeep returns 429 Too Many Requests.

If session_id is omitted, HitKeep generates a new standalone session for that record.

If page_id is omitted for a pageview, HitKeep generates one.

Do not add utm_source, utm_medium, utm_campaign, utm_term, or utm_content as top-level JSON fields. HitKeep ignores those fields on this endpoint and reads campaign attribution from url.

Use the event endpoint when the historical source contains a named action instead of a pageview.

Terminal window
curl -X POST "https://analytics.example.com/api/ingest/server/event" \
-H "Authorization: Bearer $HITKEEP_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://www.example.com/pricing",
"timestamp": "2026-04-03T12:34:10Z",
"visitor_ip": "203.0.113.42",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15",
"name": "signup_started",
"properties": {
"plan": "pro",
"source": "pricing"
},
"session_id": "018f5b11-43cb-7c0e-9d2d-3f0e7c68a001"
}'

Events with a matching session_id can share context with pageviews in the same session. Standalone events still count in event analytics and event-based goals. Link events to pageviews with session_id when you want richer session context.

For live forwarding, use the current request time as the canonical timestamp and pass the request’s original IP and user agent.

const payload = {
url: `${req.protocol}://${req.headers.host}${req.originalUrl}`,
timestamp: new Date().toISOString(),
visitor_ip: req.ip,
user_agent: req.get('user-agent') || 'unknown',
};
await fetch('https://analytics.example.com/api/ingest/server/pageview', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.HITKEEP_API_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});

If the app is behind a proxy, make sure req.ip is the real client IP according to your framework’s trusted proxy settings.

Send dnt: true when the source system knows the visitor asked not to be tracked:

{
"url": "https://www.example.com/private",
"timestamp": "2026-04-03T12:30:45Z",
"visitor_ip": "203.0.113.42",
"user_agent": "Mozilla/5.0",
"dnt": true
}

HitKeep accepts the request and applies the same privacy behavior as browser tracking.