WorldTimeAPI has shut down. Change one hostname.
worldtime.timezone.io answers the same paths as worldtimeapi.org, with the same 15
fields, in JSON or .txt. No API key and no sign-up. Plain http:// works, so devices
that can't do TLS keep working.
Before: http://worldtimeapi.org/api/timezone/Europe/London
After: http://worldtime.timezone.io/api/timezone/Europe/London
For most code, that hostname is the only change.
What happened to worldtimeapi.org
- worldtimeapi.org now shows a shutdown notice, last modified on 8 February 2026: "With a heavy heart, WorldTimeAPI has been sunset. … The ongoing hosting and operational costs have become too high to justify for a free service in the current economic climate."
- The shutdown was uneven. Some requests still got normal answers until early April 2026.
Since mid-April 2026, requests get
410,502or503errors, or no answer at all. - The service was unreliable before that. In January 2025, users on the
Python forum reported
ERR_EMPTY_RESPONSEerrors and 15.48% uptime over the previous month. By November 2025 the same thread reported 2.79%.
timezone.io is not affiliated with WorldTimeAPI. This host was built so existing clients can keep running.
Is your code affected?
If your code calls worldtimeapi.org/api/timezone/Europe/London, worldtimeapi.org/api/ip,
worldtimeapi.org/api/timezone/Etc/UTC or any other WorldTimeAPI URL, you will see one
of these:
| Symptom | What you see |
|---|---|
HTTP 410 Gone (JSON) |
{"error":"gone","status":410,"message":"WorldTimeAPI has been sunset. This endpoint is gone."} |
HTTP 410 Gone (.txt) |
WorldTimeAPI has been sunset. This endpoint is gone. |
Connection reset, most often on plain http:// |
Connection reset by peer, Python ConnectionResetError: [Errno 104] |
| Browser | ERR_EMPTY_RESPONSE or ERR_CONNECTION_RESET |
Arduino / ESP32 HTTPClient |
http.GET() returns a negative error code instead of 200 |
Migrate
Replace worldtimeapi.org with worldtime.timezone.io. To do it across a project (GNU
and macOS sed; the .bak files are backups you can delete afterwards):
grep -rlI --exclude-dir=.git 'worldtimeapi\.org' . \
| xargs sed -i.bak -E 's/(www\.)?worldtimeapi\.org/worldtime.timezone.io/g'
Review the diff before you commit. Links to WorldTimeAPI's web pages, such as
worldtimeapi.org/timezones, have no equivalent here: point them to
the zone list instead.
Endpoints
Base URL, over HTTP or HTTPS:
http://worldtime.timezone.io
https://worldtime.timezone.io
| Path | Returns |
|---|---|
/api/timezone/{area}/{location}[/{region}] |
The current time in that zone. |
/api/ip |
The current time where the caller's IP address is. |
/api/timezone |
Every zone name. |
/api/timezone/{area} |
The zone names in one area, such as Europe. |
/api/ip/{ip} |
The current time where that IP address is. |
Add .txt to any path for plain text (/api/timezone/Europe/London.txt). .json is
accepted and returns JSON, like no suffix. Query strings are ignored.
The endpoints are described in an OpenAPI 3.1 document at
https://worldtime.timezone.io/api/openapi.json,
for Postman, SDK generators and AI agents. Every curl example on this page has a Try it
button that sends it live.
Current time in a zone
GET /api/timezone/{area}/{location}[/{region}]
Zone names match in any letter case (europe/london, etc/UTC, est5edt), and old names
such as Europe/Kiev and US/Pacific still work. Single-word zones work too:
/api/timezone/UTC, /api/timezone/EST.
curl https://worldtime.timezone.io/api/timezone/Europe/London
Responses are compact JSON on one line, formatted here for reading:
{
"utc_offset": "+01:00",
"timezone": "Europe/London",
"day_of_week": 6,
"day_of_year": 269,
"datetime": "2026-09-26T02:39:32.594306+01:00",
"utc_datetime": "2026-09-26T01:39:32.594306+00:00",
"unixtime": 1790386772,
"raw_offset": 0,
"week_number": 39,
"dst": true,
"abbreviation": "BST",
"dst_offset": 3600,
"dst_from": "2026-03-29T01:00:00+00:00",
"dst_until": "2026-10-25T01:00:00+00:00",
"client_ip": "203.0.113.7"
}
The same zone as text, one key: value per line. A null value is left empty:
curl http://worldtime.timezone.io/api/timezone/Asia/Kolkata.txt
abbreviation: IST
client_ip: 203.0.113.7
datetime: 2026-09-26T07:09:32.651254+05:30
day_of_week: 6
day_of_year: 269
dst: false
dst_from:
dst_offset: 0
dst_until:
raw_offset: 19800
timezone: Asia/Kolkata
unixtime: 1790386772
utc_datetime: 2026-09-26T01:39:32.651254+00:00
utc_offset: +05:30
week_number: 39
Parse the text by key, not by line number.
Time for the caller's IP address
GET /api/ip
Returns the same fields for the time zone of the IP address the request comes from. Behind a VPN or a carrier network, that is the location of the exit point, which may be in another time zone.
curl https://worldtime.timezone.io/api/ip
{
"utc_offset": "+02:00",
"timezone": "Europe/Paris",
"day_of_week": 6,
"day_of_year": 269,
"datetime": "2026-09-26T03:39:32.670271+02:00",
"utc_datetime": "2026-09-26T01:39:32.670271+00:00",
"unixtime": 1790386772,
"raw_offset": 3600,
"week_number": 39,
"dst": true,
"abbreviation": "CEST",
"dst_offset": 3600,
"dst_from": "2026-03-29T01:00:00+00:00",
"dst_until": "2026-10-25T01:00:00+00:00",
"client_ip": "203.0.113.7"
}
When the address can't be located, the response is 404 with
{"error":"unknown location 203.0.113.7"}. There is no fallback to UTC, so handle the
404 and fall back to a zone you choose.
Time for any IP address
GET /api/ip/{ip}
Returns the same fields for the time zone of an IPv4 or IPv6 address. client_ip is still
the address the request comes from, as it was on WorldTimeAPI, not the one in the path.
curl https://worldtime.timezone.io/api/ip/8.8.8.8
Private and reserved addresses (10.0.0.1, 127.0.0.1) and addresses that can't be
located return 404 with {"error":"unknown location 8.8.8.8"}.
List zone names
GET /api/timezone
GET /api/timezone/{area}
/api/timezone returns every zone name as a JSON array, and /api/timezone.txt returns
one name per line. /api/timezone/Europe returns only the names under Europe/:
["Europe/Amsterdam", "Europe/Andorra", "Europe/Astrakhan", "Europe/Athens", "…"]
Areas are one word (Europe, America, Etc). America/Argentina is not an area, so it
returns 404.
Errors
Errors are 404 with an error message, or a line starting with Error: on .txt paths:
| Request | Status | Body |
|---|---|---|
/api/timezone/Europe/Pari |
404 |
{"error":"unknown location Europe/Pari"} |
/api/timezone/Europe/Pari.txt |
404 |
Error: unknown location |
/api/ip/not-an-ip |
404 |
{"error":"malformed ip"} |
| Any path that isn't an endpoint | 404 |
{"error":"not found"} |
| Too many requests | 429 |
{"error":"too many requests"} |
Response fields
The same 15 fields as WorldTimeAPI, in the same order:
| Field | Type | Description |
|---|---|---|
utc_offset |
string | Current offset from UTC, ±HH:MM, including daylight saving time. |
timezone |
string | The zone name, in its usual letter case. An old name is returned as requested (Europe/Kiev). |
day_of_week |
integer | Local day of the week, 0 for Sunday to 6 for Saturday. |
day_of_year |
integer | Local day of the year, starting at 1. |
datetime |
string | Local date and time, ISO 8601 with microseconds and the offset. |
utc_datetime |
string | The same moment in UTC, ending in +00:00. |
unixtime |
integer | Seconds since 1970-01-01 UTC, fraction dropped. |
raw_offset |
integer | Standard offset from UTC in seconds, without daylight saving time. |
week_number |
integer | ISO 8601 week number of the local date. |
dst |
boolean | Whether daylight saving time is in effect now. |
abbreviation |
string | Local abbreviation, such as BST. Places without one get a numeric form, such as -03. |
dst_offset |
integer | Daylight saving time in effect now, in seconds: usually 3600, 0 outside it. |
dst_from |
string or null | When the current daylight saving period started (UTC). null when dst is false. |
dst_until |
string or null | When the current daylight saving period ends (UTC). null when dst is false. |
client_ip |
string | The IP address the request came from. |
Local time in seconds is unixtime + raw_offset + dst_offset, which is what most device
code needs.
Code examples
Only the hostname differs from the WorldTimeAPI versions.
Arduino and ESP32
With HTTPClient and ArduinoJson 7. HTTPClient doesn't follow redirects by default, and
this URL answers directly over plain HTTP:
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
// Before: http://worldtimeapi.org/api/timezone/Europe/London
const char *TIME_URL = "http://worldtime.timezone.io/api/timezone/Europe/London";
// Local time in seconds since 1970, or -1 on failure.
long long fetchLocalEpoch() {
HTTPClient http;
http.useHTTP10(true); // lets getStream() below read a plain, unchunked body
http.begin(TIME_URL);
int code = http.GET(); // negative on a network error
if (code != 200) {
http.end();
return -1;
}
JsonDocument doc;
DeserializationError error = deserializeJson(doc, http.getStream());
http.end();
if (error) {
return -1;
}
return doc["unixtime"].as<long long>()
+ doc["raw_offset"].as<long long>()
+ doc["dst_offset"].as<long long>();
}
Call it once at start-up and every few hours after that, then count seconds locally with
millis(). After a failure, wait at least a minute before trying again.
MicroPython and CircuitPython
import requests # "urequests" on older MicroPython builds
# /api/ip when the device has no configured zone
response = requests.get("http://worldtime.timezone.io/api/ip")
data = response.json()
response.close()
print(data["timezone"], data["datetime"])
local_epoch = data["unixtime"] + data["raw_offset"] + data["dst_offset"]
On CircuitPython, make the same call with the requests session from
adafruit_requests.
JavaScript
Fetch once, then tick locally. The browser's clock only needs to be steady, not correct:
// Before: https://worldtimeapi.org/api/timezone/Europe/London
const response = await fetch(
'https://worldtime.timezone.io/api/timezone/Europe/London',
);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const { unixtime, timezone } = await response.json();
const skewMs = unixtime * 1000 - Date.now();
const format = new Intl.DateTimeFormat('en-GB', {
timeZone: timezone,
timeStyle: 'medium',
});
setInterval(() => {
document.querySelector('#clock').textContent = format.format(
new Date(Date.now() + skewMs),
);
}, 1000);
Pages served over HTTPS must call the https:// URL, or the browser blocks the request.
Responses carry Access-Control-Allow-Origin: *, so browsers can call the API from any
site.
Python
import requests
# Before: http://worldtimeapi.org/api/timezone/Etc/UTC
response = requests.get("https://worldtime.timezone.io/api/timezone/Etc/UTC", timeout=5)
response.raise_for_status()
print(response.json()["datetime"])
The standard-library version from older Python tutorials, reading the .txt form:
from urllib.request import Request, urlopen
# Name your app: some networks refuse urllib's default User-Agent.
request = Request(
"http://worldtime.timezone.io/api/timezone/etc/UTC.txt",
headers={"User-Agent": "my-clock/1.0"},
)
with urlopen(request) as response:
for line in response:
line = line.decode("utf-8")
if line.startswith("datetime"):
print(line.rstrip())
Limits
- Rate limit: 60 requests per minute per IP address. Over it, you get
429with aRetry-Afterheader giving the seconds to wait. Every response carriesX-RateLimit-LimitandX-RateLimit-Remaining. Devices behind one public IP address, such as a home router, share the limit. - No uptime guarantee. The service is provided as it is, under the Terms of Service.
- Not a time server. The microseconds in
datetimeare the server's clock when it answered, and network delay is far larger, so treat the time as accurate to about a second. To set a device's clock, use NTP. On ESP32,configTzTime("GMT0BST,M3.5.0/1,M10.5.0", "pool.ntp.org")keeps London time, daylight saving changes included, with no HTTP calls at all. Use this API to find out the zone or offset. - Poll gently. One request at start-up and one every few hours is enough for a clock. Code that calls every second or every few seconds will hit the rate limit.
- IP locations are approximate. An IP address is located to its network's region, and a VPN or carrier network can put it in another time zone. When you know the zone, ask for it by name.
GET /api, which returned WorldTimeAPI's OpenAPI description, returns404here.
Valid time zone names
worldtimeapi.org/timezones is gone. The full list is at
worldtime.timezone.io/api/timezone.txt, one
name per line. It comes from the IANA time zone database, so it can include zones
WorldTimeAPI never listed, such as America/Coyhaique. To find the zones used in a
country, browse countries.
Need more than WorldTimeAPI had?
The timezone.io API (/v1) adds cities with sunrise and sunset, countries, named
time zones and time conversion. It needs a free token from a verified account (see
Authentication), and it has no IP lookup.
The WorldTimeAPI fields map to GET /v1/timezones/{iana}
like this:
| WorldTimeAPI | /v1/timezones/{iana} |
|---|---|
timezone |
iana |
datetime |
current.datetime (whole seconds) |
utc_datetime |
current.utc_datetime (ends in Z) |
utc_offset |
current.utc_offset, or current.utc_offset_seconds in seconds |
abbreviation |
current.abbreviation, with the full name in current.name |
dst |
current.is_dst |
raw_offset |
standard.utc_offset_seconds |
dst_offset |
current.utc_offset_seconds minus standard.utc_offset_seconds |
dst_until |
dst.next_transition.at while current.is_dst is true |
dst_from |
No equivalent |
unixtime |
No field: parse current.utc_datetime |
day_of_week, day_of_year, week_number |
No fields: compute them from current.datetime |
client_ip |
No equivalent |
For the time in a city, use /v1/cities/{slug}. For a
country's main zone, use /v1/countries/{iso2}. To
convert a time between zones, use /v1/convert. The
Quickstart gets you to a first response.
FAQ
Is worldtimeapi.org coming back?
Its notice says the service has shut down and gives no return date.
Do I need an API key?
No. worldtime.timezone.io has no keys, accounts or sign-up. Only the /v1 API needs a
token.
Does plain HTTP work?
Yes. http://worldtime.timezone.io answers directly, with no redirect to HTTPS, because
many devices can't do TLS. Use HTTPS wherever you can.
Are the answers the same as WorldTimeAPI's?
The paths, field names, field order and formats are the same, and so are two quirks.
Europe/Dublin and Morocco's zones never report dst: true: Irish summer time shows as a
raw_offset of 3600. And client_ip is always the caller's address, even on
/api/ip/{ip}.
Why do I get 404 from /api/ip?
The IP address couldn't be located, or it is a private or reserved address such as
192.168.1.10. Call /api/timezone/{zone} with a zone you choose instead.
Why do I get 429?
More than 60 requests in a minute came from your IP address. Wait for the number of
seconds in Retry-After, then fetch less often: once, then tick locally.