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, 502 or 503 errors, or no answer at all.
  • The service was unreliable before that. In January 2025, users on the Python forum reported ERR_EMPTY_RESPONSE errors 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 429 with a Retry-After header giving the seconds to wait. Every response carries X-RateLimit-Limit and X-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 datetime are 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, returns 404 here.

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.