API Reference

WebNTP 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.

Overview

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 / PathProtocolPurpose
GET /jsonHTTPReturns the server time as JSON
GET /websocketWebSocketExchanges time over a kept-alive connection
HEAD /.well-known/timeHTTPSReturns the time in a header (lightweight)

Try it in the browser

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.

Response fields

The JSON returned by /json and /websocket is identical. All timestamps are Unix epoch seconds (sub-second precision as a fraction).

FieldTypeDescription
idstringThe server's hostname.
itnumberInitiate Time — the request time sent by the client. 0 if not sent.
stnumberSend Time — the time the server sent the response.
timenumberServer time. Same value as st (kept for backward compatibility).
leapnumberSeconds of TAI − UTC (the value in effect before next).
nextnumberTimestamp of the next or most recent leap second.
stepnumberLeap second direction. Positive: 1, negative: −1.

JSON over HTTP

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.

request
curl -s 'https://webntp.shogo82148.com/json?1788697606.057'
response · 200 application/json
{
  "id": "webntp.shogo82148.com",
  "it": 1788697606.057,
  "st": 1788697671.734335,
  "time": 1788697671.734335,
  "leap": 36,
  "next": 1483228800,
  "step": 1
}

WebSocket

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
$ wscat --connect wss://webntp.shogo82148.com/websocket \
    --subprotocol webntp.shogo82148.com
> 1558915619.944235
< {"id":"webntp.shogo82148.com","it":1558915619.944235,"st":1558916776.363423, }
From the browser you must specify the subprotocol, e.g. new WebSocket(url, ["webntp.shogo82148.com"]). Without it the handshake fails.
JavaScript
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");
};

Time over HTTPS

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.

request / response
$ 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

Sync algorithm

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:

formula
# 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.

Leap seconds

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).

This specification references NICT's (National Institute of Information and Communications Technology) documentation on time distribution over http/https (in Japanese).

CORS

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.

← Back to demo