diff --git a/config/routes.go b/config/routes.go
index e95b5f2b7d16e92d8df7ca478308a4a05aff7ddf..7a89f6ede653cfc92f55e2740afab6f56a8e819a 100644
--- a/config/routes.go
+++ b/config/routes.go
@@ -13,4 +13,8 @@ func GddRoutes(route fiber.Router) {
 	route.Get("gdd/cfs", controllers.GetCfsGDD)
 	route.Get("gdd/analog", controllers.GetAnalogYear)
 	route.Get("gdd/confidence", controllers.GetConfidenceInterval)
+
+	route.Get("freezing-dates", controllers.GetFreezingDates)
+
+	route.Get("gdd/csv", controllers.GetCSVFile)
 }
diff --git a/controllers/data_download_controller.go b/controllers/data_download_controller.go
new file mode 100644
index 0000000000000000000000000000000000000000..d94b0655bf0df3f33531cf350bffb8fdc786199f
--- /dev/null
+++ b/controllers/data_download_controller.go
@@ -0,0 +1,42 @@
+package controllers
+
+import (
+	"dawn-weather/models"
+	"dawn-weather/services"
+
+	"github.com/gofiber/fiber/v2"
+)
+
+// GetCSVFile godoc
+// @Summary Get gdd data csv
+// @Tags Gdd
+// @Description Get gdd data csv
+// @Accept  json
+// @Produce  text/csv
+// @Failure 400 {object} errors.StandardError
+// @Param analog query boolean false "Add analog data to csv"
+// @Param cfs query boolean false "Add cfs data to csv"
+// @Param cfs_upper query boolean false "Add cfs upper bound data to csv"
+// @Param cfs_lower query boolean false "Add cfs lower bound data to csv"
+// @Param comparison query boolean false "Add comparison year data to csv"
+// @Param first_freezing query boolean false "Add first freezing date data to csv"
+// @Param gefs query boolean false "Add gefs data to csv"
+// @Param last_freezing query boolean false "Add last freezing date data to csv"
+// @Param maximum query boolean false "Add maximum boundary of confidence interval data to csv"
+// @Param minimum query boolean false "Add minimum boundary of confidence interval data to csv"
+// @Param normals query boolean false "Add thirty year normal data to csv"
+// @Param primary query boolean false "Add primary GDD data to csv"
+// @Param comparison_year query int false "Comparison year to use"
+// @Param product query string true "Crop to calculate gdd for" Enums(corn, soybean, sunflower, tomato, sugar_beet, peanut, cotton, potato, wheat, pea, oat, spring_wheat, rice, sorghum)
+// @Param range query number false "Confidence interval percentage"
+// @Param temperature query int false "Freezing date temperature"
+// @Param year query int false "Year to get primary data for"
+// @Param latitude query number true "Latitude to search for"
+// @Param longitude query number true "Longitude to search for"
+// @Router /api/weather/gdd/csv [get]
+func GetCSVFile(c *fiber.Ctx) error {
+	request := models.CSVRequest{}.Build(c)
+	fileText := services.GetDataDownload(request)
+	e := c.Status(fiber.StatusOK).SendString(fileText)
+	return e
+}
diff --git a/controllers/freezing_dates_controller.go b/controllers/freezing_dates_controller.go
new file mode 100644
index 0000000000000000000000000000000000000000..25f02cd9f07cbe4696868a6af9a1ccd2af2b58a1
--- /dev/null
+++ b/controllers/freezing_dates_controller.go
@@ -0,0 +1,25 @@
+package controllers
+
+import (
+	"dawn-weather/models"
+	"dawn-weather/services"
+
+	"github.com/gofiber/fiber/v2"
+)
+
+// GetFreezingDates godoc
+// @Summary get freezing dates
+// @Tags Freezing Dates
+// @Description get freezing dates
+// @Accept  json
+// @Produce  json
+// @Success 200 {object} models.FreezingDateResponse
+// @Failure 400 {object} errors.StandardError
+// @Param latitude query number true "Latitude to search for"
+// @Param longitude query number true "Longitude to search for"
+// @Param freezing_temp query number true "Freezing temperature to use"
+// @Router /api/weather/freezing-dates [get]
+func GetFreezingDates(c *fiber.Ctx) error {
+	request := models.FreezingDateRequest{}.Build(c)
+	return c.Status(fiber.StatusOK).JSON(services.GetFreezingDate(request))
+}
diff --git a/controllers/gdd_controller.go b/controllers/gdd_controller.go
index b343cfc3a2d4ef008f663efbdb5ef979d2c9b413..8619d734eddadf1665b02f300bf244ef54589f42 100644
--- a/controllers/gdd_controller.go
+++ b/controllers/gdd_controller.go
@@ -3,6 +3,7 @@ package controllers
 import (
 	"dawn-weather/models"
 	"dawn-weather/services"
+
 	"github.com/gofiber/fiber/v2"
 )
 
diff --git a/docs/docs.go b/docs/docs.go
index 4058d36cbf9f606ababdbee58d38b74f35fbaa43..9f7bc0f339ecf1e4bd57d9a3c515ed5a7b5786d5 100644
--- a/docs/docs.go
+++ b/docs/docs.go
@@ -27,6 +27,58 @@ var doc = `{
     "host": "{{.Host}}",
     "basePath": "{{.BasePath}}",
     "paths": {
+        "/api/weather/freezing-dates": {
+            "get": {
+                "description": "get freezing dates",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Freezing Dates"
+                ],
+                "summary": "get freezing dates",
+                "parameters": [
+                    {
+                        "type": "number",
+                        "description": "Latitude to search for",
+                        "name": "latitude",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "number",
+                        "description": "Longitude to search for",
+                        "name": "longitude",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "number",
+                        "description": "Freezing temperature to use",
+                        "name": "freezing_temp",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/models.FreezingDateResponse"
+                        }
+                    },
+                    "400": {
+                        "description": "Bad Request",
+                        "schema": {
+                            "$ref": "#/definitions/errors.StandardError"
+                        }
+                    }
+                }
+            }
+        },
         "/api/weather/gdd/analog": {
             "get": {
                 "description": "Get analog year",
@@ -72,6 +124,81 @@ var doc = `{
                 }
             }
         },
+        "/api/weather/gdd/cfs": {
+            "get": {
+                "description": "Get GDD values calculated from CFS",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Gdd"
+                ],
+                "summary": "Get GDD values calculated from CFS",
+                "parameters": [
+                    {
+                        "enum": [
+                            "corn",
+                            "soybean",
+                            "sunflower",
+                            "tomato",
+                            "sugar_beet",
+                            "peanut",
+                            "cotton",
+                            "potato",
+                            "wheat",
+                            "pea",
+                            "oat",
+                            "spring_wheat",
+                            "rice",
+                            "sorghum"
+                        ],
+                        "type": "string",
+                        "description": "Crop to calculate gdd for",
+                        "name": "product",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "number",
+                        "description": "Latitude to search for",
+                        "name": "latitude",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "number",
+                        "description": "Longitude to search for",
+                        "name": "longitude",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Accumulate gdd values",
+                        "name": "accumulate",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/models.CfsGddResponse"
+                        }
+                    },
+                    "400": {
+                        "description": "Bad Request",
+                        "schema": {
+                            "$ref": "#/definitions/errors.StandardError"
+                        }
+                    }
+                }
+            }
+        },
         "/api/weather/gdd/confidence": {
             "get": {
                 "description": "Get confidence interval",
@@ -86,6 +213,29 @@ var doc = `{
                 ],
                 "summary": "Get confidence interval",
                 "parameters": [
+                    {
+                        "enum": [
+                            "corn",
+                            "soybean",
+                            "sunflower",
+                            "tomato",
+                            "sugar_beet",
+                            "peanut",
+                            "cotton",
+                            "potato",
+                            "wheat",
+                            "pea",
+                            "oat",
+                            "spring_wheat",
+                            "rice",
+                            "sorghum"
+                        ],
+                        "type": "string",
+                        "description": "Crop to calculate gdd for",
+                        "name": "product",
+                        "in": "query",
+                        "required": true
+                    },
                     {
                         "type": "number",
                         "description": "Latitude to search for",
@@ -117,6 +267,170 @@ var doc = `{
                 }
             }
         },
+        "/api/weather/gdd/csv": {
+            "get": {
+                "description": "Get gdd data csv",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "text/csv"
+                ],
+                "tags": [
+                    "Gdd"
+                ],
+                "summary": "Get gdd data csv",
+                "parameters": [
+                    {
+                        "type": "boolean",
+                        "description": "Add analog data to csv",
+                        "name": "analog",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add cfs data to csv",
+                        "name": "cfs",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add cfs upper bound data to csv",
+                        "name": "cfs_upper",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add cfs lower bound data to csv",
+                        "name": "cfs_lower",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add comparison year data to csv",
+                        "name": "comparison",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add first freezing date data to csv",
+                        "name": "first_freezing",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add gefs data to csv",
+                        "name": "gefs",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add last freezing date data to csv",
+                        "name": "last_freezing",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add maximum boundary of confidence interval data to csv",
+                        "name": "maximum",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add minimum boundary of confidence interval data to csv",
+                        "name": "minimum",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add thirty year normal data to csv",
+                        "name": "normals",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add primary GDD data to csv",
+                        "name": "primary",
+                        "in": "query"
+                    },
+                    {
+                        "type": "integer",
+                        "description": "Comparison year to use",
+                        "name": "comparison_year",
+                        "in": "query"
+                    },
+                    {
+                        "enum": [
+                            "corn",
+                            "soybean",
+                            "sunflower",
+                            "tomato",
+                            "sugar_beet",
+                            "peanut",
+                            "cotton",
+                            "potato",
+                            "wheat",
+                            "pea",
+                            "oat",
+                            "spring_wheat",
+                            "rice",
+                            "sorghum"
+                        ],
+                        "type": "string",
+                        "description": "Crop to calculate gdd for",
+                        "name": "product",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "number",
+                        "description": "Confidence interval percentage",
+                        "name": "range",
+                        "in": "query"
+                    },
+                    {
+                        "type": "integer",
+                        "description": "Freezing date temperature",
+                        "name": "temperature",
+                        "in": "query"
+                    },
+                    {
+                        "type": "integer",
+                        "description": "Year to get primary data for",
+                        "name": "year",
+                        "in": "query"
+                    },
+                    {
+                        "type": "number",
+                        "description": "Latitude to search for",
+                        "name": "latitude",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "number",
+                        "description": "Longitude to search for",
+                        "name": "longitude",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/errors.StandardError"
+                        }
+                    },
+                    "400": {
+                        "description": "Bad Request",
+                        "schema": {
+                            "$ref": "#/definitions/errors.StandardError"
+                        }
+                    }
+                }
+            }
+        },
         "/api/weather/gdd/daily": {
             "get": {
                 "description": "get gdd values",
@@ -390,22 +704,57 @@ var doc = `{
                 }
             }
         },
+        "models.CfsGddResponse": {
+            "type": "object",
+            "properties": {
+                "closest_latitude": {
+                    "type": "number"
+                },
+                "closest_longitude": {
+                    "type": "number"
+                },
+                "first_date": {
+                    "type": "string"
+                },
+                "gdd_values": {
+                    "type": "array",
+                    "items": {
+                        "type": "number"
+                    }
+                },
+                "lower_values": {
+                    "type": "array",
+                    "items": {
+                        "type": "number"
+                    }
+                },
+                "product": {
+                    "type": "string"
+                },
+                "upper_values": {
+                    "type": "array",
+                    "items": {
+                        "type": "number"
+                    }
+                }
+            }
+        },
         "models.ConfidenceIntervalResposne": {
             "type": "object",
             "properties": {
-                "closestLatitude": {
+                "closest_latitude": {
                     "type": "number"
                 },
-                "closestLongitude": {
+                "closest_longitude": {
                     "type": "number"
                 },
-                "lowerBound": {
+                "lower_bound": {
                     "type": "array",
                     "items": {
                         "type": "number"
                     }
                 },
-                "upperBound": {
+                "upper_bound": {
                     "type": "array",
                     "items": {
                         "type": "number"
@@ -413,6 +762,29 @@ var doc = `{
                 }
             }
         },
+        "models.FreezingDateResponse": {
+            "type": "object",
+            "properties": {
+                "closest_latitude": {
+                    "type": "number"
+                },
+                "closest_longitude": {
+                    "type": "number"
+                },
+                "first_date_counts": {
+                    "type": "object",
+                    "additionalProperties": {
+                        "type": "integer"
+                    }
+                },
+                "last_date_counts": {
+                    "type": "object",
+                    "additionalProperties": {
+                        "type": "integer"
+                    }
+                }
+            }
+        },
         "models.GddResponse": {
             "type": "object",
             "properties": {
@@ -457,8 +829,20 @@ var doc = `{
                 "last_date": {
                     "type": "string"
                 },
+                "lower_values": {
+                    "type": "array",
+                    "items": {
+                        "type": "number"
+                    }
+                },
                 "product": {
                     "type": "string"
+                },
+                "upper_values": {
+                    "type": "array",
+                    "items": {
+                        "type": "number"
+                    }
                 }
             }
         }
diff --git a/docs/swagger.json b/docs/swagger.json
index 270dcfa80555a5ff9cfbc599773d7a870a2fea73..8ad42559d8cc70fa7d056061fa67bed8da4ace11 100644
--- a/docs/swagger.json
+++ b/docs/swagger.json
@@ -12,6 +12,58 @@
     "host": "localhost:8080",
     "basePath": "/",
     "paths": {
+        "/api/weather/freezing-dates": {
+            "get": {
+                "description": "get freezing dates",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Freezing Dates"
+                ],
+                "summary": "get freezing dates",
+                "parameters": [
+                    {
+                        "type": "number",
+                        "description": "Latitude to search for",
+                        "name": "latitude",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "number",
+                        "description": "Longitude to search for",
+                        "name": "longitude",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "number",
+                        "description": "Freezing temperature to use",
+                        "name": "freezing_temp",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/models.FreezingDateResponse"
+                        }
+                    },
+                    "400": {
+                        "description": "Bad Request",
+                        "schema": {
+                            "$ref": "#/definitions/errors.StandardError"
+                        }
+                    }
+                }
+            }
+        },
         "/api/weather/gdd/analog": {
             "get": {
                 "description": "Get analog year",
@@ -57,6 +109,81 @@
                 }
             }
         },
+        "/api/weather/gdd/cfs": {
+            "get": {
+                "description": "Get GDD values calculated from CFS",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Gdd"
+                ],
+                "summary": "Get GDD values calculated from CFS",
+                "parameters": [
+                    {
+                        "enum": [
+                            "corn",
+                            "soybean",
+                            "sunflower",
+                            "tomato",
+                            "sugar_beet",
+                            "peanut",
+                            "cotton",
+                            "potato",
+                            "wheat",
+                            "pea",
+                            "oat",
+                            "spring_wheat",
+                            "rice",
+                            "sorghum"
+                        ],
+                        "type": "string",
+                        "description": "Crop to calculate gdd for",
+                        "name": "product",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "number",
+                        "description": "Latitude to search for",
+                        "name": "latitude",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "number",
+                        "description": "Longitude to search for",
+                        "name": "longitude",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Accumulate gdd values",
+                        "name": "accumulate",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/models.CfsGddResponse"
+                        }
+                    },
+                    "400": {
+                        "description": "Bad Request",
+                        "schema": {
+                            "$ref": "#/definitions/errors.StandardError"
+                        }
+                    }
+                }
+            }
+        },
         "/api/weather/gdd/confidence": {
             "get": {
                 "description": "Get confidence interval",
@@ -71,6 +198,29 @@
                 ],
                 "summary": "Get confidence interval",
                 "parameters": [
+                    {
+                        "enum": [
+                            "corn",
+                            "soybean",
+                            "sunflower",
+                            "tomato",
+                            "sugar_beet",
+                            "peanut",
+                            "cotton",
+                            "potato",
+                            "wheat",
+                            "pea",
+                            "oat",
+                            "spring_wheat",
+                            "rice",
+                            "sorghum"
+                        ],
+                        "type": "string",
+                        "description": "Crop to calculate gdd for",
+                        "name": "product",
+                        "in": "query",
+                        "required": true
+                    },
                     {
                         "type": "number",
                         "description": "Latitude to search for",
@@ -102,6 +252,170 @@
                 }
             }
         },
+        "/api/weather/gdd/csv": {
+            "get": {
+                "description": "Get gdd data csv",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "text/csv"
+                ],
+                "tags": [
+                    "Gdd"
+                ],
+                "summary": "Get gdd data csv",
+                "parameters": [
+                    {
+                        "type": "boolean",
+                        "description": "Add analog data to csv",
+                        "name": "analog",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add cfs data to csv",
+                        "name": "cfs",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add cfs upper bound data to csv",
+                        "name": "cfs_upper",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add cfs lower bound data to csv",
+                        "name": "cfs_lower",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add comparison year data to csv",
+                        "name": "comparison",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add first freezing date data to csv",
+                        "name": "first_freezing",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add gefs data to csv",
+                        "name": "gefs",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add last freezing date data to csv",
+                        "name": "last_freezing",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add maximum boundary of confidence interval data to csv",
+                        "name": "maximum",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add minimum boundary of confidence interval data to csv",
+                        "name": "minimum",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add thirty year normal data to csv",
+                        "name": "normals",
+                        "in": "query"
+                    },
+                    {
+                        "type": "boolean",
+                        "description": "Add primary GDD data to csv",
+                        "name": "primary",
+                        "in": "query"
+                    },
+                    {
+                        "type": "integer",
+                        "description": "Comparison year to use",
+                        "name": "comparison_year",
+                        "in": "query"
+                    },
+                    {
+                        "enum": [
+                            "corn",
+                            "soybean",
+                            "sunflower",
+                            "tomato",
+                            "sugar_beet",
+                            "peanut",
+                            "cotton",
+                            "potato",
+                            "wheat",
+                            "pea",
+                            "oat",
+                            "spring_wheat",
+                            "rice",
+                            "sorghum"
+                        ],
+                        "type": "string",
+                        "description": "Crop to calculate gdd for",
+                        "name": "product",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "number",
+                        "description": "Confidence interval percentage",
+                        "name": "range",
+                        "in": "query"
+                    },
+                    {
+                        "type": "integer",
+                        "description": "Freezing date temperature",
+                        "name": "temperature",
+                        "in": "query"
+                    },
+                    {
+                        "type": "integer",
+                        "description": "Year to get primary data for",
+                        "name": "year",
+                        "in": "query"
+                    },
+                    {
+                        "type": "number",
+                        "description": "Latitude to search for",
+                        "name": "latitude",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "number",
+                        "description": "Longitude to search for",
+                        "name": "longitude",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/errors.StandardError"
+                        }
+                    },
+                    "400": {
+                        "description": "Bad Request",
+                        "schema": {
+                            "$ref": "#/definitions/errors.StandardError"
+                        }
+                    }
+                }
+            }
+        },
         "/api/weather/gdd/daily": {
             "get": {
                 "description": "get gdd values",
@@ -375,22 +689,57 @@
                 }
             }
         },
+        "models.CfsGddResponse": {
+            "type": "object",
+            "properties": {
+                "closest_latitude": {
+                    "type": "number"
+                },
+                "closest_longitude": {
+                    "type": "number"
+                },
+                "first_date": {
+                    "type": "string"
+                },
+                "gdd_values": {
+                    "type": "array",
+                    "items": {
+                        "type": "number"
+                    }
+                },
+                "lower_values": {
+                    "type": "array",
+                    "items": {
+                        "type": "number"
+                    }
+                },
+                "product": {
+                    "type": "string"
+                },
+                "upper_values": {
+                    "type": "array",
+                    "items": {
+                        "type": "number"
+                    }
+                }
+            }
+        },
         "models.ConfidenceIntervalResposne": {
             "type": "object",
             "properties": {
-                "closestLatitude": {
+                "closest_latitude": {
                     "type": "number"
                 },
-                "closestLongitude": {
+                "closest_longitude": {
                     "type": "number"
                 },
-                "lowerBound": {
+                "lower_bound": {
                     "type": "array",
                     "items": {
                         "type": "number"
                     }
                 },
-                "upperBound": {
+                "upper_bound": {
                     "type": "array",
                     "items": {
                         "type": "number"
@@ -398,6 +747,29 @@
                 }
             }
         },
+        "models.FreezingDateResponse": {
+            "type": "object",
+            "properties": {
+                "closest_latitude": {
+                    "type": "number"
+                },
+                "closest_longitude": {
+                    "type": "number"
+                },
+                "first_date_counts": {
+                    "type": "object",
+                    "additionalProperties": {
+                        "type": "integer"
+                    }
+                },
+                "last_date_counts": {
+                    "type": "object",
+                    "additionalProperties": {
+                        "type": "integer"
+                    }
+                }
+            }
+        },
         "models.GddResponse": {
             "type": "object",
             "properties": {
@@ -442,8 +814,20 @@
                 "last_date": {
                     "type": "string"
                 },
+                "lower_values": {
+                    "type": "array",
+                    "items": {
+                        "type": "number"
+                    }
+                },
                 "product": {
                     "type": "string"
+                },
+                "upper_values": {
+                    "type": "array",
+                    "items": {
+                        "type": "number"
+                    }
                 }
             }
         }
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
index 1c95e8169f7b2cd7079439323e7276bbea54e9d7..f1fff78dcb5ef60d5ecfe21b1f3f4b90e91e173d 100644
--- a/docs/swagger.yaml
+++ b/docs/swagger.yaml
@@ -25,21 +25,59 @@ definitions:
       closest_longitude:
         type: number
     type: object
+  models.CfsGddResponse:
+    properties:
+      closest_latitude:
+        type: number
+      closest_longitude:
+        type: number
+      first_date:
+        type: string
+      gdd_values:
+        items:
+          type: number
+        type: array
+      lower_values:
+        items:
+          type: number
+        type: array
+      product:
+        type: string
+      upper_values:
+        items:
+          type: number
+        type: array
+    type: object
   models.ConfidenceIntervalResposne:
     properties:
-      closestLatitude:
+      closest_latitude:
         type: number
-      closestLongitude:
+      closest_longitude:
         type: number
-      lowerBound:
+      lower_bound:
         items:
           type: number
         type: array
-      upperBound:
+      upper_bound:
         items:
           type: number
         type: array
     type: object
+  models.FreezingDateResponse:
+    properties:
+      closest_latitude:
+        type: number
+      closest_longitude:
+        type: number
+      first_date_counts:
+        additionalProperties:
+          type: integer
+        type: object
+      last_date_counts:
+        additionalProperties:
+          type: integer
+        type: object
+    type: object
   models.GddResponse:
     properties:
       closest_latitude:
@@ -69,8 +107,16 @@ definitions:
         type: array
       last_date:
         type: string
+      lower_values:
+        items:
+          type: number
+        type: array
       product:
         type: string
+      upper_values:
+        items:
+          type: number
+        type: array
     type: object
 host: localhost:8080
 info:
@@ -81,6 +127,41 @@ info:
   title: Dawn Weather Server
   version: "1.0"
 paths:
+  /api/weather/freezing-dates:
+    get:
+      consumes:
+      - application/json
+      description: get freezing dates
+      parameters:
+      - description: Latitude to search for
+        in: query
+        name: latitude
+        required: true
+        type: number
+      - description: Longitude to search for
+        in: query
+        name: longitude
+        required: true
+        type: number
+      - description: Freezing temperature to use
+        in: query
+        name: freezing_temp
+        required: true
+        type: number
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/models.FreezingDateResponse'
+        "400":
+          description: Bad Request
+          schema:
+            $ref: '#/definitions/errors.StandardError'
+      summary: get freezing dates
+      tags:
+      - Freezing Dates
   /api/weather/gdd/analog:
     get:
       consumes:
@@ -111,12 +192,87 @@ paths:
       summary: Get analog year
       tags:
       - Gdd
+  /api/weather/gdd/cfs:
+    get:
+      consumes:
+      - application/json
+      description: Get GDD values calculated from CFS
+      parameters:
+      - description: Crop to calculate gdd for
+        enum:
+        - corn
+        - soybean
+        - sunflower
+        - tomato
+        - sugar_beet
+        - peanut
+        - cotton
+        - potato
+        - wheat
+        - pea
+        - oat
+        - spring_wheat
+        - rice
+        - sorghum
+        in: query
+        name: product
+        required: true
+        type: string
+      - description: Latitude to search for
+        in: query
+        name: latitude
+        required: true
+        type: number
+      - description: Longitude to search for
+        in: query
+        name: longitude
+        required: true
+        type: number
+      - description: Accumulate gdd values
+        in: query
+        name: accumulate
+        required: true
+        type: boolean
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/models.CfsGddResponse'
+        "400":
+          description: Bad Request
+          schema:
+            $ref: '#/definitions/errors.StandardError'
+      summary: Get GDD values calculated from CFS
+      tags:
+      - Gdd
   /api/weather/gdd/confidence:
     get:
       consumes:
       - application/json
       description: Get confidence interval
       parameters:
+      - description: Crop to calculate gdd for
+        enum:
+        - corn
+        - soybean
+        - sunflower
+        - tomato
+        - sugar_beet
+        - peanut
+        - cotton
+        - potato
+        - wheat
+        - pea
+        - oat
+        - spring_wheat
+        - rice
+        - sorghum
+        in: query
+        name: product
+        required: true
+        type: string
       - description: Latitude to search for
         in: query
         name: latitude
@@ -141,6 +297,120 @@ paths:
       summary: Get confidence interval
       tags:
       - Gdd
+  /api/weather/gdd/csv:
+    get:
+      consumes:
+      - application/json
+      description: Get gdd data csv
+      parameters:
+      - description: Add analog data to csv
+        in: query
+        name: analog
+        type: boolean
+      - description: Add cfs data to csv
+        in: query
+        name: cfs
+        type: boolean
+      - description: Add cfs upper bound data to csv
+        in: query
+        name: cfs_upper
+        type: boolean
+      - description: Add cfs lower bound data to csv
+        in: query
+        name: cfs_lower
+        type: boolean
+      - description: Add comparison year data to csv
+        in: query
+        name: comparison
+        type: boolean
+      - description: Add first freezing date data to csv
+        in: query
+        name: first_freezing
+        type: boolean
+      - description: Add gefs data to csv
+        in: query
+        name: gefs
+        type: boolean
+      - description: Add last freezing date data to csv
+        in: query
+        name: last_freezing
+        type: boolean
+      - description: Add maximum boundary of confidence interval data to csv
+        in: query
+        name: maximum
+        type: boolean
+      - description: Add minimum boundary of confidence interval data to csv
+        in: query
+        name: minimum
+        type: boolean
+      - description: Add thirty year normal data to csv
+        in: query
+        name: normals
+        type: boolean
+      - description: Add primary GDD data to csv
+        in: query
+        name: primary
+        type: boolean
+      - description: Comparison year to use
+        in: query
+        name: comparison_year
+        type: integer
+      - description: Crop to calculate gdd for
+        enum:
+        - corn
+        - soybean
+        - sunflower
+        - tomato
+        - sugar_beet
+        - peanut
+        - cotton
+        - potato
+        - wheat
+        - pea
+        - oat
+        - spring_wheat
+        - rice
+        - sorghum
+        in: query
+        name: product
+        required: true
+        type: string
+      - description: Confidence interval percentage
+        in: query
+        name: range
+        type: number
+      - description: Freezing date temperature
+        in: query
+        name: temperature
+        type: integer
+      - description: Year to get primary data for
+        in: query
+        name: year
+        type: integer
+      - description: Latitude to search for
+        in: query
+        name: latitude
+        required: true
+        type: number
+      - description: Longitude to search for
+        in: query
+        name: longitude
+        required: true
+        type: number
+      produces:
+      - text/csv
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/errors.StandardError'
+        "400":
+          description: Bad Request
+          schema:
+            $ref: '#/definitions/errors.StandardError'
+      summary: Get gdd data csv
+      tags:
+      - Gdd
   /api/weather/gdd/daily:
     get:
       consumes:
diff --git a/go.mod b/go.mod
index 01b2d5776a497a53a1166acad63779ee6ad34ce1..17e4177e0d7784332c510f8bc893cfb3b5ed8406 100644
--- a/go.mod
+++ b/go.mod
@@ -18,6 +18,7 @@ require (
 	github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect
 	github.com/gofiber/adaptor/v2 v2.1.9 // indirect
 	github.com/gofiber/fiber/v2 v2.16.0
+	github.com/google/uuid v1.3.0 // indirect
 	github.com/klauspost/compress v1.13.3 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/prometheus/client_golang v1.11.0 // indirect
diff --git a/go.sum b/go.sum
index 7ed8de744baaea6801be0362a8e621208185ea1f..67138a70881b7d86405a4213021b4207ab494e17 100644
--- a/go.sum
+++ b/go.sum
@@ -271,6 +271,8 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
diff --git a/models/cfs.go b/models/cfs.go
index fdfd0711366282aa240f77120fa8cc40247d3832..36ddbd8d5bb211fd6e307aa226dff606b861a147 100644
--- a/models/cfs.go
+++ b/models/cfs.go
@@ -7,5 +7,7 @@ type CfsGddResponse struct {
 	ClosestLatitude  float64   `json:"closest_latitude"`
 	ClosestLongitude float64   `json:"closest_longitude"`
 	GddValues        []float64 `json:"gdd_values"`
+	UpperBound       []float64 `json:"upper_values"`
+	LowerBound       []float64 `json:"lower_values"`
 	FirstDate        time.Time `json:"first_date"`
 }
diff --git a/models/csv.go b/models/csv.go
new file mode 100644
index 0000000000000000000000000000000000000000..cb3dd7bdb19dbbf41430ca3150429b95bb2c2b15
--- /dev/null
+++ b/models/csv.go
@@ -0,0 +1,116 @@
+package models
+
+import (
+	"dawn-weather/errors"
+	"strconv"
+	"time"
+
+	validation "github.com/go-ozzo/ozzo-validation"
+	"github.com/gofiber/fiber/v2"
+)
+
+// type GddResponse struct {
+// 	Product          string    `json:"product"`
+// 	ClosestLatitude  float64   `json:"closest_latitude"`
+// 	ClosestLongitude float64   `json:"closest_longitude"`
+// 	GddValues        []float64 `json:"gdd_values"`
+// 	LastDate         time.Time `json:"last_date"`
+// }
+
+type CSVRequest struct {
+	Analog         bool    `json:"analog"`
+	Cfs            bool    `json:"cfs"`
+	CfsLower       bool    `json:"cfs_lower"`
+	CfsUpper       bool    `json:"cfs_upper"`
+	Comparison     bool    `json:"comparison"`
+	FirstFreezing  bool    `json:"first_freezing"`
+	Gefs           bool    `json:"gefs"`
+	LastFreezing   bool    `json:"last_freezing"`
+	Maximum        bool    `json:"maximum"`
+	Minimum        bool    `json:"minimum"`
+	Normals        bool    `json:"normals"`
+	Primary        bool    `json:"primary"`
+	ComparisonYear int     `json:"comparison_year"`
+	Product        string  `json:"product"`
+	Range          int     `json:"range"`
+	Temperature    int     `json:"temperature"`
+	Year           int     `json:"year"`
+	Latitude       float64 `json:"latitude"`
+	Longitude      float64 `json:"longitude"`
+}
+
+func (r CSVRequest) Validate() error {
+	return validation.ValidateStruct(&r,
+		validation.Field(&r.Year, validation.Required, validation.Min(1981), validation.Max(time.Now().Year())),
+		validation.Field(&r.Product, validation.Required, validation.Length(1, 100)),
+		validation.Field(&r.Latitude, validation.Required, validation.Min(-90.0), validation.Max(90.0)),
+		validation.Field(&r.Longitude, validation.Required, validation.Min(-180.0), validation.Max(180.0)),
+	)
+}
+
+// func (r GddRequest) ValidateNoYear() error {
+// 	return validation.ValidateStruct(&r,
+// 		validation.Field(&r.Product, validation.Required, validation.Length(1, 100)),
+// 		validation.Field(&r.Latitude, validation.Required, validation.Min(-90.0), validation.Max(90.0)),
+// 		validation.Field(&r.Longitude, validation.Required, validation.Min(-180.0), validation.Max(180.0)),
+// 	)
+// }
+
+func parseBool(str string) bool {
+	a, e := strconv.ParseBool(str)
+	if e != nil {
+		return false
+	}
+	return a
+}
+
+func atoi(str string) int {
+	a, e := strconv.Atoi(str)
+	if e != nil {
+		return 0
+	}
+	return a
+}
+
+func (r CSVRequest) Build(c *fiber.Ctx) CSVRequest {
+
+	latitude, _ := strconv.ParseFloat(c.Query("latitude", "-1000.0"), 64)
+	longitude, _ := strconv.ParseFloat(c.Query("longitude", "-1000.0"), 64)
+
+	newRequest := CSVRequest{
+		Analog:        parseBool(c.Query("analog")),
+		Cfs:           parseBool(c.Query("cfs")),
+		CfsLower:      parseBool(c.Query("cfs_lower")),
+		CfsUpper:      parseBool(c.Query("cfs_upper")),
+		Comparison:    parseBool(c.Query("comparison")),
+		FirstFreezing: parseBool(c.Query("first_freezing")),
+		Gefs:          parseBool(c.Query("gefs")),
+		LastFreezing:  parseBool(c.Query("last_freezing")),
+		Maximum:       parseBool(c.Query("maximum")),
+		Minimum:       parseBool(c.Query("minimum")),
+		Normals:       parseBool(c.Query("normals")),
+		Primary:       parseBool(c.Query("primary")),
+
+		ComparisonYear: atoi(c.Query("comparison_year", "0")),
+		Product:        c.Query("product", "NONE"),
+		Range:          atoi(c.Query("range", "0")),
+		Temperature:    atoi(c.Query("temperature", "0")),
+		Year:           atoi(c.Query("year", "0")),
+		Latitude:       latitude,
+		Longitude:      longitude,
+	}
+
+	if newRequest.Validate() != nil {
+		panic(errors.BAD_REQUEST)
+	}
+
+	return newRequest
+}
+
+// func (r GddRequest) BuildLocation() entities.Location {
+// 	l := entities.Location{
+// 		Type:        "Point",
+// 		Coordinates: []float64{r.Longitude, r.Latitude},
+// 	}
+// 	return l
+// }
diff --git a/models/freezing_dates.go b/models/freezing_dates.go
new file mode 100644
index 0000000000000000000000000000000000000000..5d67b8443c3923cf51bda3584f08eaff831bb2c0
--- /dev/null
+++ b/models/freezing_dates.go
@@ -0,0 +1,63 @@
+package models
+
+import (
+	"dawn-weather/errors"
+	"dawn-weather/persistence/entities"
+	"strconv"
+	"time"
+
+	validation "github.com/go-ozzo/ozzo-validation"
+	"github.com/gofiber/fiber/v2"
+)
+
+type DateCount struct {
+	Date  time.Time `json:"date"`
+	Count int       `json:"count"`
+}
+
+type FreezingDateResponse struct {
+	ClosestLatitude  float64        `json:"closest_latitude"`
+	ClosestLongitude float64        `json:"closest_longitude"`
+	FirstDateCounts  map[string]int `json:"first_date_counts"`
+	LastDateCounts   map[string]int `json:"last_date_counts"`
+}
+
+type FreezingDateRequest struct {
+	Latitude     float64 `json:"latitude"`
+	Longitude    float64 `json:"longitude"`
+	FreezingTemp int     `json:"freezing_temp"`
+}
+
+func (r FreezingDateRequest) Validate() error {
+	return validation.ValidateStruct(&r,
+		validation.Field(&r.Latitude, validation.Required, validation.Min(-90.0), validation.Max(90.0)),
+		validation.Field(&r.Longitude, validation.Required, validation.Min(-180.0), validation.Max(180.0)),
+		validation.Field(&r.FreezingTemp, validation.Required, validation.Min(25), validation.Max(32)),
+	)
+}
+
+func (r FreezingDateRequest) Build(c *fiber.Ctx) FreezingDateRequest {
+	latitude, _ := strconv.ParseFloat(c.Query("latitude", "-10000"), 64)
+	longitude, _ := strconv.ParseFloat(c.Query("longitude", "-10000"), 64)
+	freezingTemp, _ := strconv.Atoi(c.Query("freezing_temp", "0"))
+
+	rNew := FreezingDateRequest{
+		Latitude:     latitude,
+		Longitude:    longitude,
+		FreezingTemp: freezingTemp,
+	}
+
+	if rNew.Validate() != nil {
+		panic(errors.BAD_REQUEST)
+	}
+
+	return rNew
+}
+
+func (r FreezingDateRequest) BuildLocation() entities.Location {
+	l := entities.Location{
+		Type:        "Point",
+		Coordinates: []float64{r.Longitude, r.Latitude},
+	}
+	return l
+}
diff --git a/models/gefs.go b/models/gefs.go
index b7cd5620fcb3fe45ecd1c909c8fed6070ceaf564..34da997be5cce39f7b48e975f0cd3f64c1cad451 100644
--- a/models/gefs.go
+++ b/models/gefs.go
@@ -7,6 +7,8 @@ type GefsGddResponse struct {
 	ClosestLatitude  float64   `json:"closest_latitude"`
 	ClosestLongitude float64   `json:"closest_longitude"`
 	GddValues        []float64 `json:"gdd_values"`
+	UpperBound       []float64 `json:"upper_values"`
+	LowerBound       []float64 `json:"lower_values"`
 	LastDate         time.Time `json:"last_date"`
 	FirstDate        time.Time `json:"first_date"`
 }
diff --git a/persistence/entities/freezing_dates.go b/persistence/entities/freezing_dates.go
new file mode 100644
index 0000000000000000000000000000000000000000..ab115cf56c168169b0550449d4f21434ecaadc51
--- /dev/null
+++ b/persistence/entities/freezing_dates.go
@@ -0,0 +1,9 @@
+package entities
+
+type FreezingDates struct {
+	ID           string   `bson:"_id,omitempty"`
+	Location     Location `bson:"location,omitempty"`
+	FirstDates   [][]int  `bson:"first_dates,omitempty"`
+	LastDates    [][]int  `bson:"last_dates,omitempty"`
+	AllowedTemps []int    `bson:"allowed_temps,omitempty"`
+}
diff --git a/persistence/entities/gdd.go b/persistence/entities/gdd.go
index b0a1de06315811a7079c2b35ea08474048d26679..f17aff3b98f8a0e27a5b6e32a6c07f68c4396530 100644
--- a/persistence/entities/gdd.go
+++ b/persistence/entities/gdd.go
@@ -26,13 +26,17 @@ type GefsGdd struct {
 	Location Location           `bson:"location,omitempty"`
 	MinTemp  float64            `bson:"min_temp,omitempty"`
 	MaxTemp  float64            `bson:"max_temp,omitempty"`
+	VarMin   float64            `bson:"var_min,omitempty"`
+	VarMax   float64            `bson:"var_max,omitempty"`
 	Date     primitive.DateTime `bson:"date,omitempty"`
 }
 
 type CfsGdd struct {
-	ID        string             `bson:"_id,omitempty"`
-	Location  Location           `bson:"location,omitempty"`
-	MinTemps  []float64          `bson:"min_temps,omitempty"`
-	MaxTemps  []float64          `bson:"max_temps,omitempty"`
-	StartDate primitive.DateTime `bson:"start_date,omitempty"`
+	ID       string             `bson:"_id,omitempty"`
+	Location Location           `bson:"location,omitempty"`
+	MinTemps []float64          `bson:"min_temps,omitempty"`
+	MaxTemps []float64          `bson:"max_temps,omitempty"`
+	VarMin   []float64          `bson:"var_mins,omitempty"`
+	VarMax   []float64          `bson:"var_maxs,omitempty"`
+	Date     primitive.DateTime `bson:"date,omitempty"`
 }
diff --git a/persistence/mongodb.go b/persistence/mongodb.go
index faf0902cfecd6713ec168cf174ccc4afdda1daa4..45c5336061241cbc88c9a438ac497e60d524e1b3 100644
--- a/persistence/mongodb.go
+++ b/persistence/mongodb.go
@@ -169,3 +169,17 @@ func FindAnalogYear(location entities.Location) models.AnalogResponse {
 
 	return results
 }
+
+func FindFreezingDates(location entities.Location) entities.FreezingDates {
+	coll := Conn.Database(viper.GetString("db.database")).Collection("freezing_dates")
+
+	filter := buildLocationRequest(location, nil)
+
+	var g entities.FreezingDates
+	err := coll.FindOne(Ctx, filter).Decode(&g)
+	if err != nil {
+		panic(errors.NO_DATA_FOUND)
+	}
+
+	return g
+}
diff --git a/services/data_download_service.go b/services/data_download_service.go
new file mode 100644
index 0000000000000000000000000000000000000000..723782382c341a647011cbf850053fc4517287bf
--- /dev/null
+++ b/services/data_download_service.go
@@ -0,0 +1,181 @@
+package services
+
+import (
+	"dawn-weather/errors"
+	"dawn-weather/models"
+	"dawn-weather/persistence"
+	"dawn-weather/persistence/entities"
+	"encoding/csv"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"math"
+	"os"
+	"strconv"
+	"time"
+
+	"github.com/google/uuid"
+)
+
+func fillKeys(request models.CSVRequest) []string {
+	keys := []string{}
+
+	if request.Analog {
+		keys = append(keys, "Date")
+	}
+	if request.Analog {
+		keys = append(keys, "Analog Year GDD")
+	}
+	// if request.Cfs {
+	// 	keys = append(keys, "CFS GDD")
+	// }
+	// if request.CfsLower {
+	// 	keys = append(keys, "CFS Lower Boundary GDD")
+	// }
+	// if request.CfsUpper {
+	// 	keys = append(keys, "CFS Upper Boundary GDD")
+	// }
+	// if request.Comparison {
+	// 	keys = append(keys, "Comparison Year GDD")
+	// }
+	// if request.FirstFreezing {
+	// 	keys = append(keys, "First Freezing Date Counts")
+	// }
+	// if request.Gefs {
+	// 	keys = append(keys, "GEFS GDD")
+	// }
+	// if request.LastFreezing {
+	// 	keys = append(keys, "Last Freezing Date Counts")
+	// }
+	// if request.Maximum {
+	// 	keys = append(keys, "Confidence Interval Upper Boundary")
+	// }
+	// if request.Minimum {
+	// 	keys = append(keys, "Confidence Interval Lower Boundary")
+	// }
+	if request.Normals {
+		keys = append(keys, "Thirty Year Normal GDD")
+	}
+	if request.Primary {
+		keys = append(keys, "Primary GDD ("+strconv.Itoa(request.Year)+")")
+	}
+	return keys
+}
+
+func timeToString(timeValue time.Time) string {
+	day := timeValue.Day()
+	month := int(timeValue.Month())
+	timeString := fmt.Sprintf("%02d", month) + "-" + fmt.Sprintf("%02d", day)
+	return timeString
+}
+
+func fillDates() []time.Time {
+	dates := []time.Time{}
+	date := time.Date(1981, time.January, 1, 0, 0, 0, 0, time.UTC)
+
+	for i := 0; i < 365; i++ {
+		dates = append(dates, date)
+		date = date.Add(time.Duration(24) * time.Hour)
+	}
+
+	return dates
+}
+
+func getPrimary(request models.CSVRequest, dates []time.Time) []float64 {
+	gddRequest := models.GddRequest{
+		Year:       request.Year,
+		Product:    request.Product,
+		Latitude:   request.Latitude,
+		Longitude:  request.Longitude,
+		Accumulate: true,
+	}
+
+	gddValues := GetGddValues(gddRequest).GddValues
+	for len(gddValues) < 365 {
+		gddValues = append(gddValues, math.NaN())
+	}
+	return gddValues
+}
+
+func getNormals(request models.CSVRequest, dates []time.Time) []float64 {
+	gddRequest := models.GddRequest{
+		Year:       request.Year,
+		Product:    request.Product,
+		Latitude:   request.Latitude,
+		Longitude:  request.Longitude,
+		Accumulate: true,
+	}
+
+	gddValues := GetNormalValues(gddRequest).GddValues
+	for len(gddValues) < 365 {
+		gddValues = append(gddValues, math.NaN())
+	}
+	return gddValues
+}
+
+func getAnalogYear(request models.CSVRequest, dates []time.Time) []float64 {
+
+	location := entities.Location{
+		Type:        "Point",
+		Coordinates: []float64{request.Longitude, request.Latitude},
+	}
+
+	analogYear := persistence.FindAnalogYear(location).AnalogYear
+
+	gddRequest := models.GddRequest{
+		Year:       analogYear,
+		Product:    request.Product,
+		Latitude:   request.Latitude,
+		Longitude:  request.Longitude,
+		Accumulate: true,
+	}
+
+	gddValues := GetNormalValues(gddRequest).GddValues
+	for len(gddValues) < 365 {
+		gddValues = append(gddValues, math.NaN())
+	}
+	return gddValues
+}
+
+func GetDataDownload(request models.CSVRequest) string {
+
+	fileId := uuid.New()
+
+	f, err := os.Create(fileId.String() + ".csv")
+
+	if err != nil {
+		panic(errors.BAD_REQUEST)
+	}
+
+	w := csv.NewWriter(f)
+
+	keys := fillKeys(request)
+	dates := fillDates()
+	primary := getPrimary(request, dates)
+	normals := getNormals(request, dates)
+	analog := getAnalogYear(request, dates)
+
+	records := [][]string{keys}
+
+	for i := 1; i < 366; i++ {
+		records = append(records,
+			[]string{timeToString(dates[i-1]),
+				fmt.Sprintf("%f", analog[i-1]),
+				fmt.Sprintf("%f", primary[i-1]),
+				fmt.Sprintf("%f", normals[i-1]),
+			})
+	}
+
+	for _, record := range records {
+		if err := w.Write(record); err != nil {
+			log.Fatalln("error writing record to file", err)
+		}
+	}
+
+	w.Flush()
+
+	fileText, err := ioutil.ReadFile(fileId.String() + ".csv")
+	f.Close()
+	os.Remove(fileId.String() + ".csv")
+	return string(fileText)
+}
diff --git a/services/freezing_date_service.go b/services/freezing_date_service.go
new file mode 100644
index 0000000000000000000000000000000000000000..72add6366de082a08e676b44664f5ca655f099d0
--- /dev/null
+++ b/services/freezing_date_service.go
@@ -0,0 +1,87 @@
+package services
+
+import (
+	"dawn-weather/errors"
+	"dawn-weather/models"
+	"dawn-weather/persistence"
+	"dawn-weather/persistence/entities"
+	"fmt"
+	"time"
+)
+
+var may31st = 150
+var sept1st = 243
+var nov30th = 330
+
+func isLeapYear(year int) bool {
+	if (year % 4) == 0 {
+		if (year % 100) == 0 {
+			if (year % 400) == 0 {
+				return true
+			} else {
+				return false
+			}
+		} else {
+			return true
+		}
+	} else {
+		return false
+	}
+}
+
+func GetFreezingDate(request models.FreezingDateRequest) models.FreezingDateResponse {
+
+	// product := enums.GetProductFromString(request.Product)
+	var freezingDates entities.FreezingDates
+	freezingDates = persistence.FindFreezingDates(request.BuildLocation())
+
+	firstDate := time.Date(1981, time.January, 1, 0, 0, 0, 0, time.UTC)
+
+	tempIdx := -1
+
+	for i := 0; i < len(freezingDates.AllowedTemps); i++ {
+		if freezingDates.AllowedTemps[i] == request.FreezingTemp {
+			tempIdx = i
+		}
+	}
+
+	if tempIdx == -1 {
+		panic(errors.BAD_REQUEST)
+	}
+
+	firstDates := freezingDates.FirstDates[tempIdx]
+	lastDates := freezingDates.LastDates[tempIdx]
+
+	firstDateCounts := make(map[string]int)
+	for _, row := range firstDates {
+		if row == -1 {
+			continue
+		}
+		date := firstDate.Add(time.Duration(24*row) * time.Hour)
+		day := date.Day()
+		month := int(date.Month())
+		str := fmt.Sprintf("%02d", month) + "-" + fmt.Sprintf("%02d", day)
+		firstDateCounts[str]++
+	}
+
+	lastDateCounts := make(map[string]int)
+	for _, row := range lastDates {
+		if row == -1 {
+			continue
+		}
+		date := firstDate.Add(time.Duration(24*row) * time.Hour)
+		day := date.Day()
+		month := int(date.Month())
+		str := fmt.Sprintf("%02d", month) + "-" + fmt.Sprintf("%02d", day)
+		lastDateCounts[str]++
+	}
+
+	response := models.FreezingDateResponse{
+		FirstDateCounts:  firstDateCounts,
+		LastDateCounts:   lastDateCounts,
+		ClosestLatitude:  freezingDates.Location.Coordinates[1],
+		ClosestLongitude: freezingDates.Location.Coordinates[0],
+	}
+
+	return response
+}
diff --git a/services/nomads_service.go b/services/nomads_service.go
index b480821b0ab889dcb4a1879fb3e9a9ae62464c1e..f7678277339c15c4f53432c732d76c421cca4a19 100644
--- a/services/nomads_service.go
+++ b/services/nomads_service.go
@@ -5,6 +5,7 @@ import (
 	"dawn-weather/models/enums"
 	"dawn-weather/persistence"
 	"dawn-weather/utils"
+	"math"
 )
 
 func GetGefsGddValues(request models.GddRequest) models.GefsGddResponse {
@@ -14,14 +15,31 @@ func GetGefsGddValues(request models.GddRequest) models.GefsGddResponse {
 	var returnGdds models.GefsGddResponse
 
 	var gdds []float64
+	var lowerBound []float64
+	var upperBound []float64
 
 	for i := 0; i < 10; i++ {
 		temp := g[i]
+		variance := (temp.VarMin + temp.VarMax)
+		variance *= math.Pow(0.5, 2)
+		std := math.Pow(variance, 0.5)
+
 		value := utils.CalculateSingleGdd(temp.MinTemp, temp.MaxTemp, product)
+
+		lowerBoundValue := (value - (std / (math.Pow(3, 0.5)) * 1.960))
+		upperBoundValue := (value + (std / (math.Pow(3, 0.5)) * 1.960))
+
+		lowerBoundValue = utils.ClipMinFloat(lowerBoundValue, 0.0)
+		upperBoundValue = utils.ClipMinFloat(upperBoundValue, 0.0)
+
 		if request.Accumulate && i > 0 {
 			value += gdds[len(gdds)-1]
+			lowerBoundValue += lowerBound[len(gdds)-1]
+			upperBoundValue += upperBound[len(gdds)-1]
 		}
 		gdds = append(gdds, value)
+		lowerBound = append(lowerBound, lowerBoundValue)
+		upperBound = append(upperBound, upperBoundValue)
 	}
 
 	returnGdds = models.GefsGddResponse{
@@ -29,6 +47,8 @@ func GetGefsGddValues(request models.GddRequest) models.GefsGddResponse {
 		ClosestLatitude:  location.Coordinates[1],
 		ClosestLongitude: location.Coordinates[0],
 		GddValues:        gdds,
+		UpperBound:       upperBound,
+		LowerBound:       lowerBound,
 		FirstDate:        g[0].Date.Time().UTC(),
 		LastDate:         g[9].Date.Time().UTC(),
 	}
@@ -42,13 +62,31 @@ func GetCfsGddValues(request models.GddRequest) models.CfsGddResponse {
 	var returnGdds models.CfsGddResponse
 
 	var gdds []float64
+	var lowerBound []float64
+	var upperBound []float64
 
 	for i := 0; i < len(g.MaxTemps); i++ {
+
+		variance := (g.VarMin[i] + g.VarMax[i])
+		variance *= math.Pow(0.5, 2)
+		std := math.Pow(variance, 0.5)
+
 		value := utils.CalculateSingleGdd(g.MinTemps[i], g.MaxTemps[i], product)
+
+		lowerBoundValue := (value - (std / (math.Pow(4, 0.5)) * 1.960))
+		upperBoundValue := (value + (std / (math.Pow(4, 0.5)) * 1.960))
+
+		lowerBoundValue = utils.ClipMinFloat(lowerBoundValue, 0.0)
+		upperBoundValue = utils.ClipMinFloat(upperBoundValue, 0.0)
+
 		if request.Accumulate && i > 0 {
 			value += gdds[len(gdds)-1]
+			lowerBoundValue += lowerBound[len(gdds)-1]
+			upperBoundValue += upperBound[len(gdds)-1]
 		}
 		gdds = append(gdds, value)
+		lowerBound = append(lowerBound, lowerBoundValue)
+		upperBound = append(upperBound, upperBoundValue)
 	}
 
 	returnGdds = models.CfsGddResponse{
@@ -56,7 +94,9 @@ func GetCfsGddValues(request models.GddRequest) models.CfsGddResponse {
 		ClosestLatitude:  location.Coordinates[1],
 		ClosestLongitude: location.Coordinates[0],
 		GddValues:        gdds,
-		FirstDate:        g.StartDate.Time().UTC(),
+		LowerBound:       lowerBound,
+		UpperBound:       upperBound,
+		FirstDate:        g.Date.Time().UTC(),
 	}
 	return returnGdds
 }