Skip to content
Back to skills

Counterapi

ASecurity

Track simple hit counters (page visits, installs, etc.) via counterapi.dev v2 — a lightweight alternative to GA4, PostHog, or Umami when you only need a few numeric counters.

  • 2 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added October 13, 2026
ai-agentsbashreactnodegitapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 13, 2026

npx -y skills add phucbm/skills --skill counterapi --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Counterapi?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Counterapi
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/phucbm-counterapi/badge)](https://www.skillsdirectory.com/skills/phucbm-counterapi)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: counterapi
description: Track simple hit counters (page visits, installs, etc.) via counterapi.dev v2 — a lightweight alternative to GA4, PostHog, or Umami when you only need a few numeric counters.
when_to_use: |
  - User wants to track visits, installs, downloads, or any simple numeric event
  - User wants a zero-config, no-dashboard alternative to GA4 / PostHog / Umami
  - User needs fire-and-forget analytics that never block rendering or throw errors
allowed-tools:
  - Read
  - Edit
  - Write
  - Bash
---

# counterapi.dev v2 — Simple Hit Counter Usage

## When to use
Use counterapi.dev when you need to track **simple numeric values** (visits, installs, downloads, clicks) without setting up GA4, PostHog, or Umami. It requires no SDK, no dashboard configuration, and no cookie consent — just HTTP calls with a Bearer token.

## Setup

### Env var
```env
NEXT_PUBLIC_COUNTERAPI_KEY=
```

### Base URL pattern
```
https://api.counterapi.dev/v2/{your-namespace}
```
Pick a namespace (e.g. your GitHub username). Counter names are arbitrary strings you define on first use.

## Core patterns

### Increment a counter (fire-and-forget)
```ts
const BASE = "https://api.counterapi.dev/v2/your-namespace";
const headers: HeadersInit = {
  Authorization: `Bearer ${process.env.NEXT_PUBLIC_COUNTERAPI_KEY ?? ""}`,
};
const isProd = process.env.NODE_ENV === "production";

export async function trackVisit(): Promise<void> {
  if (!isProd) return; // never increment in dev
  try {
    await fetch(`${BASE}/my-app-visits/up`, { headers });
  } catch {
    // silent fail — never block the user
  }
}
```
- Always gate on `NODE_ENV === "production"` before incrementing.
- Always wrap in `try/catch` and swallow the error.

### Read a counter
`GET /{namespace}/{counter-name}` returns `{ count: number }`.

### Read multiple counters in parallel
```ts
export interface Stats {
  visits: number;
  installs: number;
}

export async function getStats(): Promise<Stats> {
  try {
    const [visitsRes, installsRes] = await Promise.all([
      fetch(`${BASE}/my-app-visits`, { headers }),
      fetch(`${BASE}/my-app-installs`, { headers }),
    ]);
    const [v, i] = await Promise.all([
      visitsRes.json() as Promise<{ count: number }>,
      installsRes.json() as Promise<{ count: number }>,
    ]);
    return { visits: v.count, installs: i.count };
  } catch {
    return { visits: 0, installs: 0 }; // zeroes on any error
  }
}
```

## React display component
```tsx
"use client";
import { useEffect, useState } from "react";
import { getStats, type Stats } from "@/core/pwa";

export function StatsBadge() {
  const [stats, setStats] = useState<Stats | null>(null);

  useEffect(() => {
    getStats().then((data) => {
      if (data.visits > 0 || data.installs > 0) setStats(data);
    });
  }, []);

  if (!stats) return null; // render nothing while loading or on zero/error

  return (
    <span>
      {stats.visits.toLocaleString()} visits · {stats.installs.toLocaleString()} installs
    </span>
  );
}
```

## Key rules
- Never throw — all fetches must be wrapped in `try/catch` with silent fallback
- Never increment in development (`NODE_ENV !== "production"`)
- Render nothing (`return null`) while stats are loading or on error
- Use `Promise.all` for parallel reads to avoid waterfall

## Source reference
- `src/core/pwa.ts` — increment + read helpers
- `src/components/InstallBadge.tsx` — React display component
- Project: `phucbm/hieu-chu-han`

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.