API Documentation
Welcome! This is the documentation for the NeatStats API, which lets you pull your own site’s view statistics programmatically.
Authentication
All requests require authentication with an API key, which you can find on your account page. You can use the key with an Authorization: Bearer <token> header (where the token is your API key), or as the password in an HTTP Basic authentication request (the username can be anything, as long as the password is your API key).
Every request is scoped to a single site, specified in the URL path. Your API key must belong to the account that owns that site (you can only retrieve stats for sites you own).
The API supports cross-origin requests (CORS), so it's safe to call directly from browser-based JavaScript.
Common parameters
Most endpoints accept a range parameter, and several also accept a limit parameter. There’s also a sort parameter that can be used where it makes sense. Here’s how these work:
rangedefines how far back to look. Acceptable values are24h,7d,30d,6m, and1y. Defaults to24hif omitted.limitdefines how many results to return, for endpoints that return a breakdown of many items. Defaults to0, which means unbounded (you get everything). Pass a positive integer to cap the number of results to your top N by view count.sortdefines the order in which the data is provided. Defaults tocount, but acceptschronological(served in descending order).
Errors
When something goes wrong, you'll get a non-200 http_response_code along with an error field describing what happened:
{
"http_response_code": 400,
"http_response_text": "Bad Request",
"error": "Invalid range. Must be one of: 24h, 7d, 30d, 6m, 1y."
}
Endpoints
The API currently offers endpoints in a single category: Sites.
Sites
Site endpoints support retrieving stats for your sites, identified by the site domain in the URL path.
Get site info
GET /api/v1/sites/site/info
Fetches basic info about a site, like its verification status and a lifetime summary of its views.
Sample response
{
"http_response_code": 200,
"http_response_text": "OK",
"site": {
"domain": "example.com",
"verified": true,
"verified_at": "2026-01-19 03:12:07 UTC",
"verified_by": "dns",
"summary": {
"view_count": 303908,
"first_view_at": 1768863455,
"last_view_at": 1788524730,
"executive_summary": "Your site has a total of 303,908 views since 2026-01-19. The most recent view was 9 minutes ago."
}
}
}
verified_bywill be one ofdns,file, ormeta, matching whichever verification method you used. If the site hasn't been verified yet,verifiedisfalseandverified_at/verified_byare bothnull. If the site has no views yet,summary.view_countis0andfirst_view_at/last_view_at/executive_summaryare allnull.
Get page views
GET /api/v1/sites/site/views
Fetches a time-series of view counts, bucketed automatically based on the requested range (15-minute buckets for 24h, hourly for 7d, every six hours for 30d, daily for 6m, and weekly for 1y). Also returns an SVG line chart of the same data. And the total number of views recorded for the requested range.
Accepts: range
Sample response
{
"http_response_code": 200,
"http_response_text": "OK",
"site": "example.com",
"range": "24h",
"bucket_seconds": 900,
"start": 1788490800,
"end": 1788577200,
"total_views": 13,
"data": [
{ "ts": 1788490800, "views": 4 },
{ "ts": 1788491700, "views": 7 },
{ "ts": 1788492600, "views": 2 }
],
"chart_svg": ""
}
chart_svgis trimmed above for readability — in a real response it's the full markup for a self-contained line chart you can drop straight into a page.
Get browser breakdown
GET /api/v1/sites/site/browsers
Fetches a breakdown of views by browser, along with an SVG donut chart of the same data.
Accepts: range, limit
Sample response
{
"http_response_code": 200,
"http_response_text": "OK",
"site": "example.com",
"range": "7d",
"data": {
"Chrome": 412,
"Safari": 298,
"Firefox": 54,
"Edge": 22,
"Yandex": 9
},
"chart_svg": ""
}
Get platform breakdown
GET /api/v1/sites/site/platforms
Fetches a breakdown of views by platform (operating system), along with an SVG donut chart of the same data.
Accepts: range, limit
Sample response
{
"http_response_code": 200,
"http_response_text": "OK",
"site": "example.com",
"range": "7d",
"data": {
"Macintosh": 301,
"Windows": 187,
"Android": 122,
"iPhone": 98,
"Linux": 41
},
"chart_svg": ""
}
Get top pages
GET /api/v1/sites/site/pages
Fetches the most viewed pages on your site, ranked by view count.
Accepts: range, limit
Sample response
{
"http_response_code": 200,
"http_response_text": "OK",
"site": "example.com",
"range": "7d",
"total_paths": 47,
"data": {
"/": 1204,
"/about": 389,
"/blog/hello-world": 201
}
}
total_pathsis the total number of distinct pages that had at least one view in the requested range, even iflimitcapped thedataobject to fewer than that.
Get top referrers
GET /api/v1/sites/site/referrers
Fetches the sites and pages that sent you the most traffic, ranked by view count.
Accepts: range, limit
Sample response
{
"http_response_code": 200,
"http_response_text": "OK",
"site": "example.com",
"range": "7d",
"total_referrers": 132,
"data": {
"https://google.com/": 1204,
"https://reddit.com/": 897,
"https://neatnik.net/": 213
}
}
total_referrersworks the same way astotal_pathsabove (it's the true total, independent of anylimityou applied).
Get map data
GET /api/v1/sites/site/map
Fetches per-location view counts, with coordinates suitable for plotting on a map.
Accepts: range, limit
Sample response
{
"http_response_code": 200,
"http_response_text": "OK",
"site": "example.com",
"range": "24h",
"total_locations": 62,
"data": [
{
"geonameid": 5128581,
"name": "New York City",
"country_iso": "US",
"latitude": 40.71427,
"longitude": -74.00597,
"views": 214
},
{
"geonameid": 2643743,
"name": "London",
"country_iso": "GB",
"latitude": 51.50853,
"longitude": -0.12574,
"views": 88
}
]
}
- With no
limitspecified, this returns every distinct location that’s had at least one view in the requested range. At a range like1ythat can be a lot of locations. If you’re rendering pins on a map yourself, you may want to either pass alimitor use marker clustering on your end to keep things responsive.
Get country totals
GET /api/v1/sites/site/countries
Fetches view totals grouped by country, using ISO 3166-1 alpha-2 country codes.
Accepts: range, limit
Sample response
{
"http_response_code": 200,
"http_response_text": "OK",
"site": "example.com",
"range": "24h",
"total_countries": 18,
"data": {
"US": 512,
"GB": 88,
"DE": 41
}
}
You've reached the end of our API documentation.
We hope this was helpful. If you have questions, or there's an endpoint you'd find useful that isn't here yet, please let us know.