Skip to content

Migrate from WorldTimeAPI: code for every language, field map and troubleshooting

Replace worldtimeapi.org in JavaScript, Python, PHP, Go, Java, C#, Ruby, curl or ESP32: same paths and 15 fields, new host, an API key, plus error fixes.

Updated

Short answer: keep your code, change the host. Every worldtimeapi.org path works on https://api.timelogicapi.com with the same JSON fields and .txt output; add a TimeLogic API key and you are done. Below is the change for ten languages, a field-by-field map, and fixes for the errors people hit while moving.

- http://worldtimeapi.org/api/timezone/Europe/London
+ https://api.timelogicapi.com/api/timezone/Europe/London?api_key=YOUR_KEY

New here? WorldTimeAPI is offline; what happened and why a compatible host is the quickest fix. The exact contract is in the WorldTimeAPI-compatible routes docs, and you can call each route from your browser in the interactive WorldTimeAPI-compatible reference.

How do I replace worldtimeapi.org in three steps?

  1. Create a free API key. The Free plan includes 10,000 requests per month, no card.
  2. Change the base URL to https://api.timelogicapi.com. Keep /api/timezone/... or /api/ip/... exactly as it is; there is no /v1 in these paths.
  3. Send the key in one of three ways: Authorization: Bearer YOUR_KEY, X-API-Key: YOUR_KEY, or ?api_key=YOUR_KEY for tools that cannot set headers.

If your code builds URLs from one base constant, the migration is literally one line plus the key:

- BASE_URL = "http://worldtimeapi.org"
+ BASE_URL = "https://api.timelogicapi.com"   # and send your API key

Migration code for every language

Each example fetches Europe/London and reads datetime, exactly as the old code did. Store the key in an environment variable (TIMELOGIC_API_KEY) rather than in source.

JavaScript (browser or Node 18+, fetch)

- const res = await fetch("http://worldtimeapi.org/api/timezone/Europe/London");
+ const res = await fetch("https://api.timelogicapi.com/api/timezone/Europe/London", {
+   headers: { Authorization: `Bearer ${process.env.TIMELOGIC_API_KEY}` },
+ });
  const data = await res.json();
  console.log(data.datetime, data.utc_offset);

In a browser, call your own server and let it add the key; anything shipped to the browser is visible. For the plain current time in JavaScript, see Get the current time from an API in JavaScript.

Python (requests)

  import os, requests
- r = requests.get("http://worldtimeapi.org/api/timezone/Europe/London")
+ r = requests.get(
+     "https://api.timelogicapi.com/api/timezone/Europe/London",
+     headers={"X-API-Key": os.environ["TIMELOGIC_API_KEY"]},
+     timeout=10,
+ )
  r.raise_for_status()
  print(r.json()["datetime"])

The Python docs tutorial once used /api/timezone/etc/UTC.txt; that exact path works too, lowercase included. More Python patterns: Get the current time from the internet in Python.

PHP

<?php
$key = getenv('TIMELOGIC_API_KEY');
$context = stream_context_create(['http' => [
    'header'  => "X-API-Key: $key\r\n",
    'timeout' => 10,
]]);
// before: file_get_contents('http://worldtimeapi.org/api/timezone/Europe/London')
$json = file_get_contents('https://api.timelogicapi.com/api/timezone/Europe/London', false, $context);
$data = json_decode($json, true);
echo $data['datetime'], PHP_EOL;

Go

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
)

type WorldTime struct {
	Datetime  string `json:"datetime"`
	Timezone  string `json:"timezone"`
	Unixtime  int64  `json:"unixtime"`
	UTCOffset string `json:"utc_offset"`
	DST       bool   `json:"dst"`
}

func main() {
	// before: http.Get("http://worldtimeapi.org/api/timezone/Europe/London")
	req, _ := http.NewRequest("GET", "https://api.timelogicapi.com/api/timezone/Europe/London", nil)
	req.Header.Set("Authorization", "Bearer "+os.Getenv("TIMELOGIC_API_KEY"))
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	var t WorldTime
	if err := json.NewDecoder(res.Body).Decode(&t); err != nil {
		panic(err)
	}
	fmt.Println(t.Datetime, t.Timezone)
}

Java (11+, java.net.http)

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

HttpClient client = HttpClient.newHttpClient();
// before: URI.create("http://worldtimeapi.org/api/timezone/Europe/London")
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.timelogicapi.com/api/timezone/Europe/London"))
    .header("Authorization", "Bearer " + System.getenv("TIMELOGIC_API_KEY"))
    .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); // the same JSON fields as worldtimeapi.org

C# (.NET 6+)

using System.Net.Http.Json;

using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-API-Key", Environment.GetEnvironmentVariable("TIMELOGIC_API_KEY"));
// before: "http://worldtimeapi.org/api/timezone/Europe/London"
var time = await http.GetFromJsonAsync<WorldTime>("https://api.timelogicapi.com/api/timezone/Europe/London");
Console.WriteLine($"{time!.datetime} {time.timezone}");

record WorldTime(string datetime, string timezone, long unixtime, string utc_offset, bool dst);

Ruby

require "net/http"
require "json"

# before: URI("http://worldtimeapi.org/api/timezone/Europe/London")
uri = URI("https://api.timelogicapi.com/api/timezone/Europe/London")
req = Net::HTTP::Get.new(uri)
req["X-API-Key"] = ENV.fetch("TIMELOGIC_API_KEY")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts JSON.parse(res.body)["datetime"]

curl and shell scripts

# JSON
curl "https://api.timelogicapi.com/api/timezone/Europe/London?api_key=$TIMELOGIC_API_KEY"

# plain text, one field
curl -s "https://api.timelogicapi.com/api/timezone/Etc/UTC.txt?api_key=$TIMELOGIC_API_KEY" | grep '^unixtime:'

# your own IP's zone
curl "https://api.timelogicapi.com/api/ip?api_key=$TIMELOGIC_API_KEY"

PowerShell:

Invoke-RestMethod -Uri "https://api.timelogicapi.com/api/timezone/Europe/London" -Headers @{ "X-API-Key" = $env:TIMELOGIC_API_KEY }

Arduino and ESP32

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

const char* API_KEY = "YOUR_KEY";

void printLondonTime() {
  WiFiClientSecure client;
  client.setInsecure();  // quick start only; pin the server's root CA with setCACert() in production
  HTTPClient http;
  // before: http.begin("http://worldtimeapi.org/api/timezone/Europe/London");
  http.begin(client, "https://api.timelogicapi.com/api/timezone/Europe/London");
  http.addHeader("X-API-Key", API_KEY);
  if (http.GET() == 200) {
    JsonDocument doc;  // ArduinoJson 7
    deserializeJson(doc, http.getString());
    Serial.println(doc["datetime"].as<const char*>());
  }
  http.end();
}

WorldTimeAPI sketches often used plain http://; use https:// with WiFiClientSecure on the new host. The full device walkthrough (setting the ESP32 clock, ESP8266, parsing .txt) is in Get the time from the internet on ESP32 and Arduino.

Power BI, Excel and other no-code tools

= Json.Document(Web.Contents("https://api.timelogicapi.com/api/timezone/Etc/UTC?api_key=YOUR_KEY"))

Field-by-field compatibility

Every field WorldTimeAPI returned is present with the same name, type and format.

FieldWorldTimeAPITimeLogic compatible routeSame?
abbreviationstring, e.g. BSTstring, e.g. BSTYes
client_ipcaller's IPcaller's IP (also on /api/ip/{ip})Yes
datetimeISO 8601, 6 fractional digits + offsetISO 8601, 6 fractional digits + offsetYes; last three digits are 000
day_of_week0 = Sunday … 6 = Saturday0 = Sunday … 6 = SaturdayYes
day_of_year1 on January 11 on January 1Yes
dstbooleanbooleanYes
dst_fromUTC start of DST or nullUTC start of DST or nullYes
dst_offsetseconds added by DSTseconds added by DSTYes
dst_untilUTC end of DST or nullUTC end of DST or nullYes
raw_offsetstandard offset in secondsstandard offset in secondsYes
timezonezone namezone nameYes
unixtimeUnix secondsUnix secondsYes
utc_datetimeUTC instant, +00:00UTC instant, +00:00Yes; last three digits are 000
utc_offset±HH:MM±HH:MMYes
week_numberISO weekISO weekYes

Routes, status codes and bodies match too:

RequestResponse
/api/timezoneJSON array of all zone names (597)
/api/timezone/{area}JSON array of names in that area
/api/timezone/{area}/{location}[/{region}]time object
/api/ip, /api/ip/{ip}time object for the caller's or the given IPv4/IPv6 address
any of the above + .txttext/plain key: value lines or one name per line
unknown zone404 {"error":"unknown location"} / Error: unknown location
malformed IP404 {"error":"malformed ip"} / Error: malformed ip
path that is not an endpoint404 {"error":"not found"} / Error: not found

What is different, on purpose?

  • API key required on every request; it shares your plan's monthly quota with the /v1 API.
  • Microseconds end in 000 (millisecond clock).
  • .txt lines are alphabetical, the fixed order WorldTimeAPI used from 2019 to July 2024.
  • Newer tz and IP data: 597 zones including America/Coyhaique; a few IP addresses may map differently than they did years ago.
  • Quota instead of rate-limit headers: limits follow your plan; there are no x-ratelimit-* headers.
  • Optional extra: sign=true adds six X-TimeLogic-* signature headers and leaves the body byte-for-byte unchanged, on JSON and .txt (signed responses).

Troubleshooting the move

"Connection reset by peer", ERR_EMPTY_RESPONSE or timeouts from worldtimeapi.org

The old service is offline. When we checked on October 30, 2026, worldtimeapi.org closed connections without answering. Python raises ConnectionResetError or requests.exceptions.ConnectionError, Node shows ECONNRESET, Flutter a SocketException. Nothing in your code fixes it; change the host.

410 Gone: "WorldTimeAPI has been sunset"

From April 2026, worldtimeapi.org answered with 410 Gone and {"error":"gone","status":410,"message":"WorldTimeAPI has been sunset. This endpoint is gone."} (archived response). 410 is permanent; retries and backoff will not bring it back.

502, 503 and other server errors from worldtimeapi.org

Before the shutdown the service was frequently down: its status page showed 15.48% uptime for the month before January 11, 2025, and users reported 2.79% in November 2025 (Python forum thread). Gateway errors from that host were the service, not your network.

A CORS error in the browser

A failed or empty response carries no Access-Control-Allow-Origin header, so browsers report a dying server as a CORS problem. The compatible routes send Access-Control-Allow-Origin: *. If your page is public, route the call through your own backend so the key is not exposed.

401 UNAUTHORIZED after switching

No key, a wrong key, or two different keys in one request. Send exactly one of Authorization: Bearer, X-API-Key or api_key. The error body is TimeLogic's standard JSON ({"error":{"code":"UNAUTHORIZED","message":"Missing API key credentials."},"request_id":"…"}), not a WorldTimeAPI-style body; see errors.

404 {"error":"unknown location"}

The zone name is not in the list. Check the spelling against /api/timezone (any letter case works). These 404s do not count against your quota.

429 QUOTA_EXCEEDED or 403 SUBSCRIPTION_INACTIVE

The plan's monthly quota is used up, or the key's subscription is inactive. See pricing for plan limits.

TLS errors on microcontrollers

Use https:// with WiFiClientSecure (ESP32) or BearSSL::WiFiClientSecure (ESP8266). setInsecure() gets a prototype running; pin the root certificate for production.

Should new code keep using the /api routes?

The compatible routes are for existing code. New code can use the same key on the /v1 World Time API for things WorldTimeAPI never offered: timezone by coordinates or IP, conversion, DST transitions, business days and holidays and bulk lookups. Comparing options first? See the time API comparison.

Frequently asked questions

What is the smallest change that moves my code off worldtimeapi.org?

Change the base URL from http://worldtimeapi.org to https://api.timelogicapi.com and send a TimeLogic API key. The path after the domain, the 15 JSON fields and the .txt format stay the same. If you cannot add a header, append ?api_key=YOUR_KEY to the URL.

Why do I get "Connection reset by peer" or ERR_EMPTY_RESPONSE from worldtimeapi.org?

Because the service is gone, not because of your code. WorldTimeAPI was shut down in 2026; when we checked on October 30, 2026 it closed connections without answering. Point the same path at https://api.timelogicapi.com and add a key.

What does 410 Gone from worldtimeapi.org mean?

Since April 2026 worldtimeapi.org has answered some requests with 410 Gone and the message "WorldTimeAPI has been sunset. This endpoint is gone." 410 means the resource is permanently removed, so retrying will not help. Switch the host.

My browser shows a CORS error for worldtimeapi.org. Is my CORS setup wrong?

Usually not. When a server fails or returns an error page without an Access-Control-Allow-Origin header, the browser reports it as a CORS error. The TimeLogic WorldTimeAPI-compatible routes answer with Access-Control-Allow-Origin: *. Keep your API key on a server where you can, because anything in browser code is visible to users.

I switched hosts and now get 401 UNAUTHORIZED. What is missing?

The API key. Every request needs one, sent as Authorization: Bearer YOUR_KEY, X-API-Key: YOUR_KEY or ?api_key=YOUR_KEY. The error message says which problem it is: Missing API key credentials, Invalid API key credentials, or Conflicting API key credentials when two different keys arrive in one request.

Do I have to change my JSON parsing?

No. The time object has the same 15 fields with the same names, types and formats, so code that reads datetime, unixtime, utc_offset, dst or week_number keeps working. The differences are small: microseconds always end in 000, and .txt lines are in alphabetical order.

Can an ESP32 or Arduino use the new endpoint?

Yes. Use HTTPS with WiFiClientSecure and send the key in an X-API-Key header (or the api_key query parameter). For the smallest sketches, the .txt route returns key: value lines that are easy to parse without a JSON library.

Does the zone list stay the same?

Almost exactly. /api/timezone returns WorldTimeAPI's final 596 names plus America/Coyhaique, 597 in all, and names match in any letter case.