diff --git a/geomagio/WebService.py b/geomagio/WebService.py
index a9344e40d6415f064bc2fdb5154131b727865236..a5c531fc5b6c76302760d3e11999f6325de8e08d 100644
--- a/geomagio/WebService.py
+++ b/geomagio/WebService.py
@@ -4,6 +4,8 @@
 from __future__ import print_function
 from cgi import parse_qs, escape
 from datetime import datetime
+from json import load
+import os.path
 import sys
 
 from geomagio.edge import EdgeFactory
@@ -16,6 +18,15 @@ DEFAULT_DATA_TYPE = 'variation'
 DEFAULT_ELEMENTS = ('X', 'Y', 'Z', 'F')
 DEFAULT_OUTPUT_FORMAT = 'iaga2002'
 DEFAULT_SAMPLING_PERIOD = '60'
+ERROR_CODE_MESSAGES = {
+        204: 'No Data',
+        400: 'Bad Request',
+        404: 'Not Found',
+        409: 'Conflict',
+        500: 'Internal Server Error',
+        501: 'Not Implemented',
+        503: 'Service Unavailable'
+}
 VALID_DATA_TYPES = [
         'variation',
         'adjusted',
@@ -45,12 +56,12 @@ def _get_param(params, key, required=False):
 
     Raises
     ------
-    WebServiceError
+    WebServiceException
         if the parameter is specified more than once
         or if required paramenter is not specified.
     """
     value = params.get(key)
-    if type(value) in (list, tuple):
+    if isinstance(value, (list, tuple)):
         if len(value) > 1:
             raise WebServiceException('"' + key +
                     '" may only be specified once.')
@@ -62,75 +73,54 @@ def _get_param(params, key, required=False):
     return value
 
 
-def _verify_parameters(query):
-    """Verify that parameters are valid.
-
-    Parameters
-    ----------
-    query : WebServiceQuery
-        parsed query object.
-
-    Raises
-    ------
-    WebServiceError
-        if any parameters are not supported.
-    """
-    if len(query.elements) > 4 and query.output_format == 'iaga2002':
-        raise WebServiceException(
-                'No more than 4 elements allowed for iaga2002 format.')
-    if query.starttime > query.endtime:
-        raise WebServiceException(
-                'Starttime must be before endtime.')
-    if query.data_type not in VALID_DATA_TYPES:
-        raise WebServiceException(
-                'Bad type value "%s".'
-                ' Valid values are: %s' % (query.data_type, VALID_DATA_TYPES))
-    if query.sampling_period not in VALID_SAMPLING_PERIODS:
-        raise WebServiceException(
-                'Bad sampling_period value "%s".'
-                ' Valid values are: %s'
-                % (query.sampling_period, VALID_SAMPLING_PERIODS))
-    if query.output_format not in VALID_OUTPUT_FORMATS:
-        raise WebServiceException(
-                'Bad format value "%s".'
-                ' Valid values are: %s'
-                % (query.output_format, VALID_OUTPUT_FORMATS))
-
-
 class WebService(object):
     def __init__(self, factory, metadata=None):
         self.factory = factory
         self.metadata = metadata or ObservatoryMetadata().metadata
+        base = os.path.dirname(__file__)
+        filepath = os.path.abspath(os.path.join(base, '..', 'package.json'))
+        with open(filepath) as package:
+            specifications = load(package)
+        self.version = specifications['version']
 
     def __call__(self, environ, start_response):
         """Implement WSGI interface"""
         try:
             # parse params
             query = self.parse(parse_qs(environ['QUERY_STRING']))
-            # fetch data
-            data = self.fetch(query)
-            # format data
-            data_string = self.format_data(query, data)
-            if isinstance(data_string, str):
-                data_string = data_string.encode('utf8')
+            query._verify_parameters()
         except Exception:
             exception = sys.exc_info()[1]
             message = exception.args[0]
-            error = WebServiceError('BAD_REQUEST', message, environ)
-            start_response(error.status,
-                    [
-                        ("Content-Type", "text/plain")
-                    ])
-            return [error.error_body]
-        # send response
-        start_response('200 OK',
+            self.error(400, message, environ, start_response)
+            return [self.error_body]
+        try:
+            # fetch timeseries
+            timeseries = self.fetch(query)
+            # format timeseries
+            timeseries_string = self.format_data(
+                    query, timeseries, start_response)
+            if isinstance(timeseries_string, str):
+                timeseries_string = timeseries_string.encode('utf8')
+        except Exception:
+            exception = sys.exc_info()[1]
+            message = exception.args[0]
+            self.error(500, message, environ, start_response)
+            return [self.error_body]
+        return [timeseries_string]
+
+    def error(self, code, message, environ, start_response):
+        """Assign error_body value based on error format."""
+        # TODO: Add option for json formatted error
+        self.error_body = self.http_error(code, message, environ)
+        status = str(code) + ' ' + ERROR_CODE_MESSAGES[code]
+        start_response(status,
                 [
                     ("Content-Type", "text/plain")
                 ])
-        return [data_string]
 
     def fetch(self, query):
-        """Get requested data.
+        """Get requested timeseries.
 
         Parameters
         ----------
@@ -141,27 +131,27 @@ class WebService(object):
         obspy.core.Stream
             timeseries object with requested data.
         """
-        _verify_parameters(query)
         if query.sampling_period == '1':
-            query.sampling_period = 'second'
+            sampling_period = 'second'
         if query.sampling_period == '60':
-            query.sampling_period = 'minute'
-        data = self.factory.get_timeseries(
+            sampling_period = 'minute'
+        timeseries = self.factory.get_timeseries(
                 observatory=query.observatory_id,
                 channels=query.elements,
                 starttime=query.starttime,
                 endtime=query.endtime,
                 type=query.data_type,
-                interval=query.sampling_period)
-        return data
+                interval=sampling_period)
+        return timeseries
 
-    def format_data(self, query, data):
-        """Format requested data.
+    @classmethod
+    def format_data(cls, query, timeseries, start_response):
+        """Format requested timeseries.
 
         Parameters
         ----------
         query : dictionary of parsed query parameters
-        data : obspy.core.Stream
+        timeseries : obspy.core.Stream
             timeseries object with data to be written
 
         Returns
@@ -170,8 +160,33 @@ class WebService(object):
           IAGA2002 formatted string.
         """
         # TODO: Add option for json format
-        data_string = IAGA2002Writer.format(data, query.elements)
-        return data_string
+        timeseries_string = IAGA2002Writer.format(timeseries, query.elements)
+        start_response('200 OK',
+                [
+                    ("Content-Type", "text/plain")
+                ])
+        return timeseries_string
+
+    def http_error(self, code, message, environ):
+        """Format http error message.
+
+        Returns
+        -------
+        error_body : str
+            body of http error message.
+        """
+        status_message = ERROR_CODE_MESSAGES[code]
+        error_body = 'Error ' + str(code) + ': ' + status_message + \
+                '\n\n' + message + '\n\n' + \
+                'Usage details are available from ' + \
+                'http://geomag.usgs.gov/ws/edge/ \n\n' + \
+                'Request:\n' + \
+                environ['QUERY_STRING'] + '\n\n' + \
+                'Request Submitted:\n' + \
+                datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") + '\n\n' + \
+                'Service version:\n' + \
+                str(self.version)
+        return error_body
 
     def parse(self, params):
         """Parse query string parameters and set defaults.
@@ -188,7 +203,7 @@ class WebService(object):
 
         Raises
         ------
-        WebServiceError
+        WebServiceException
             if any parameters are not supported.
         """
         # Get values
@@ -237,7 +252,7 @@ class WebService(object):
         if not elements:
             elements = DEFAULT_ELEMENTS
         else:
-            elements = [el.strip().upper() for el in elements.split(',')]
+            elements = [e.strip().upper() for e in elements.replace(',', '')]
         if not sampling_period:
             sampling_period = DEFAULT_SAMPLING_PERIOD
         else:
@@ -292,82 +307,43 @@ class WebServiceQuery(object):
         self.data_type = data_type
         self.output_format = output_format
 
+    @classmethod
+    def _verify_parameters(cls):
+        """Verify that parameters are valid.
+
+        Raises
+        ------
+        WebServiceException
+            if any parameters are not supported.
+        """
+        if len(cls.elements) > 4 and cls.output_format == 'iaga2002':
+            raise WebServiceException(
+                    'No more than four elements allowed for iaga2002 format.')
+        if cls.starttime > cls.endtime:
+            raise WebServiceException(
+                    'Starttime must be before endtime.')
+        if cls.data_type not in VALID_DATA_TYPES:
+            raise WebServiceException(
+                    'Bad type value "%s".'
+                    ' Valid values are: %s'
+                    % (cls.data_type, VALID_DATA_TYPES))
+        if cls.sampling_period not in VALID_SAMPLING_PERIODS:
+            raise WebServiceException(
+                    'Bad sampling_period value "%s".'
+                    ' Valid values are: %s'
+                    % (cls.sampling_period, VALID_SAMPLING_PERIODS))
+        if cls.output_format not in VALID_OUTPUT_FORMATS:
+            raise WebServiceException(
+                    'Bad format value "%s".'
+                    ' Valid values are: %s'
+                    % (cls.output_format, VALID_OUTPUT_FORMATS))
+
 
 class WebServiceException(Exception):
     """Base class for exceptions thrown by web services."""
     pass
 
 
-class WebServiceError(object):
-    """Base class for creating error pages."""
-    def __init__(self, code, message, environ):
-        self.code = code
-        self.message = message
-        self.environ = environ
-        self.error_body = None
-        self.error_types = {
-                'NO_DATA': {
-                        'code': 204,
-                        'status': 'No Data'
-                },
-                'BAD_REQUEST': {
-                        'code': 400,
-                        'status': 'Bad Request'
-                },
-                'NOT_FOUND': {
-                        'code': 404,
-                        'status': 'Not Found'
-                },
-                'CONFLICT': {
-                        'code': 409,
-                        'status': 'Conflict'
-                },
-                'SERVER_ERROR': {
-                        'code': 500,
-                        'status': 'Internal Server Error'
-                },
-                'NOT_IMPLEMENTED': {
-                        'code': 501,
-                        'status': 'Not Implemented'
-                },
-                'SERVICE_UNAVAILABLE': {
-                        'code': 503,
-                        'status': 'Service Unavailable'
-                },
-        }
-        self.status = None
-        self.error()
-
-    def error(self):
-        """Assign error_body value based on error format."""
-        # TODO: Add option for json formatted error
-        self.error_body = self.http_error()
-
-    def http_error(self):
-        """Format http error message.
-
-        Returns
-        -------
-        error_body : str
-            body of http error message.
-        """
-        code_message = str(self.error_types[self.code]['code'])
-        status_message = self.error_types[self.code]['status']
-        self.status = code_message + ' ' + status_message
-        server_software = self.environ['SERVER_SOFTWARE'].split(' ')
-        error_body = 'Error ' + code_message + ': ' + status_message + \
-                '\n\n' + self.message + '\n\n' + \
-                'Usage details are available from ' + \
-                'http://geomag.usgs.gov/ws/edge/ \n\n' + \
-                'Request:\n' + \
-                self.environ['QUERY_STRING'] + '\n\n' + \
-                'Request Submitted:\n' + \
-                datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") + '\n\n' + \
-                'Service version:\n' + \
-                server_software[0]
-        return error_body
-
-
 if __name__ == '__main__':
     from wsgiref.simple_server import make_server