Documentation
NeatStats offers simple, lightweight, privacy-first web stats. This page covers everything there is to know about the service.
Privacy
We count views, not people. We don’t store IP addresses, we don’t use cookies, and we don’t track individuals or their behavior.
NeatStats focuses on privacy by counting views, not tracking viewers. We don’t do any fingerprinting, nor do we perform any behavioral tracking of any kind. Our pageview counter doesn’t set any cookies or use any browser local storage.
We only store the most basic information about the page view: the URL of the page, the referrer (if the browser provides one), the user agent header, and the GeoName. GeoNames are coarse indicators of location, usually aligned with the center of the nearest city. NeatStats doesn’t collect or store precise location information—only GeoNames.
Here’s a sample database entry, so you can see the data that we store:
| Item | Sample | How it’s used |
|---|---|---|
| URL | https://neatstats.net/ | This is the URL of the page that was viewed. |
| Referrer | https://social.lol/ | This is the URL where the link was clicked to view the page. |
| GeoName ID | 4299276 | This is the GeoName ID representing the rough location from which the page was viewed. These are coarse representations of location, generally tied to nearby city-centers. (This one is Louisville, Kentucky, USA.) |
| User Agent | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Safari/605.1.15 | This is the User Agent header, which provides information about the web browser. (This one is Safari running on macOS.) |
| Timestamp | 1768837753 | This is the UNIX timestamp of the page view. (This one is “Mon, 19 Jan 2026 15:49:13 GMT”.) |
NeatStats members can see which of their web pages were viewed, how often they were viewed, and by what web browsers. They can also view a map showing the GeoNames from which their pages were viewed.
NeatStats does not store any information about individual people, and no attempt is made to identify people through any of the data collected.
How the view counter works
The view counter is a tiny script that runs once when a page loads. It doesn’t hide what it is, and it doesn’t pretend to be something else. It doesn’t use a tracking pixel, it isn’t disguised as part of your site, and it doesn’t load anything else. It sends one small message straight to neatstats.net, and that’s it. You can see the whole thing at https://neatstats.net/view.js.
Here’s what happens when the script is loaded:
- It uses the Beacon API, a standard browser feature made for sending short “this happened” notes without slowing the page down. (Older browsers that don’t support it use a plain fetch() request instead.)
- The message holds two things: the address of the page (just the domain and path, without any ?query or #fragment, which can contain search terms or login tokens), and the referrer, if the browser shares one.
- The browser adds the usual details, as it does with every request to every website: the user agent (the type of browser and operating system being used) and the IP address that the connection comes from.
- The IP address is only used to look up a rough nearby location. We look it up in a location database stored on our own server (no third-party service is involved), match it to the nearest GeoName (usually a city center) and then expunge the IP address. IP addresses are never saved with the page view or kept in our archives.
- The script doesn’t remember anything. There are no cookies, no local storage, no fingerprinting, and no IDs. Every view is recorded in isolation, and there’s no way to connect any two views to the same person.
- We don’t fight content blockers. If a visitor’s browser or content blocker stops the script, that’s their choice, and we fully respect it. We don’t proxy the script through your domain or disguise it to get around blockers.
Getting started
Setting up NeatStats only takes a few minutes. Here’s how it works:
- Sign in with your Neatnik ID.
- Add your site on the Your sites page. Just type in its domain, like example.com.
- Verify that it’s yours. We give you three ways to do this, so pick whichever is easiest for you (a DNS TXT record, a small file at /.well-known/neatstats, or a meta tag on your home page). Once you’ve added one, click the button to check it.
- Add the view counter to any page you want to count. Paste this into your HTML (near the end of the page is great):
<script src="https://neatstats.net/view.js"></script>
That’s it! Views will start showing up on your site’s stats page within a few seconds.
Each domain is its own site. If people visit both example.com and www.example.com, add both so you can see the views for each. If they’re really the same site, you can combine them.
The view counter is tiny, and it doesn’t set cookies or use browser storage. It sends the address of the page (without any ?query or #fragment, which can contain private things like search terms or login links) along with the referrer, if the browser provides one.
Combining example.com and www.example.com
If your site works at both example.com and www.example.com, you can combine them so you see all of your stats in one place. Here’s how:
- Add and verify both domains. (We check each one separately, since they don’t always belong to the same person.)
- Open the stats page of the one you’d like to keep as your main site, and look for Combine with www.example.com in the Preferences section.
Once they’re combined, your stats, updoots, and hit counters include both domains, and the two count as one site. We still keep each domain’s views separately behind the scenes, so you can split them apart again any time, and nothing gets mixed up.
Your stats
Each site has its own stats page, which you can reach from the menu. Here’s what you’ll find there:
- Views: a chart of how many views your site has received.
- Browsers and platforms: which web browsers and operating systems your visitors use.
- Top pages: your most-viewed pages.
- Top referrers: the sites and pages that sent people your way.
- Map and countries: roughly where your views came from (by GeoName, never a precise location).
- Updoots: how many updoots each of your pages has received.
You pick a time range for most stats, so you can see the last 24h, 7d, 30d, 6m, or 1y. We’ll remember the range you pick for each one. The shorter ranges are up to the minute, and the longer ones (6 months and 1 year) are updated once a day.
Want to see more than the top 10? Use the “View all” links under Top pages and Top referrers.
Exporting your data
Your stats are yours, so you can download all of them whenever you like. Use Export your data in a site’s preferences (at the bottom of its stats page) to get a ZIP file with:
- views.csv: every page view NeatStats has recorded for the site, with its time, page, referrer, browser, platform, rough location, and user agent.
- updoots.csv: every updoot on the site’s pages, with its count.
CSV files open in any spreadsheet app. There are no IP addresses in the export, because we never store them. Most exports are ready right away; for a really big site, we’ll put it together in the background and email you a link. Download links work for 48 hours, and only while you’re signed in. (Prefer JSON, or want to automate it? The API has you covered.)
Numberless mode
Sometimes it’s nicer not to know exactly how many people are looking. Turn on numberless mode in a site’s preferences (at the bottom of its stats page), and you’ll see rankings and trends instead of view counts:
- The views chart keeps its shape but loses its numbers.
- Browsers and platforms show names without percentages, and their “View all” lists show a rank instead of views.
- Top pages and top referrers are ranked (1., 2., 3., and so on) instead of counted.
- The map says “View(s) from” a place, and countries are shown without counts.
- The executive summary skips the total, and traffic alert emails tell you what’s taking off (and where people came from) without the numbers.
Updoots and hit counters keep their numbers, since the number is the whole point of them. The API isn’t changed, except that its charts and summary follow the setting too.
Filtering referrers
Sometimes your referrer list fills up with things you don’t care about, like links from your own site or referrer spam. You can hide them!
- To hide links from your own site, check Show only external referrers in the Preferences section at the bottom of your site’s stats page.
- To hide other referrers on one site, add them to Filtered referrers in that same Preferences section.
- To hide referrers on all of your sites at once, add them on your Preferences page.
Add one pattern per line (up to 100). Here’s how patterns work:
| Pattern | What it hides |
|---|---|
| example.com | Links from example.com and all of its subdomains, like www.example.com. |
| *spam* | The * matches anything, so this hides links from any domain with “spam” in it. |
| example.com/forum | Add a path to be more specific. This hides links from that part of example.com only. |
Capitalization and a leading https:// don’t matter. Filters only hide referrers from your lists—your view counts stay the same, and nothing is deleted, so you can change your filters any time.
Traffic alerts
Gone viral? We’ll let you know! Traffic alerts are on for every site by default, so we’ll email you when one of your pages takes off. If you’d rather not get them, uncheck Email me when one of my pages suddenly gets a lot more attention than usual in the Preferences section of your site’s stats page (or use the link in any alert).
Here’s what counts as “taking off”: in the last hour, a page got at least 50 views, at least five times its usual traffic, and a noticeable share of your whole site’s traffic. (That last part keeps busy sites from getting an alert for every little blip.) The email tells you which page it is and where the visitors are coming from, so you can take a look and maybe join the conversation.
You’ll get at most one alert per page per day, and at most three per site per day. Alerts go to the email address on your Neatnik ID, and every alert has a link to turn them off. They only work for sites you’ve verified.
Updoots
Updoots turn any emoji into a button on your pages. Each click makes the number go up. Use Updoots for likes, votes, or your own creative ideas. Here’s one you can test right now:
To add an Updoot to a page on your site, paste this into the source:
<script src="https://neatstats.net/updoot.js" defer></script>
<span class="updoot" data-emoji="⭐️"></span>
You only need the script once per page, and you can add as many updoots as you like. Here are the options:
| Option | What it does |
|---|---|
| data-emoji="⭐️" | The emoji to show. Use any emoji you like! (If you leave it out, you’ll get a star.) |
| data-key="…" | A name for the updoot. Each page counts its updoots separately, so you only need this if you want two updoots with the same emoji on one page. |
| data-page="/blog/my-post" | Counts the updoot for a different page on your site. It’s handy for a list of posts: give each post’s updoot the address of the post, and it’ll share its count with the updoot on the post itself. |
| data-slowmode="true" | Allows one updoot per IP address per day (it resets at midnight UTC), and turns off press-and-hold. See how slow mode works below. |
A few more things to know:
- You can press and hold to send a bunch of updoots at once (up to 100). Wheeee!
- Updoots are counted for each page, and the address is used without its ?query or #fragment. If you’ve combined example.com and www.example.com, an updoot shows the same total on both.
- An updoot that hasn’t been clicked yet shows the emoji (without any number beside it, because showing a zero there would be kinda sad, you know?). The count appears with the first click.
- If the page isn’t on a site you’ve added to NeatStats, the updoot will show up grayed out, with a note explaining that it won’t be counted.
How slow mode works (without storing IP addresses)
To know whether someone has already updooted today, slow mode needs some way to recognize a repeat visit, but we don’t want to store IP addresses. So instead of the IP address, we keep a hash of it.
Specifically, it’s an HMAC (SHA-256) of the IP address, using a secret key that includes today’s date (in UTC), and we only keep the first part of the result. Here’s what that means:
- The IP address is never stored. Only the hash is, and it can’t be turned back into the IP address. Since the key is secret, nobody can work out which hash belongs to which IP address by trying them all, either.
- The key changes every day. At midnight UTC, the date in the key changes, so the same IP address gets a completely different hash. That’s what resets slow mode each day, and it means there’s no way to link one day’s visits to the next.
- The hashes don’t stick around. They’re deleted after a week, and they’re only ever used for limits like this one (plus a generous limit on all updoots that stops automated floods). They never appear in your stats.
Because it’s based on the IP address, people sharing a connection (like a home or office network) share one daily updoot in slow mode, and someone whose IP address changes might get another one. Slow mode is meant to keep things friendly and fair, not to be bulletproof.
You can see all of your updoots in the Updoots section of your site’s stats page, sorted by the most updoots, the most recent activity, or by page.
Hit counters
Hit counters, baby! Show off how many views a page (or your whole site) has had, in whichever style you like. Here are all of them, counting the views for neatstats.net:
| Style | What it looks like |
|---|---|
| plain | |
| rainbow | |
| odometer | |
| flip | |
| lcd | |
| badge | |
| 90s | |
| roman |
To add one, paste this into any page on a site you’ve added to NeatStats:
<script src="https://neatstats.net/counter.js" defer></script>
<span class="neatstats-counter" data-style="odometer"></span>
You only need the script once per page, and you can add as many counters as you like. Counters show the views that NeatStats has counted, so be sure the view counter is on your pages, too. Here are the options:
| Option | What it does |
|---|---|
| data-style="plain" | The style to use: plain (the default), odometer, flip, lcd, badge, 90s, roman, or rainbow. (In roman, numbers past 3,999 use a bar over the thousands, the way the Romans did it. Hover over one to see the number.) |
| data-scope="page" | Use page (the default) to count the views of the page the counter is on, or site to count every view your site has ever had. |
| data-digits="…" | For odometer, flip, lcd, and 90s: how many places to show. By default, you’ll get at least 6, plus a spare one once your count reaches 6 digits (to show there’s room to grow). Use a number to pick your own minimum, or 0 for no extra places. |
| data-label="…" | Text to show before the number. For badge, this is the left side of the badge (it says “views” if you leave it out). |
| data-label-after="…" | Text to show after the number. |
| data-ordinal="true" | Shows the number as a place, like “1,235th.” (It adds one, so the person reading it is the next visitor.) |
| data-animate="count" | Counts up from zero when the page loads. This works with plain, lcd, badge, 90s, roman, and rainbow. (The odometer and flip styles always animate.) |
| data-color="green" | For badge: the color of the number’s side. Use green (the default), blue, red, orange, purple, pink, gray, black, or a hex color like #ff6600. |
Want an old-school greeting? Try this:
<span class="neatstats-counter" data-scope="site" data-ordinal="true"
data-label="You’re the " data-label-after=" visitor!"></span>
That shows:
A few more things to know:
- Counters include every view up to a few seconds ago. (A visitor’s own view shows up the next time they load the page.)
- Counters count views for the domain of the page, so a counter on www.example.com shows the views for www.example.com. (If you’ve combined it with example.com, it counts both.)
- If the page isn’t on a site you’ve added to NeatStats, the counter stays empty.
- If someone has asked their device to reduce motion, the counter skips the animation and shows the number right away.
- Want to style it yourself? The number is always inside <span class="neatstats-counter-value">, and each counter gets a class for its style, like neatstats-counter--odometer. The plain style doesn’t add any styling at all, so it’s a great place to start.
API
You can use the NeatStats API to get your stats as JSON, as often as you’d like. It’s great for dashboards, reports, or anything else you dream up. You can also use it to send views and updoots to NeatStats, so you can count all kinds of things.
To get started, generate an API key on your account page. We only show the key once, when it’s created, so be sure to copy it somewhere safe. (If you lose it, you can generate a new one. That will turn off the old key right away.)
Then send your key with each request, like this:
curl https://neatstats.net/api/v1/sites/example.com/views?range=7d \
-H "Authorization: Bearer YOUR_API_KEY"
Each request is for one of your sites, and your key only works for sites on your account. Here’s what you can get:
| Endpoint | What you get |
|---|---|
| /info | Basic info about your site and a summary of its views. |
| /views | Your views over time, plus a chart. |
| /browsers | Views by web browser. |
| /platforms | Views by operating system. |
| /pages | Your top pages. |
| /referrers | Your top referrers (with your referrer filters applied). |
| /map | Locations for a map, by GeoName. |
| /countries | Views by country. |
| /updoots | Your pages with updoots, and their counts. |
Most endpoints accept a range (24h, 7d, 30d, 6m, or 1y), and many accept a limit and a sort. Check out the full API reference for all of the details and sample responses.
Counting other things
NeatStats isn’t just for web pages! With your API key, you can send us views and updoots from anywhere you can make a web request: your server, an app, a script, or a little gadget on your desk. Here are a few ideas:
- Count downloads by recording a view for /downloads/app.zip each time someone grabs your file.
- Count app launches, newsletter signups, or form submissions the same way, with whatever paths you like. (They don’t need to be real pages.)
- Use an updoot as a simple counter, or set it to a number from somewhere else.
Here’s how to record a view:
curl https://neatstats.net/api/v1/sites/example.com/views \
-H "Authorization: Bearer YOUR_API_KEY" \
-d "path=/downloads/app.zip"
Views you send show up in your stats just like any others, within a few seconds. You can include a referrer, a user agent (for the browser and platform breakdowns), and an IP address (for the map and countries). As always, we only use an IP address to find an approximate location, and we never store it.
| Endpoint | What it does |
|---|---|
| POST /views | Records a view (or up to 100 at once). |
| POST /updoots | Adds to an updoot’s count. (If the updoot doesn’t exist yet, we’ll create it.) |
| PUT /updoots | Sets an updoot’s count to an exact number. |
| DELETE /updoots | Deletes an updoot. |
Sending data needs your API key, even if you’re signed in. The full API reference has all of the details.
Need help?
If you run into any issues or have any questions, send an email to help@neatnik.net and we’ll give you a hand.
License & Credits
NeatStats uses the handy flag icons maintained by Panayiotis Lipiridis. (License)