API Reference
WebNTP distributes time over three protocols. Every response shares a common JSON format (except Time over HTTPS), and the client derives the clock offset from the round-trip delay.
The public server's base URL is https://webntp.shogo82148.com. To run your own, start it with webntp -serve :8080. There are three endpoints.
| Method / Path | Protocol | Purpose |
|---|---|---|
| GET /json | HTTP | Returns the server time as JSON |
| GET /websocket | WebSocket | Exchanges time over a kept-alive connection |
| HEAD /.well-known/time | HTTPS | Returns the time in a header (lightweight) |
Call all three protocols against webntp.shogo82148.com, the public server that hosts this page. You'll see the raw response along with the offset and round-trip delay computed from it.
Press “Run” and the result appears here.
The JSON returned by /json and /websocket is identical. All timestamps are Unix epoch seconds (sub-second precision as a fraction).
| Field | Type | Description |
|---|---|---|
| id | string | The server's hostname. |
| it | number | Initiate Time — the request time sent by the client. 0 if not sent. |
| st | number | Send Time — the time the server sent the response. |
| time | number | Server time. Same value as st (kept for backward compatibility). |
| leap | number | Seconds of TAI − UTC (the value in effect before next). |
| next | number | Timestamp of the next or most recent leap second. |
| step | number | Leap second direction. Positive: 1, negative: −1. |
GET /json?<timestamp>
Pass the client's send time (epoch seconds) as the query string and the server echoes it back in it. Omit the query and it is 0.
curl -s 'https://webntp.shogo82148.com/json?1788697606.057'
{
"id": "webntp.shogo82148.com",
"it": 1788697606.057,
"st": 1788697671.734335,
"time": 1788697671.734335,
"leap": 36,
"next": 1483228800,
"step": 1
}
WS /websocket
Connect with the subprotocol webntp.shogo82148.com. Send a timestamp (epoch seconds) in a text frame and the server replies in the same JSON format. You can query repeatedly over one connection for the highest accuracy; the server closes the connection after a period of inactivity.
$ wscat --connect wss://webntp.shogo82148.com/websocket \
--subprotocol webntp.shogo82148.com
> 1558915619.944235
< {"id":"webntp.shogo82148.com","it":1558915619.944235,"st":1558916776.363423, …}
new WebSocket(url, ["webntp.shogo82148.com"]). Without it the handshake fails.const conn = new WebSocket(
"wss://webntp.shogo82148.com/websocket",
["webntp.shogo82148.com"]
);
conn.onopen = () => conn.send((Date.now() / 1000).toString());
conn.onmessage = (ev) => {
const r = JSON.parse(ev.data);
const end = Date.now();
const delay = end - r.it * 1000;
const offset = r.st * 1000 - end + delay / 2;
console.log("offset", offset, "ms");
};
HEAD /.well-known/time
Carries no body; the server time (epoch seconds) is returned in the X-Httpstime response header. It's the lightest-weight method, based on FreeBSD's Time over HTTPS specification.
$ curl -I https://webntp.shogo82148.com/.well-known/time
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Httpstime
Cache-Control: no-cache
X-Httpstime: 1788697967.925993
Like NTP, the client measures the round-trip delay to estimate the clock offset. Using the send time it, the response-received time end, and the server send time st:
# round-trip delay (time spent on the network round trip)
delay = end - it
# clock offset (server time − client time)
# correct by half the delay for the one-way trip
offset = st - end + delay / 2
Adding the estimated offset to the local clock gives a time synchronised to the server. A smaller round-trip delay yields a better estimate, which is why WebSocket is the most accurate.
The leap / next / step fields are leap-second metadata. Leap seconds are scheduled to be abolished, but for backward compatibility the server keeps returning the most recently inserted one (the positive leap second on 2017-01-01, TAI − UTC = 36 s).
Every endpoint returns Access-Control-Allow-Origin: *, so it can be used from a browser on any origin. For Time over HTTPS, Access-Control-Expose-Headers: X-Httpstime lets JavaScript read the header. All responses carry Cache-Control: no-cache: a cache may store them, but must revalidate with the server before reuse.