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.
When to use it
Section titled “When to use it”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
Choose the ingest endpoint
Section titled “Choose the ingest endpoint”| Endpoint | Use it for | Site resolution | Time and visitor context | Authentication |
|---|---|---|---|---|
POST /ingest | Browser pageviews sent by hk.js | Browser Origin header | HitKeep uses request time and derives the visitor IP from the HTTP request and trusted proxy settings | Public tracker endpoint with CORS |
POST /api/ingest/server/pageview | Trusted server-side pageviews, live forwarding, CMS plugins, reverse proxy forwarding, and historical pageview replay | Hostname in the submitted url | Caller sends timestamp, visitor_ip, and user_agent from the original request or log record | API client token with site.manage_data |
POST /api/ingest/server/event | Trusted server-side custom events such as purchases, signup milestones, webhook outcomes, and account upgrades | Hostname in the submitted url | Caller sends timestamp, visitor_ip, and user_agent from the original request or source system | API 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 a token
Section titled “Create a token”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.
Historical timestamps
Section titled “Historical timestamps”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.
How HitKeep chooses the site
Section titled “How HitKeep chooses the site”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/docshttps://www.example.com/pricing?utm_source=newsletterThe url should be the visitor-facing page URL, not the HitKeep API URL.
Required fields
Section titled “Required fields”Pageviews and events both require:
| Field | Meaning |
|---|---|
url | Absolute visitor-facing URL. HitKeep uses the hostname for site resolution and the path/query for page context. |
timestamp | Canonical analytics time in RFC3339 format. Historical replays should use the original visit time. |
visitor_ip | Trusted transient visitor IP context from the original request. |
user_agent | User 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.
Rate limiting and clusters
Section titled “Rate limiting and clusters”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.
Replay historical pageviews
Section titled “Replay historical pageviews”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.
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.
Replay historical events
Section titled “Replay historical events”Use the event endpoint when the historical source contains a named action instead of a pageview.
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.
Live server-side forwarding
Section titled “Live server-side forwarding”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.
Do Not Track
Section titled “Do Not Track”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.