Getting started

Updated on: April 2, 2026

Introduction

Page Views API is an open-source, serverless solution to track how many times a specific page has been visited – without any dashboards, accounts, or complex setup.

It provides two things:

  • A tracking script you drop into any website to count visits
  • A REST API to read those counts and display them anywhere

The model is Precision Tracking – you explicitly tell the script exactly which path to track. This means no accidental tracking of every route and no noisy data.


Usage

Add the Tracking Script

Drop a single <script> tag into any HTML page. That's it.

<script
  src="https://page-views-api.ratneshc.com/script"
  data-site="example.com"
  data-path="/blog/my-post"
  defer
></script>

Script Attributes

AttributeRequiredDescription
srcYesAlways https://page-views-api.ratneshc.com/script.
data-siteYesYour unique site identifier. Use your domain (e.g. example.com). Protocol and trailing slashes are stripped automatically.
data-pathYesThe specific page path to track (e.g. /, /blog/my-post). The script only fires when the current URL matches this value.
data-debugNoSet to "true" to enable console logging. Useful during local development and integration testing.

How the script works

When a page loads, the script:

  1. Reads data-site and data-path from its own <script> tag
  2. Normalizes the current URL path (strips trailing slashes, collapses double slashes)
  3. Compares it to data-path – if they don't match it exits silently
  4. If they match, it calls GET /api/v1/track?site=…&path=… using fetch with keepalive: true
  5. The API deduplicates the visit and increments the counter

SPA & Client-side Navigation

The script is fully compatible with Single Page Applications (Next.js, React, Vue, etc.) where the browser never fully reloads on navigation.

It hooks into the browser's history API to detect route changes:

  • history.pushState – intercepted so the script fires on every client-side navigation
  • popstate – listened to handle browser Back / Forward buttons

Each navigation triggers track(), which re-checks the path match. The counter only increments when the new URL matches data-path.

Using data-debug

<script
  src="https://page-views-api.ratneshc.com/script"
  data-site="example.com"
  data-path="/"
  data-debug="true"
  defer
></script>

With data-debug="true", you'll see logs like:

[PV] Tracking visit: /
[PV] Path mismatch, skipping: { current: "/about", target: "/" }

The global window.__PV__ object is also available for debugging:

PropertyDescription
window.__PV__.statusCurrent state: loading, ready, missing-script-tag, missing-parameters, or error
window.__PV__.trackThe tracking function – call manually to trigger a track

Fetch View Counts

Once views are being tracked, you can read the count for any path using the views endpoint.

Endpoint

GET https://page-views-api.ratneshc.com/api/v1/views?site=example.com&path=/

Query Parameters

ParameterTypeRequiredDescription
sitestringYesYour site identifier (same value used in data-site).
pathstringYesThe page path to fetch view count for.

Response

{
  "views": 1234
}

Examples

const res = await fetch(
  "https://page-views-api.ratneshc.com/api/v1/views?site=example.com&path=/"
);
 
const data = await res.json();
// { views: 1234 }
 
console.log(`Total views: ${data.views}`);

How It Works

Here's what happens end-to-end when a visitor lands on a tracked page:

  1. The browser loads your <script> tag and executes the tracking script
  2. The script normalizes the path and checks it matches data-path
  3. A GET /api/v1/track request is made with site and path
  4. The API generates a short-lived visitor ID – a 16-character SHA-256 hash derived from the visitor's IP address and User-Agent string
  5. The visitor ID is checked against a Redis deduplication key for that site + path
  6. If the visitor is new within the window, the counter is incremented and the key is set with a TTL
  7. If the visitor already exists in the window, the request is acknowledged but the counter is not incremented

Deduplication & Privacy

To keep view counts accurate and respect visitor privacy:

  • Deduplication window: A visitor is counted once every 30 minutes per site + path combination
  • No IP storage: IP addresses are never stored. They are combined with the User-Agent and immediately hashed (SHA-256), producing a short-lived token that expires with the TTL
  • No cookies: The script sets no cookies and reads no local storage
  • No personal data: Nothing that can identify an individual is persisted

API Input Normalization

Both endpoints silently normalize the site and path values before processing, so minor formatting inconsistencies don't create duplicate counters.

site normalization

InputNormalized
https://example.comexample.com
http://example.com/example.com
EXAMPLE.COMexample.com

path normalization

InputNormalized
/blog//blog
blog/blog
//blog//post/blog/post
(empty)/

Rate Limiting

To protect the API from abuse:

  • Limit: 60 requests per 60-second window
  • Scope: Per IP address, scoped to the site + path being tracked
  • Response on limit exceeded: 429 Too Many Requests

Error Handling

Both endpoints return standard HTTP status codes and a consistent JSON error body.

Error Response Format

{
  "error": "Missing or empty required query parameter: site"
}

Status Codes

StatusMeaningWhen it happens
200OKRequest succeeded.
400Bad RequestA required query parameter (site or path) is missing or empty.
429Too Many RequestsRate limit exceeded for this IP + site + path.
500Internal Server ErrorUnexpected server-side error.

CORS

Both endpoints support cross-origin requests out of the box.

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, OPTIONS

You can call the views endpoint directly from a browser, a React component, or any client-side code without needing a proxy or server-side wrapper.