Skip to content

Ecency analytics

How Ecency uses Plausible for cookie-free analytics, the public post views endpoint and connecting your own Plausible instance.

Ecency counts page views with Plausible Analytics, a privacy-friendly tool that doesn’t use cookies. This page covers the public endpoint for a post’s view stats. It also shows how to connect a self-hosted copy of the Ecency web app to your own Plausible Community Edition (CE) server.

Anyone can read the view stats of a single post. This is the endpoint behind the stats shown on Ecency posts.

const res = await fetch("https://ecency.com/api/stats", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
url: "/@alice/my-first-post",
metrics: ["visitors", "pageviews", "visit_duration"],
date_range: "30d",
}),
});
const stats = await res.json();
if (!res.ok || typeof stats.status === "number") {
// Error: see "Errors" below. Some errors come back as HTTP 200 with a status in the body.
}

Request body:

Field Required Allowed values
url Yes The path of one post. It must end in /@author/permlink (a community or tag prefix is fine, such as /hive-123/@alice/my-first-post). Query strings and # fragments are ignored. It may be URL-encoded.
metrics Yes 1 to 4 unique values from visitors, visits, pageviews, visit_duration.
dimensions No Empty, or one of visit:source, visit:device, visit:country_name.
date_range No day, 7d, 30d, month, 6mo or 12mo (the default), or a pair of dates such as ["2026-01-01", "2026-10-01"]. all is not accepted.
filterBy No event:page (default, the post was viewed during the visit) or visit:entry_page (the visit started on the post).

The response is Plausible’s Stats API v2 query result, passed through as is. For example:

{
"results": [{ "metrics": [120, 185, 42], "dimensions": [] }],
"meta": {},
"query": { "site_id": "ecency.com", "metrics": ["visitors", "pageviews", "visit_duration"] }
}

Each metrics array follows the order of the metrics you asked for. With a dimension, you get one result per value (for example one per country).

Errors:

  • 400 for invalid JSON, metrics, dimensions or date range.
  • 403 when url isn’t a single post path (for example /, /trending or a profile).
  • 502 or 504 when the analytics service fails or times out. Plausible’s own error status is passed through too.

Two errors come back with HTTP status 200 and the real status in the body, so check the body too, not just res.ok:

  • {"status": 400} when url is missing.
  • {"status": 404} when stats are turned off on that server (Plausible isn’t enabled).

A successful response never has a top-level status field.

Results are cached for a few minutes, so cache them on your side as well and don’t poll.

The SDK wraps this endpoint as getPageStatsQueryOptions(url, dimensions?, metrics?, dateRange?) in @ecency/sdk. See the SDK docs.

Profile-wide stats come from a separate route, /api/profile-insights. It needs a signed-in user and only returns numbers to the profile’s owner or to an Ecency Pro member.

This part is for people running their own copy of the Ecency web app (vision-web). Out of the box the app reports to Ecency’s own Plausible instance, so change it before you go live.

Follow the setup in Plausible’s official Community Edition repository. In short, on a host with Docker and the Docker Compose plugin:

Terminal window
# Use the current release tag shown in the CE README
git clone -b <release-tag> --single-branch https://github.com/plausible/community-edition plausible-ce
cd plausible-ce
echo "BASE_URL=https://plausible.example.com" >> .env
echo "SECRET_KEY_BASE=$(openssl rand -base64 48)" >> .env
echo "HTTP_PORT=80" >> .env
echo "HTTPS_PORT=443" >> .env
docker compose up -d

Check the CE README for the full list of options and for upgrade notes between major versions. Then open your BASE_URL, create an account and add your site’s domain.

Variable Used for
PLAUSIBLE_API_HOST Your Plausible URL, for example https://plausible.example.com.
PLAUSIBLE_DOMAIN The site domain you added in Plausible, for example example.com.
PLAUSIBLE_API_KEY A Stats API key, created in Plausible under your account settings, API keys. It’s a read key. Only the server uses it, for /api/stats and /api/profile-insights. Page views are sent by the tracking script and don’t need a key.

These feed the visionFeatures.plausible block in apps/web/src/config/config.ts:

plausible: {
enabled: true,
host: process.env.PLAUSIBLE_API_HOST || "<Ecency's instance>",
siteId: process.env.PLAUSIBLE_DOMAIN || "ecency.com",
apiKey: process.env.PLAUSIBLE_API_KEY
}

Set enabled: false to turn off the stats endpoints.

3. Point the tracking script at your server

Section titled “3. Point the tracking script at your server”

The environment variables don’t change the tracking script. You have to edit two files:

  • apps/web/src/app/layout.tsx loads the script with a hardcoded domain:

    <Script defer data-domain="ecency.com" data-api="/pl/api/event" src="/pl/js/script.js" />

    Change data-domain to your PLAUSIBLE_DOMAIN.

  • apps/web/next.config.js has two rewrites that proxy /pl/js/script.js and /pl/api/event to Ecency’s Plausible server. Change both destinations to your Plausible host:

    { source: "/pl/js/script.js", destination: "https://plausible.example.com/js/script.js" },
    { source: "/pl/api/event", destination: "https://plausible.example.com/api/event" },

Because the script and events go through /pl/ on your own domain, the browser never contacts a third-party analytics host.

Redeploy the web app and open a few pages. Watch them appear in your Plausible dashboard’s realtime view. Then open a post and call /api/stats for it to confirm the Stats API key works.