diff --git a/glide.lock b/glide.lock
index 50f3c83c3b4279bc7845c4596f3f40f40351a8c5..bbfcb979be3e03f18ad0861cd567063fdb0f89fb 100644
--- a/glide.lock
+++ b/glide.lock
@@ -1,8 +1,21 @@
-hash: e3afe7d6be6078ab261a84bcc69e2f87d9805c7553025546d12074b9926fd288
-updated: 2018-05-24T12:12:33.899483528+02:00
+hash: 096846ddf934eee04d5e78003271d70eae05b8167e2611f72d6c34c187689b27
+updated: 2018-05-24T18:48:12.125295149+02:00
 imports:
+- name: github.com/go-redis/redis
+  version: 0f9028adf0837cf93c9705817493e5f6997cf026
 - name: github.com/lib/pq
   version: 90697d60dd844d5ef6ff15135d0203f65d2f53b8
   subpackages:
   - oid
+- name: gopkg.in/bsm/ratelimit.v1
+  version: db14e161995a5177acef654cb0dd785e8ee8bc22
+- name: gopkg.in/redis.v4
+  version: c938162545c57136fa59879746bc65d0f1db3d1e
+  subpackages:
+  - internal
+  - internal/consistenthash
+  - internal/errors
+  - internal/hashtag
+  - internal/pool
+  - internal/proto
 testImports: []
diff --git a/glide.yaml b/glide.yaml
index c1239165b972e041b5a29a68e6efaa398d562c1b..dd7f22ab2dc1ef4f9ad4f551adc11dcc0a50f6b5 100644
--- a/glide.yaml
+++ b/glide.yaml
@@ -1,3 +1,5 @@
 package: git.kuschku.de/justjanne/statsbot
 import:
-- package: github.com/lib/pq
\ No newline at end of file
+- package: github.com/lib/pq
+- package: github.com/go-redis/redis
+  version: ^6.11.0
\ No newline at end of file
diff --git a/main.go b/main.go
index 32cad87e117ba647bce5f462d7cceaf086c0bcd8..c861ead7bd443b220e9c3486cd722ca4a22ef0c6 100644
--- a/main.go
+++ b/main.go
@@ -2,8 +2,10 @@ package main
 
 import (
 	"database/sql"
+	"encoding/json"
 	"fmt"
 	_ "github.com/lib/pq"
+	"gopkg.in/redis.v4"
 	"html/template"
 	"net/http"
 	"os"
@@ -16,6 +18,7 @@ const DEBUG = true
 
 type Config struct {
 	Database DatabaseConfig
+	Redis    RedisConfig
 }
 
 type DatabaseConfig struct {
@@ -23,12 +26,20 @@ type DatabaseConfig struct {
 	Url    string
 }
 
+type RedisConfig struct {
+	Address  string
+	Password string
+}
+
 func NewConfigFromEnv() Config {
 	config := Config{}
 
 	config.Database.Format = os.Getenv("KSTATS_DATABASE_TYPE")
 	config.Database.Url = os.Getenv("KSTATS_DATABASE_URL")
 
+	config.Redis.Address = os.Getenv("KSTATS_REDIS_ADDRESS")
+	config.Redis.Password = os.Getenv("KSTATS_REDIS_PASSWORD")
+
 	return config
 }
 
@@ -107,6 +118,11 @@ func handleError(err error) {
 func main() {
 	config := NewConfigFromEnv()
 
+	redisClient := redis.NewClient(&redis.Options{
+		Addr:     config.Redis.Address,
+		Password: config.Redis.Password,
+	})
+
 	db, err := sql.Open(config.Database.Format, config.Database.Url)
 	if err != nil {
 		panic(err)
@@ -116,100 +132,113 @@ func main() {
 	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
 		_, channel := path.Split(r.URL.Path)
 		if strings.HasPrefix(channel, "#") {
-			channelData := ChannelData{}
-			err = db.QueryRow("SELECT id, channel FROM channels WHERE channel ILIKE $1", channel).Scan(&channelData.Id, &channelData.Name)
-			if err != nil {
-				handleError(err)
-				return
-			}
-			err = db.QueryRow("SELECT COUNT(*), SUM(words), AVG(words), AVG(characters) FROM messages WHERE channel = $1", channelData.Id).Scan(&channelData.Lines, &channelData.Words, &channelData.WordsPerLine, &channelData.CharactersPerLine)
-			if err != nil {
-				handleError(err)
-				return
+			var channelData ChannelData
+			data, err := redisClient.Get(channel).Bytes()
+			if err == nil {
+				err = json.Unmarshal(data, channelData)
 			}
 
-			channelData.Users, err = retrieveUsers(db, channelData.Id)
 			if err != nil {
-				handleError(err)
-				return
+				channelData, err = buildChannelData(db, channel)
+				if err != nil {
+					handleError(err)
+					return
+				}
+				data, err = json.Marshal(channelData)
+				err = redisClient.Set(channel, data, time.Minute*5).Err()
+				if err != nil {
+					handleError(err)
+					return
+				}
 			}
 
-			channelData.Questions, err = retrievePercentageStats(db, channelData.Id, "question")
+			err = formatTemplate(w, "statistics", channelData)
 			if err != nil {
 				handleError(err)
 				return
 			}
+		}
+	})
 
-			channelData.Exclamations, err = retrievePercentageStats(db, channelData.Id, "exclamation")
-			if err != nil {
-				handleError(err)
-				return
-			}
+	http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
+		w.Write([]byte("OK"))
+	})
 
-			channelData.Caps, err = retrievePercentageStats(db, channelData.Id, "caps")
-			if err != nil {
-				handleError(err)
-				return
-			}
+	err = http.ListenAndServe(":8080", nil)
+	if err != nil {
+		panic(err)
+	}
+}
 
-			channelData.EmojiHappy, err = retrievePercentageStats(db, channelData.Id, "emoji_happy")
-			if err != nil {
-				handleError(err)
-				return
-			}
+func buildChannelData(db *sql.DB, channel string) (channelData ChannelData, err error) {
+	fmt.Printf("Recomputing channel data for %s\n", channel)
 
-			channelData.EmojiSad, err = retrievePercentageStats(db, channelData.Id, "emoji_sad")
-			if err != nil {
-				handleError(err)
-				return
-			}
+	err = db.QueryRow("SELECT id, channel FROM channels WHERE channel ILIKE $1", channel).Scan(&channelData.Id, &channelData.Name)
+	if err != nil {
+		return
+	}
 
-			channelData.LongestLines, err = retrieveLongestLines(db, channelData.Id)
-			if err != nil {
-				handleError(err)
-				return
-			}
+	err = db.QueryRow("SELECT COUNT(*), SUM(words), AVG(words), AVG(characters) FROM messages WHERE channel = $1", channelData.Id).Scan(&channelData.Lines, &channelData.Words, &channelData.WordsPerLine, &channelData.CharactersPerLine)
+	if err != nil {
+		return
+	}
 
-			channelData.ShortestLines, err = retrieveShortestLines(db, channelData.Id)
-			if err != nil {
-				handleError(err)
-				return
-			}
+	channelData.Users, err = retrieveUsers(db, channelData.Id)
+	if err != nil {
+		return
+	}
 
-			channelData.TotalWords, err = retrieveTotalWords(db, channelData.Id)
-			if err != nil {
-				handleError(err)
-				return
-			}
+	channelData.Questions, err = retrievePercentageStats(db, channelData.Id, "question")
+	if err != nil {
+		return
+	}
 
-			channelData.References, err = retrieveReferences(db, channelData.Id)
-			if err != nil {
-				handleError(err)
-				return
-			}
+	channelData.Exclamations, err = retrievePercentageStats(db, channelData.Id, "exclamation")
+	if err != nil {
+		return
+	}
 
-			channelData.AverageWords, err = retrieveAverageWords(db, channelData.Id)
-			if err != nil {
-				handleError(err)
-				return
-			}
+	channelData.Caps, err = retrievePercentageStats(db, channelData.Id, "caps")
+	if err != nil {
+		return
+	}
 
-			err = formatTemplate(w, "statistics", channelData)
-			if err != nil {
-				handleError(err)
-				return
-			}
-		}
-	})
+	channelData.EmojiHappy, err = retrievePercentageStats(db, channelData.Id, "emoji_happy")
+	if err != nil {
+		return
+	}
 
-	http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
-		w.Write([]byte("OK"))
-	})
+	channelData.EmojiSad, err = retrievePercentageStats(db, channelData.Id, "emoji_sad")
+	if err != nil {
+		return
+	}
 
-	err = http.ListenAndServe(":8080", nil)
+	channelData.LongestLines, err = retrieveLongestLines(db, channelData.Id)
 	if err != nil {
-		panic(err)
+		return
 	}
+
+	channelData.ShortestLines, err = retrieveShortestLines(db, channelData.Id)
+	if err != nil {
+		return
+	}
+
+	channelData.TotalWords, err = retrieveTotalWords(db, channelData.Id)
+	if err != nil {
+		return
+	}
+
+	channelData.References, err = retrieveReferences(db, channelData.Id)
+	if err != nil {
+		return
+	}
+
+	channelData.AverageWords, err = retrieveAverageWords(db, channelData.Id)
+	if err != nil {
+		return
+	}
+
+	return
 }
 
 func retrievePercentageStats(db *sql.DB, channel int, stats string) ([]FloatEntry, error) {