diff --git a/handlers.go b/handlers.go
new file mode 100644
index 0000000000000000000000000000000000000000..f0ab2aec48b7770a187b5063ab4ec73b90c1c17f
--- /dev/null
+++ b/handlers.go
@@ -0,0 +1,89 @@
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"html/template"
+	"net/http"
+	"net/url"
+)
+
+// Index must be wrapped by the withTemplates middleware before it can be used
+func index(w http.ResponseWriter, r *http.Request, t *template.Template) {
+	t = t.Lookup("index.html")
+	if t == nil {
+		http.Error(w, fmt.Sprintf("error looking up index.html"), http.StatusInternalServerError)
+		return
+	}
+	t.Execute(w, nil)
+}
+
+// FormsPost must be wrapped by withTemplates middleware before it can be used
+func formsPost(w http.ResponseWriter, r *http.Request, t *template.Template) {
+	t = t.Lookup("forms-post.html")
+	if t == nil {
+		http.Error(w, fmt.Sprintf("error looking up index.html"), http.StatusInternalServerError)
+		return
+	}
+	t.Execute(w, nil)
+}
+
+func get(w http.ResponseWriter, r *http.Request) {
+	args, err := url.ParseQuery(r.URL.RawQuery)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("error parsing query params: %s", err), http.StatusBadRequest)
+		return
+	}
+	resp := &getResponse{
+		Args:    args,
+		Headers: r.Header,
+		Origin:  getOrigin(r),
+		URL:     getURL(r),
+	}
+	body, _ := json.Marshal(resp)
+	writeJSON(w, body, http.StatusOK)
+}
+
+func post(w http.ResponseWriter, r *http.Request) {
+	args, err := url.ParseQuery(r.URL.RawQuery)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("error parsing query params: %s", err), http.StatusBadRequest)
+		return
+	}
+
+	resp := &bodyResponse{
+		Args:    args,
+		Headers: r.Header,
+		Origin:  getOrigin(r),
+		URL:     getURL(r),
+	}
+
+	err = parseBody(w, r, resp)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("error parsing request body: %s", err), http.StatusBadRequest)
+	}
+
+	body, _ := json.Marshal(resp)
+	writeJSON(w, body, http.StatusOK)
+}
+
+func ip(w http.ResponseWriter, r *http.Request) {
+	body, _ := json.Marshal(&ipResponse{
+		Origin: getOrigin(r),
+	})
+	writeJSON(w, body, http.StatusOK)
+}
+
+func userAgent(w http.ResponseWriter, r *http.Request) {
+	body, _ := json.Marshal(&userAgentResponse{
+		UserAgent: r.Header.Get("User-Agent"),
+	})
+	writeJSON(w, body, http.StatusOK)
+}
+
+func headers(w http.ResponseWriter, r *http.Request) {
+	body, _ := json.Marshal(&headersResponse{
+		Headers: r.Header,
+	})
+	writeJSON(w, body, http.StatusOK)
+}
diff --git a/main_test.go b/handlers_test.go
similarity index 97%
rename from main_test.go
rename to handlers_test.go
index 1c8563d5a9338a2903c2553ee261775d8d027880..e923c880d897c3b6541e35e4b92e190d0db97a3f 100644
--- a/main_test.go
+++ b/handlers_test.go
@@ -42,7 +42,7 @@ func TestGet__Basic(t *testing.T) {
 		t.Fatalf("expected status code 200, got %d", w.Code)
 	}
 
-	var resp *Resp
+	var resp *bodyResponse
 	err := json.Unmarshal(w.Body.Bytes(), &resp)
 	if err != nil {
 		t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err)
@@ -160,7 +160,7 @@ func TestGet__XForwardedProto(t *testing.T) {
 		w := httptest.NewRecorder()
 		app().ServeHTTP(w, r)
 
-		var resp *Resp
+		var resp *bodyResponse
 		err := json.Unmarshal(w.Body.Bytes(), &resp)
 		if err != nil {
 			t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err)
@@ -178,7 +178,7 @@ func TestIP(t *testing.T) {
 	w := httptest.NewRecorder()
 	app().ServeHTTP(w, r)
 
-	var resp *IPResp
+	var resp *ipResponse
 	err := json.Unmarshal(w.Body.Bytes(), &resp)
 	if err != nil {
 		t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err)
@@ -195,7 +195,7 @@ func TestUserAgent(t *testing.T) {
 	w := httptest.NewRecorder()
 	app().ServeHTTP(w, r)
 
-	var resp *UserAgentResp
+	var resp *userAgentResponse
 	err := json.Unmarshal(w.Body.Bytes(), &resp)
 	if err != nil {
 		t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err)
@@ -215,7 +215,7 @@ func TestHeaders(t *testing.T) {
 	w := httptest.NewRecorder()
 	app().ServeHTTP(w, r)
 
-	var resp *HeadersResp
+	var resp *headersResponse
 	err := json.Unmarshal(w.Body.Bytes(), &resp)
 	if err != nil {
 		t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err)
@@ -237,7 +237,7 @@ func TestPost__EmptyBody(t *testing.T) {
 	w := httptest.NewRecorder()
 	app().ServeHTTP(w, r)
 
-	var resp *Resp
+	var resp *bodyResponse
 	err := json.Unmarshal(w.Body.Bytes(), &resp)
 	if err != nil {
 		t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err)
@@ -262,7 +262,7 @@ func TestPost__FormEncodedBody(t *testing.T) {
 	w := httptest.NewRecorder()
 	app().ServeHTTP(w, r)
 
-	var resp *Resp
+	var resp *bodyResponse
 	err := json.Unmarshal(w.Body.Bytes(), &resp)
 	if err != nil {
 		t.Fatalf("failed to unmarshal body %#v from JSON: %s", w.Body.String(), err)
@@ -295,7 +295,7 @@ func TestPost__FormEncodedBodyNoContentType(t *testing.T) {
 	w := httptest.NewRecorder()
 	app().ServeHTTP(w, r)
 
-	var resp *Resp
+	var resp *bodyResponse
 	err := json.Unmarshal(w.Body.Bytes(), &resp)
 	if err != nil {
 		t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err)
@@ -332,7 +332,7 @@ func TestPost__JSON(t *testing.T) {
 	w := httptest.NewRecorder()
 	app().ServeHTTP(w, r)
 
-	var resp *Resp
+	var resp *bodyResponse
 	err := json.Unmarshal(w.Body.Bytes(), &resp)
 	if err != nil {
 		t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err)
diff --git a/helpers.go b/helpers.go
index e7c4a155d5ae7a357497f0df1f586a403e1082d4..b8daae9c46be8422dbb3f21fb4d1da86841927b5 100644
--- a/helpers.go
+++ b/helpers.go
@@ -47,14 +47,8 @@ func getURL(r *http.Request) string {
 	return u.String()
 }
 
-func writeResponse(w http.ResponseWriter, r *http.Request, resp *Resp) {
-	resp.Origin = getOrigin(r)
-	resp.URL = getURL(r)
-	body, _ := json.Marshal(resp)
-	writeJSON(w, body)
-}
-
-func writeJSON(w http.ResponseWriter, body []byte) {
+func writeJSON(w http.ResponseWriter, body []byte, status int) {
+	w.WriteHeader(status)
 	w.Header().Set("Content-Type", "application/json; encoding=utf-8")
 	w.Write(body)
 }
@@ -62,7 +56,7 @@ func writeJSON(w http.ResponseWriter, body []byte) {
 // parseBody handles parsing a request body into our standard API response,
 // taking care to only consume the request body once based on the Content-Type
 // of the request. The given Resp will be updated.
-func parseBody(w http.ResponseWriter, r *http.Request, resp *Resp) error {
+func parseBody(w http.ResponseWriter, r *http.Request, resp *bodyResponse) error {
 	if r.Body == nil {
 		return nil
 	}
diff --git a/main.go b/main.go
index a99de7ccf9f4dc7f9146f0efafd2b31bbbdd862f..891dfc6bf5157189319b5ebb99feb804b91f98f0 100644
--- a/main.go
+++ b/main.go
@@ -1,118 +1,13 @@
 package main
 
 import (
-	"encoding/json"
-	"fmt"
-	"html/template"
 	"log"
 	"net/http"
-	"net/url"
 )
 
-// Resp is the standard JSON response from httpbin
-type Resp struct {
-	Args    url.Values  `json:"args"`
-	Headers http.Header `json:"headers"`
-	Origin  string      `json:"origin"`
-	URL     string      `json:"url"`
-
-	Data  []byte              `json:"data,omitempty"`
-	Files map[string][]string `json:"files,omitempty"`
-	Form  map[string][]string `json:"form,omitempty"`
-	JSON  interface{}         `json:"json,omitempty"`
-}
-
-// IPResp is the response for the /ip endpoint
-type IPResp struct {
-	Origin string `json:"origin"`
-}
-
-// HeadersResp is the response for the /headers endpoint
-type HeadersResp struct {
-	Headers http.Header `json:"headers"`
-}
-
-// UserAgentResp is the response for the /user-agent endpoint
-type UserAgentResp struct {
-	UserAgent string `json:"user-agent"`
-}
-
 // Max size of a request body we'll handle
 const maxMemory = 1024*1024*5 + 1
 
-// Index must be wrapped by the withTemplates middleware before it can be used
-func index(w http.ResponseWriter, r *http.Request, t *template.Template) {
-	t = t.Lookup("index.html")
-	if t == nil {
-		http.Error(w, fmt.Sprintf("error looking up index.html"), http.StatusInternalServerError)
-		return
-	}
-	t.Execute(w, nil)
-}
-
-// FormsPost must be wrapped by withTemplates middleware before it can be used
-func formsPost(w http.ResponseWriter, r *http.Request, t *template.Template) {
-	t = t.Lookup("forms-post.html")
-	if t == nil {
-		http.Error(w, fmt.Sprintf("error looking up index.html"), http.StatusInternalServerError)
-		return
-	}
-	t.Execute(w, nil)
-}
-
-func get(w http.ResponseWriter, r *http.Request) {
-	args, err := url.ParseQuery(r.URL.RawQuery)
-	if err != nil {
-		http.Error(w, fmt.Sprintf("error parsing query params: %s", err), http.StatusBadRequest)
-		return
-	}
-	resp := &Resp{
-		Args:    args,
-		Headers: r.Header,
-	}
-	writeResponse(w, r, resp)
-}
-
-func post(w http.ResponseWriter, r *http.Request) {
-	args, err := url.ParseQuery(r.URL.RawQuery)
-	if err != nil {
-		http.Error(w, fmt.Sprintf("error parsing query params: %s", err), http.StatusBadRequest)
-		return
-	}
-
-	resp := &Resp{
-		Args:    args,
-		Headers: r.Header,
-	}
-
-	err = parseBody(w, r, resp)
-	if err != nil {
-		http.Error(w, fmt.Sprintf("error parsing request body: %s", err), http.StatusBadRequest)
-	}
-	writeResponse(w, r, resp)
-}
-
-func ip(w http.ResponseWriter, r *http.Request) {
-	body, _ := json.Marshal(&IPResp{
-		Origin: getOrigin(r),
-	})
-	writeJSON(w, body)
-}
-
-func userAgent(w http.ResponseWriter, r *http.Request) {
-	body, _ := json.Marshal(&UserAgentResp{
-		UserAgent: r.Header.Get("User-Agent"),
-	})
-	writeJSON(w, body)
-}
-
-func headers(w http.ResponseWriter, r *http.Request) {
-	body, _ := json.Marshal(&HeadersResp{
-		Headers: r.Header,
-	})
-	writeJSON(w, body)
-}
-
 func app() http.Handler {
 	h := http.NewServeMux()
 	templateWrapper := withTemplates("templates/*.html")
diff --git a/types.go b/types.go
new file mode 100644
index 0000000000000000000000000000000000000000..75a314f132a21032391b657f9de9354eb1b6b0d2
--- /dev/null
+++ b/types.go
@@ -0,0 +1,38 @@
+package main
+
+import (
+	"net/http"
+	"net/url"
+)
+
+type headersResponse struct {
+	Headers http.Header `json:"headers"`
+}
+
+type ipResponse struct {
+	Origin string `json:"origin"`
+}
+
+type userAgentResponse struct {
+	UserAgent string `json:"user-agent"`
+}
+
+type getResponse struct {
+	Args    url.Values  `json:"args"`
+	Headers http.Header `json:"headers"`
+	Origin  string      `json:"origin"`
+	URL     string      `json:"url"`
+}
+
+// A generic response for any incoming request that might contain a body
+type bodyResponse struct {
+	Args    url.Values  `json:"args"`
+	Headers http.Header `json:"headers"`
+	Origin  string      `json:"origin"`
+	URL     string      `json:"url"`
+
+	Data  []byte              `json:"data"`
+	Files map[string][]string `json:"files"`
+	Form  map[string][]string `json:"form"`
+	JSON  interface{}         `json:"json"`
+}