diff --git a/xtrn/wttr.in/readme.txt b/xtrn/wttr.in/readme.txt
index 416a59957f62e56e33c3262ec5fa1f7e0188c41a..80a18a5eaa1f70c1bbf1b319499b13208e1999be 100644
--- a/xtrn/wttr.in/readme.txt
+++ b/xtrn/wttr.in/readme.txt
@@ -33,10 +33,19 @@ Contents
 
 3) Customization
 
-	Any arguments that you pass on the command line will be treated as an
-	alternate URL to query. For example:
+	The default query URL is https://wttr.in/?AFn for ANSI, no 'Follow' line,
+	and narrow output. You can override this by supplying an alternate URL on
+	the command line, such as:
 
 		Command Line	?wttr.js https://wttr.in/?m0AFn
 
-	The default is URL is 'https://wttr.in/?AFn' for ANSI, no 'Follow' line,
-	and narrow output.
+	Responses are cached in your OS temp directory and remain valid for 3600
+	seconds (one hour). You can override this default cache TTL by supplying
+	a numeric value (in seconds) on the command line, such as:
+
+		Command Line	?wttr.js 1800
+
+	You can combine the parameters in whichever order you prefer:
+
+		Command Line 	?wttr.js https://wttr.in/?m0AFn 1800
+		Command Line 	?wttr.js 1800 https://wttr.in/?m0AFn
diff --git a/xtrn/wttr.in/wttr-lib.js b/xtrn/wttr.in/wttr-lib.js
new file mode 100644
index 0000000000000000000000000000000000000000..0515c3ac552d5b9fbb6da1c5360ae4cb52e6dce6
--- /dev/null
+++ b/xtrn/wttr.in/wttr-lib.js
@@ -0,0 +1,52 @@
+require('http.js', 'HTTPRequest');
+const locator = load({}, js.exec_dir + 'locator.js');
+const xterm = load({}, js.exec_dir + 'xterm-colors.js');
+
+function uReplace(str) {
+	return str.replace(/\xE2\x9A\xA1/g, '\x01+\x01h\x01yZ \x01-'); // U+26A1 Lightning bolt
+}
+
+function getCacheName(qs, addr) {
+	const cfn = format('wttr.in_%s_%s.ans', qs, addr || '').replace(/[^0-9a-z\.]+/ig, '_');
+	return system.temp_path + cfn;
+}
+
+function readCache(qs, addr, ttl) {
+	const cfn = getCacheName(qs, addr);
+	const f = new File(cfn);
+	if (!f.exists) return;
+	if (time() - file_date(cfn) > ttl) return;
+	if (!f.open('r')) return;
+	const cache = f.read();
+	f.close();
+	return cache;
+}
+
+function writeCache(qs, addr, ans) {
+	const cfn = getCacheName(qs, addr);
+	const f = new File(cfn);
+	if (!f.open('w')) return;
+	f.write(ans);
+	f.close();
+}
+
+function fetchWeather(qs, addr) {
+	const http = new HTTPRequest();
+	if (addr !== undefined) http.extra_headers = { 'X-Forwarded-For': addr };
+	const body = http.Get(qs);
+	if (http.response_code !== 200) throw new Error('wttr.in response had status ' + http.response_code);
+	return body;
+}
+
+function getWeather(qs, ttl) {
+	const addr = locator.getAddress();
+	const cachedWeather = readCache(qs, addr, ttl);
+	if (cachedWeather !== undefined) return cachedWeather;
+	const weather = fetchWeather(qs, addr);
+	const text = uReplace(weather);
+	const ansi = xterm.convertColors(text);
+	writeCache(qs, addr, ansi);
+	return ansi;
+}
+
+this;
\ No newline at end of file
diff --git a/xtrn/wttr.in/wttr.js b/xtrn/wttr.in/wttr.js
index ccc724c7edac7d3a1aec09a26ae577522336fa1a..74533859705704e64a916c0783b9ce3cac4361c6 100644
--- a/xtrn/wttr.in/wttr.js
+++ b/xtrn/wttr.in/wttr.js
@@ -1,26 +1,25 @@
 require('sbbsdefs.js', 'P_UTF8');
-require('http.js', 'HTTPRequest');
-const xterm = load({}, js.exec_dir + 'xterm-colors.js');
-const locator = load({}, js.exec_dir + 'locator.js');
+const wttr = load({}, js.exec_dir + 'wttr-lib.js');
 
-function uReplace(str) {
-	return str.replace(/\xE2\x9A\xA1/g, '/ '); // U+26A1 Lightning bolt
-}
-
-function fetchWeather(addr) {
-	const qs = argc > 0 ? argv.join('') : 'https://wttr.in/?AFn';
-	const http = new HTTPRequest();
-	if (addr !== undefined) http.extra_headers = { 'X-Forwarded-For': addr };
-	const body = http.Get(qs);
-	if (http.response_code !== 200) throw new Error('wttr.in response had status ' + http.response_code);
-	return body;
+function parseArgs() {
+	const ret = {
+		qs: 'https://wttr.in/?AFn',
+		ttl: 3600, // Seconds
+	};
+	for (var n = 0; n < argc; n++) {
+		const arg = parseInt(argv[n], 10);
+		if (isNaN(arg)) {
+			ret.qs = arg;
+		} else {
+			ret.ttl = arg;
+		}
+	}
+	return ret;
 }
 
 function main() {
-	const addr = locator.getAddress();
-	const weather = fetchWeather(addr);
-	const text = uReplace(weather);
-	const ansi = xterm.convertColors(text);
+	const args = parseArgs();
+	const ansi = wttr.getWeather(args.qs, args.ttl);
 	const attr = console.attributes;
 	console.clear(BG_BLACK|LIGHTGRAY);
 	console.putmsg(ansi, P_UTF8);