Developer Documentation

Initial Setup
In order to begin using in production your account must be activated.
However, for testing all you need is a valid account. If your account is not active the numbers shown in counters will simply be example data.
An account can be created here.
Usage Examples's hit counters can be used to increase engagement across a various number of platforms and use cases.
The classic example of a hit counter is a simple counter that keeps track of the number of visits to a page.'s hit counters are a bit different and go up and down with the number of people viewing a page, product, auction, or video. This means there are two primary use cases.
  • A counter that increments or decrements with the current number of users, including the current user
  • A counter that increments or decrements with the current number of users, excluding the current viewer
The second use case is useful for product or flight lists, or auctions where you may want to show the activity of an item before the user selects it. In the case of the prebuilt web library the default behavior is that each user impacts the value of the counter.

Web Client

Common Examples
The below examples are in Javascript and assume the available web client is already included on the page.
The web client minified code is available here.
Counter Per URL
watchly.connect(document.getElementById('watchly-target'), { key: 'your-api-key' });
Counter Per Product ID
watchly.connect(document.getElementById('watchly-target'), { key: 'your-api-key', urlId: 'some-product' });
Pass a User ID as the Presence ID
watchly.connect(document.getElementById('watchly-target'), { key: 'your-api-key', pId: 'some-user-id' });
View-Only Counter
watchly.connect(document.getElementById('watchly-target'), { key: 'your-api-key', impactsCount: false });
Custom Formatter
Note - if you use a custom formatter you must handle localization.
watchly.connect(document.getElementById('watchly-target'), { key: 'your-api-key', formatNumber: function(number) { return number + 'k': } });
Name Key Type Default Value Valid Values
URL ID urlId String window.location.href Any Unique ID
API Key key String None API Key Provided by
Presence ID pId String UUID A sufficiently unique identifier to identify a single or group of users
Impacts Count Flag impactsCount boolean true A flag with states of either true or false
Number Formatter formatNumber Function(Number) -> Number A function that can be used to format the displayed number
When rendering numbers for hit counters across the globe attempts to determine each individual's locale and give them the best experience possible.

For this to work, tries to determine the user's locale and render the numbers appropriately. Also, as the value in the hit counter grows we may need to shorten its representation and append an identifier to the number.

For the en_US locale some common examples of formatting are:
1,234 10.2k 500k 5.1M
In this case "k" means thousand, "m" means million. The web client library will use one decimal place when appropriate.


User Verification

What it Does
User verification is useful for ensuring only valid users can affect the state of your counters.

For example this prevents potential bad-actors from making your users think that there are more people watching a bid, product, or stream.
Without User Verification ❌
With User Verification ✅
How it Works
For User Verification to work you must expose an API server that can call to verify a user.

When a user loads your website or application you must pass that user's session (or cookie) id when connecting via a the Presence ID. will then use your API response to decide whether or not to allow that user to impact the value of the counter.

They will still get to connect and see the counter value.
API Spec
For user verification expects a REST API (preferably using SSL) that responds with either a 401 or 200 status code that indicates whether or not the user is valid.
In addition, it must also respond with a response body that contains only your API Key. This verifies you own the endpoint. will invoke this endpoint via the POST method with a pId parameter in the request body JSON.

API Verification is used to verify the API is configured correctly and you have ownership of it. This verification process involves invoking your API twice when you click the "test" or "save" options in the product dashboard. It will call it in two ways. Once with the presence id "w-invalid" and once with "w-valid". Your API should respond with the 401 and 200 status codes for these values, respectively, as well as your api key.

For each of the verification test cases (w-invalid and w-valid) will also perform a load test on YOUR SERVER.

Your API must respond to all of the requests in a total of three seconds to pass.

You should expect 500 simultaneous calls when running the test. This will ensure your server is up to the task of handling traffic and not causing a back-fill of traffic.

Client Spec

Writing Your Own Client
It is suggested that the first step in writing your own client is to review the reference implementation which handles connection errors and localization.
It can be viewed here.
Protocol uses Websockets to handle connections between your users and our servers. This helps reduce bandwidth while allowing us to update the numbers in real time. While the initial connection has to include HTTP headers, each subsequent update only requires a couple bytes of overhead.
In order to scale uses client side load balancing.

This means that rather than all connections to going through a single load balancer depends on the clients to spread out the load.
To illustrate, with traditional load balancing all clients connect to a single or cluster of load balancers.
Traditional Load Balancing ❌
Whereas with each client randomly connects to a server. Load Balancing ✅

Clients must randomly connect to hosts through Additionally, they should send a message with the content of 'PING' every five minutes. will handle state synchronization between servers meaning users for the same hit counter and product can connect to separate servers.

It is important to keep in mind that depending on your contract each successful connection may be billed. This means for example you should not go and create millions of concurrent connections for testing, unless you wish to pay for it.

In conclusion, it is also noteworthy that all connections use SSL for a secure experience for your users.
API Definition
The API does not require any messages to be sent across the lifecycle of a session. Clients are subscribers only.

This means that in order to pass state and configuration like api key, presence id, and url id we have to do it while connecting. We do this by defining these parameters in the URL.

wss://[host]/sub?urlId=[URL ID]&key=[API Key]&pId=[Presence ID]&i=[t or f]

The "i" parameter is the only optional configuration. Without it (or setting it to t) connecting will impact the counter of all connected clients. Setting it to "f" will mean that this connection will not impact the session count.

Here is an example with some dummy values:

Don't forget to URI Encode values like the urlId, especially if you are storing raw URLs.
Handling Errors and Disconnects
It is important that when an error occurs your client automatically attempts to re-establish the connection after a period of time. Connection errors in the real world happen, and we have to be ready for them.

Upon disconnect you should not reset the counter to zero. This will result in a bad user experience especially with the use of animations. Since disconnects are usually very temporary (milliseconds to several seconds) there should be no reason to reset the counter. Simply keep the last known valid value and then update it once connected again.

If you are writing your own client you should implement an exponential back-off to prevent hurting
What this means is that you should add some padding to the time between connection attempts. In the case of the web client, we simply do 1000 * Math.random().

While this implementation is currently not exponential, it helps limit the number of connections at once to in the event of a large network outage (or server restart).
Handling Messages
Currently, the only message you will receive from is a number. There is no object or array that contains the number - the message is the number itself. Note that this may change in the future, so please do a safe check to see if the message is a number.

You can see this in the reference implementation by searching for isNaN.