diff --git a/src/apiutils.nim b/src/apiutils.nim
index cebe3afac99d7c407a48eb63442180d31e565709..89c7687c65fddffb77d6d17ff3e0a77d7b1af7f7 100644
--- a/src/apiutils.nim
+++ b/src/apiutils.nim
@@ -31,13 +31,16 @@ proc genHeaders*(token: Token = nil): HttpHeaders =
     "DNT": "1"
   })
 
+proc rateLimitError(): ref RateLimitError =
+  newException(RateLimitError, "rate limited with " & getPoolInfo())
+
 proc fetch*(url: Uri; oldApi=false): Future[JsonNode] {.async.} =
   once:
     pool = HttpPool()
 
   var token = await getToken()
   if token.tok.len == 0:
-    result = newJNull()
+    raise rateLimitError()
 
   let headers = genHeaders(token)
   try:
@@ -59,4 +62,4 @@ proc fetch*(url: Uri; oldApi=false): Future[JsonNode] {.async.} =
       token.release()
   except Exception:
     echo "error: ", url
-    result = newJNull()
+    raise rateLimitError()
diff --git a/src/nitter.nim b/src/nitter.nim
index 11951b169c24196ad4cf8023bfddc728848179db..586cd4eabf1ff1a063565b5289ab363af6687e87 100644
--- a/src/nitter.nim
+++ b/src/nitter.nim
@@ -1,5 +1,6 @@
 import asyncdispatch, strformat
 from net import Port
+from htmlgen import a
 
 import jester
 
@@ -9,6 +10,8 @@ import routes/[
   preferences, timeline, status, media, search, rss, list,
   unsupported, embed, resolver, router_utils]
 
+const instancesUrl = "https://github.com/zedeus/nitter/wiki/Instances"
+
 const configPath {.strdefine.} = "./nitter.conf"
 let (cfg, fullCfg) = getConfig(configPath)
 
@@ -71,6 +74,12 @@ routes:
   error Http404:
     resp Http404, showError("Page not found", cfg)
 
+  error RateLimitError:
+    echo error.exc.msg
+    resp Http429, showError("Instance has been rate limited.<br>Use " &
+      a("another instance", href = instancesUrl) &
+      " or try again later.", cfg)
+
   extend unsupported, ""
   extend preferences, ""
   extend resolver, ""
diff --git a/src/sass/general.scss b/src/sass/general.scss
index 36a2e4ace7e4ac6958ea79414e47c03ecfaf7f60..9feb3d3fa3fd4205475fafb96d7e42a020cd0309 100644
--- a/src/sass/general.scss
+++ b/src/sass/general.scss
@@ -8,6 +8,7 @@
 
 .error-panel {
     @include center-panel(var(--error_red));
+    text-align: center;
 }
 
 .search-bar > form {
diff --git a/src/tokens.nim b/src/tokens.nim
index 3a85ac79c1b87296334c7b6e93d2675ae1956f62..5741b9d74aab449cc7364daf49a6004244135812 100644
--- a/src/tokens.nim
+++ b/src/tokens.nim
@@ -1,4 +1,5 @@
-import asyncdispatch, httpclient, times, sequtils, strutils, json
+import asyncdispatch, httpclient, times, sequtils, json, math
+import strutils, strformat
 import types, agents, consts, http_pool
 
 var
@@ -32,8 +33,8 @@ proc fetchToken(): Future[Token] {.async.} =
                    init: time, lastUse: time)
   except Exception as e:
     lastFailed = getTime()
-    echo "fetching token failed: ", e.msg
     result = Token()
+    echo "fetching token failed: ", e.msg
 
 proc expired(token: Token): bool {.inline.} =
   const
@@ -77,3 +78,7 @@ proc initTokenPool*(cfg: Config) {.async.} =
     if tokenPool.countIt(not it.isLimited) < cfg.minTokens:
       await poolTokens(min(4, cfg.minTokens - tokenPool.len))
     await sleepAsync(2000)
+
+proc getPoolInfo*: string =
+  let avg = tokenPool.mapIt(it.remaining).sum()
+  return &"{tokenPool.len} tokens, average remaining: {avg}"
diff --git a/src/types.nim b/src/types.nim
index dbfe1f6bbd2d17ae21d46de24ded88b4fcbdb4f7..756b2775bc73619a218178e963a61db14517deb2 100644
--- a/src/types.nim
+++ b/src/types.nim
@@ -4,6 +4,8 @@ import prefs_impl
 genPrefsType()
 
 type
+  RateLimitError* = object of CatchableError
+
   Token* = ref object
     tok*: string
     remaining*: int
diff --git a/src/views/general.nim b/src/views/general.nim
index 742b6ea4ae2ca8bdf6dc14ce884e7e1ec5b8e83f..267722a7b82dec2404c7e71b85e2256a43cc5ab6 100644
--- a/src/views/general.nim
+++ b/src/views/general.nim
@@ -117,4 +117,4 @@ proc renderMain*(body: VNode; req: Request; cfg: Config; prefs=defaultPrefs;
 proc renderError*(error: string): VNode =
   buildHtml(tdiv(class="panel-container")):
     tdiv(class="error-panel"):
-      span: text error
+      span: verbatim error