This API allows you to create simple numeric counters. IaaS, Integer as a Service.
It goes down to:
All counters are “unlisted”—accessible if you know the key but not publicly listed anywhere.
Want to track the number of hits a page had? Sure.
Want to know the number of users that clicked on the button “Press me”? There you go.
So far, ...
keys have been created and there have been ...
requests served. ...
of those have been hits and ...
have been gets. Abacus is currently on version ...
.
This page has been visited ...
times.
Each counter is identified inside a namespace with a key.
The namespace should be unique, so its recommend using your site's domain.
Inside each namespace you can generate all the counters you may need.
The hit endpoint provides increment by one counters directly. Each time its requested the counter will increase by one:
https://abacus.jasoncameron.dev/hit/namespace
/key
⇒ 200 { "value": 1234 }
Want to start counting right away? Check the examples below!
Let’s say you want to display the amount of page views a site received.
Remember to change .
mysite.com
/visits");
xhr.responseType = "json";
xhr.onload = function() {
document.getElementById('visits').innerText = this.response.value;
}
xhr.send();mysite.com
/visits",
function(response) {
$("#visits").text(response.value);
});
If you want to have a counter for each individual page you can replace visits
with a unique
identifier for each page, i.e. index
, contact
, item-1234
. Check the right format a key must have.
Alternatively, you can use some reserved words that are replaced server-side.
For example, if a request is
made from https://mysite.com/example/page
:
:HOST:
will be replaced with mysite.com
:PATH:
will be replaced with examplepage
Note: Reserved words are left padded with dots if their length is less than three.
So you could use something like:
https://abacus.jasoncameron.dev/hit/mysite.com
/:PATH:
Or even more generic (though not recommended):
https://abacus.jasoncameron.dev/hit/:HOST:/:PATH:
You can use the API to count any kind of stuff, let’s say:
mysite.com
/awesomeclick");
xhr.responseType = "json";
xhr.onload = function() {
alert(`This button has been clicked ${this.response.value} times!`);
}
xhr.send();
}
</script>
Scroll a bit further.
Completely free.
This means that each unique IP address is allowed to make a maximum of 30 requests every 3 seconds. Exceeding this limit will temporarily block further requests from that IP until your limit is up.
If you require a higher rate limit for legitimate use cases, please contact me at [email protected].
The API provides informative headers in responses to help you track your usage:
RateLimit-Remaining
: Number of requests remaining in the current window.RateLimit-Reset
: Unix timestamp indicating when the rate limit window resets.RateLimit-Policy
: String describing the rate limit policy (e.g., "30;w=3" for 30 requests per 3
seconds).
Retry-After
: Number of ms to wait before retrying (included when rate limited).429 Too Many Requests
status code response
similar to:
{ "error": "Too many requests. Try again in 2s" }
If you originally created the key using the /create endpoint, then yes, you can delete the key and all data associated with it by using the /delete endpoint along with your admin key.
Abacus is using Valkey as a database, a rapid key-value
solution.
If you are planning to make tens of thousands of requests, I'll be glad if you email me letting me know.
If you have issues, suggestions or just want to contact me, shoot me an email or create a GitHub issue.
Endpoints requiring administrative actions (delete, set, reset, update) need an admin key passed in the `Authorization` header as a Bearer token. You get this admin key when creating a counter via /create.
Rate limiting is in place to ensure fair usage: 30 requests per IP address every 3 seconds.
Namespaces are used to avoid name collisions.
You can specify a namespace when creating a key.
It's recommended to use the domain of your app as the namespace to prevent conflicts with other websites.
If
you don't
specify a namespace, the key is assigned to the default
namespace.
You don't need to specify the `default` namespace in your requests.
All requests support cross-origin resource sharing (CORS) and SSL.
Base API path: https://abacus.jasoncameron.dev
In case of a server failure, the API will send:
⇒ 500 { "error": "Error description" }
Check the health and uptime of the API.
GET /healthcheck ⇒ 200 { "status": "ok", "uptime": "1h23m45s" }
Redirects to the API documentation.
Retrieve the current value of a counter. Optionally specify the namespace.
If you want to use JSONP, please pass in the callback via the ?callback query param (e.g. ?callback=myjsfunction)
GET /get/test ⇒ 200 { "value": 42 }
GET /get/nonexisting ⇒ 404 { "error": "Key not found" }
Increment a counter by 1 and return the new value. If the counter doesn't exist, it will be created. Optionally specify a namespace
If you want to use JSONP, please pass in the callback via the ?callback query param (e.g. ?callback=myjsfunction)
GET /hit/mysite.com/visits (value was 35) ⇒ 200 { "value": 36 }
GET /hit/nonexisting (key is created) ⇒ 200 { "value": 1 }
Stream updates to a counter's value using Server-Sent Events (SSE). This means you get updated right as a key is updated instead of having to poll. Optionally specify a namespace.
GET /stream/mysite.com/visits ⇒ data: {"value": 36}
Create a new counter with an optional initial value (default 0). Specify both namespace and key.
Note about admin_key: this is the only time you will be able to see it, if you lose the key then you lose access to control the counter.
Note about expiration: Every time a key is accessed its expiration is set to 6 months. So don't worry, if you still using it, it won't expire.
Keys and namespaces must have at least 3 characters and less or equal to 64. Keys and namespaces must match: ^[A-Za-z0-9_-.]{3,64}$
GET /create/myapp/newcounter?initializer=10 ⇒ 201 {"key": "newcounter", "namespace": "myapp", "admin_key": "YOUR_ADMIN_KEY", "value": 10}
GET /create/myapp/alreadyexists ⇒ 409 { "error": "Key already exists, please use a different key." }
Create a new counter with a random namespace and key. This endpoint does not take any parameters.
GET /create ⇒ 201 {"key": "randomkey", "namespace": "randomnamespace", "admin_key": "YOUR_ADMIN_KEY", "value": 0}
Get detailed information about a counter, including its value, key, expiration, etc. Optionally specify the namespace.
GET /info/existing ⇒ 200 { "value": 42, // Current counter value "full_key": "K:default:existing", // The full DB key (K:namespace:key) "is_genuine": true, // Indicates if the counter was created with an admin key (false) or not (true) "expires_in": 172800, // Time to live (TTL) in seconds "expires_str": "2d", // TTL in a human-readable format "exists": true // Whether the key exists in the DB }
GET /info/nonexisting ⇒ 404 { "value": -1, "full_key": "default:nonexisting", "is_genuine": true, "expires_in": -2e-9, "expires_str": "-2ns", // When the server received the request compared to when it output a response. "exists": false }
Delete a counter. Specify both namespace and key. Include the admin key in the `Authorization` header.
POST /delete/myapp/mycounter Authorization: Bearer YOUR_ADMIN_KEY ⇒ 200 { "status": "ok", "message": "Deleted key: myapp:mycounter" }
Set the value of a counter, overwriting the existing value. Specify both namespace and key, and provide the `value` query parameter. Include the admin key in the `Authorization` header.
POST /set/myapp/mycounter?value=15 Authorization: Bearer YOUR_ADMIN_KEY ⇒ 200 { "value": 15 }
POST /set/myapp/nonexisting?value=15 Authorization: Bearer YOUR_ADMIN_KEY ⇒ 404 { "error": "Key does not exist, please use a different key." }
Reset a counter to 0. Specify both namespace and key. Include the admin key in the `Authorization` header.
POST /reset/myapp/mycounter Authorization: Bearer YOUR_ADMIN_KEY ⇒ 200 { "value": 0 }
POST /reset/myapp/nonexisting Authorization: Bearer YOUR_ADMIN_KEY ⇒ 404 { "error": "Key doesnot exist, please use a different key." }
Increment or decrement a counter by the specified amount. Specify both namespace and key, and provide the value query parameter (positive to increment, negative to decrement). Include the admin key in the Authorization header.
POST /update/myapp/mycounter?value=5 Authorization: Bearer YOUR_ADMIN_KEY ⇒ 200 { "value": 20 } // Assuming the previous value was 15
POST /update/myapp/nonexisting?value=-3 Authorization: Bearer YOUR_ADMIN_KEY ⇒ 404 { "error": "Key does not exist, please first create it using /create." }
Gives some info about the server and database. The "commands" stats are updated every 30s per shard
GET /stats/ ⇒ 200 { "commands": { "create": 20394, // total number of /create's "get": 403232, // total number of /get's "hit": 703232, // total number of /hit's "total": 2003058 // total number of requests served }, "db_uptime": "5558", // database uptime in seconds "db_version": "6.0.16", // database version "expired_keys__since_restart": "130", // number of keys expired since db's last restart "key_misses__since_restart": "205", // number of keys not found since db's last restart "total_keys": 87904, // total number of keys created "version": "1.3.3", // Abacus's version "shard": "boujee-coorgi", // Handler shard "uptime": "1h23m45s" // shard uptime }