diff --git a/geomagio/WebService.py b/geomagio/WebService.py
index 5d880eb83036a0d03d25a520c5b672030b27fb0c..9a4af1edfe9539a28cc17335d98178f52fd02834 100644
--- a/geomagio/WebService.py
+++ b/geomagio/WebService.py
@@ -3,11 +3,14 @@
 
 from __future__ import print_function
 from cgi import escape, parse_qs
+from collections import OrderedDict
 from datetime import datetime
+from json import dumps
 import sys
 
 from geomagio.edge import EdgeFactory
 from geomagio.iaga2002 import IAGA2002Writer
+from geomagio.imfjson import IMFJSONWriter
 from geomagio.ObservatoryMetadata import ObservatoryMetadata
 from obspy.core import UTCDateTime
 
@@ -31,7 +34,7 @@ VALID_DATA_TYPES = [
         'quasi-definitive',
         'definitive'
 ]
-VALID_OUTPUT_FORMATS = ['iaga2002']
+VALID_OUTPUT_FORMATS = ['iaga2002', 'json']
 VALID_SAMPLING_PERIODS = ['1', '60']
 
 
@@ -83,9 +86,15 @@ class WebService(object):
             # parse params
             query = self.parse(parse_qs(environ['QUERY_STRING']))
             query._verify_parameters()
+            self.output_format = query.output_format
         except Exception:
             exception = sys.exc_info()[1]
             message = exception.args[0]
+            ftype = parse_qs(environ['QUERY_STRING']).get('format', [''])[0]
+            if ftype == 'json':
+                self.output_format = 'json'
+            else:
+                self.output_format = 'iaga2002'
             error_body = self.error(400, message, environ, start_response)
             return [error_body]
         try:
@@ -93,7 +102,7 @@ class WebService(object):
             timeseries = self.fetch(query)
             # format timeseries
             timeseries_string = self.format_data(
-                    query, timeseries, start_response)
+                    query, timeseries, start_response, environ)
             if isinstance(timeseries_string, str):
                 timeseries_string = timeseries_string.encode('utf8')
         except Exception:
@@ -105,7 +114,6 @@ class WebService(object):
 
     def error(self, code, message, environ, start_response):
         """Assign error_body value based on error format."""
-        # TODO: Add option for json formatted error
         error_body = self.http_error(code, message, environ)
         status = str(code) + ' ' + ERROR_CODE_MESSAGES[code]
         start_response(status,
@@ -142,7 +150,7 @@ class WebService(object):
                 interval=sampling_period)
         return timeseries
 
-    def format_data(self, query, timeseries, start_response):
+    def format_data(self, query, timeseries, start_response, environ):
         """Format requested timeseries.
 
         Parameters
@@ -156,8 +164,14 @@ class WebService(object):
         unicode
           IAGA2002 formatted string.
         """
-        # TODO: Add option for json format
-        timeseries_string = IAGA2002Writer.format(timeseries, query.elements)
+        url = environ['HTTP_HOST'] + environ['PATH_INFO'] + \
+                environ['QUERY_STRING']
+        if query.output_format == 'json':
+            timeseries_string = IMFJSONWriter.format(timeseries,
+                    query.elements, url)
+        else:
+            timeseries_string = IAGA2002Writer.format(timeseries,
+                    query.elements)
         start_response('200 OK',
                 [
                     ("Content-Type", "text/plain")
@@ -169,22 +183,65 @@ class WebService(object):
 
         Returns
         -------
-        error_body : str
+        http_error_body : str
             body of http error message.
         """
+        query_string = environ['QUERY_STRING']
+        path_info = environ['PATH_INFO']
+        host = environ['HTTP_HOST']
+        if self.output_format == 'json':
+            http_error_body = self.json_error(code, message, path_info,
+                    query_string, host)
+        else:
+            http_error_body = self.iaga2002_error(code, message, path_info,
+                    query_string)
+        return http_error_body
+
+    def iaga2002_error(self, code, message, path_info, query_string):
+        """Format iaga2002 error message.
+
+        Returns
+        -------
+        error_body : str
+            body of iaga2002 error message.
+        """
         status_message = ERROR_CODE_MESSAGES[code]
-        http_error_body = 'Error ' + str(code) + ': ' + status_message + \
+        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['PATH_INFO'] + '?' + environ['QUERY_STRING'] + \
-                '\n\n' + 'Request Submitted:\n' + \
+                path_info + '?' + query_string + '\n\n' + \
+                'Request Submitted:\n' + \
                 datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") + '\n\n'
         # Check if there is version information available
         if self.version is not None:
-            http_error_body += 'Service version:\n' + str(self.version)
-        return http_error_body
+            error_body += 'Service version:\n' + str(self.version)
+        return error_body
+
+    def json_error(self, code, message, path_info, query_string, host):
+        """Format json error message.
+
+        Returns
+        -------
+        error_body : str
+            body of json error message.
+        """
+        error_dict = OrderedDict()
+        error_dict['type'] = "Error"
+        error_dict['metadata'] = OrderedDict()
+        error_dict['metadata']['status'] = 400
+        date = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ")
+        error_dict['metadata']['generated'] = date
+        error_dict['metadata']['url'] = host + path_info + '?' + query_string
+        status_message = ERROR_CODE_MESSAGES[code]
+        error_dict['metadata']['title'] = status_message
+        error_dict['metadata']['api'] = str(self.version)
+        error_dict['metadata']['error'] = message
+        error_body = dumps(error_dict,
+                ensure_ascii=True).encode('utf8')
+        error_body = str(error_body)
+        return error_body
 
     def parse(self, params):
         """Parse query string parameters and set defaults.
@@ -234,7 +291,7 @@ class WebService(object):
         else:
             try:
                 starttime = UTCDateTime(starttime)
-            except TypeError:
+            except Exception:
                 raise WebServiceException(
                         'Bad starttime value "%s".'
                         ' Valid values are ISO-8601 timestamps.' % starttime)
@@ -243,7 +300,7 @@ class WebService(object):
         else:
             try:
                 endtime = UTCDateTime(endtime)
-            except TypeError:
+            except Exception:
                 raise WebServiceException(
                         'Bad endtime value "%s".'
                         ' Valid values are ISO-8601 timestamps.' % endtime)
diff --git a/geomagio/imfjson/IMFJSONFactory.py b/geomagio/imfjson/IMFJSONFactory.py
index 13bfeba5c13342cbd020e06571714c0c747469d3..c50dea1c2dfe8132c77b91ecf54fe762fd9562f8 100644
--- a/geomagio/imfjson/IMFJSONFactory.py
+++ b/geomagio/imfjson/IMFJSONFactory.py
@@ -24,7 +24,7 @@ class IMFJSONFactory(TimeseriesFactory):
     def __init__(self, **kwargs):
         TimeseriesFactory.__init__(self, **kwargs)
 
-    def write_file(self, fh, timeseries, channels):
+    def write_file(self, fh, timeseries, channels, url=None):
         """writes timeseries data to the given file object.
 
         Parameters
@@ -33,6 +33,9 @@ class IMFJSONFactory(TimeseriesFactory):
         timeseries : obspy.core.Stream
             stream containing traces to store.
         channels : array_like
-            list of channels to store
+            list of channels to store.
+        url : str
+            string representing the requested url
+            uses default if unspecified.
         """
-        IMFJSONWriter().write(fh, timeseries, channels)
+        IMFJSONWriter().write(fh, timeseries, channels, url)
diff --git a/geomagio/imfjson/IMFJSONWriter.py b/geomagio/imfjson/IMFJSONWriter.py
index 6c01e41c23a4b871e2acf475cf801709c0f86246..e8323c43f81492e3253836217571c90f95107228 100644
--- a/geomagio/imfjson/IMFJSONWriter.py
+++ b/geomagio/imfjson/IMFJSONWriter.py
@@ -89,9 +89,10 @@ class IMFJSONWriter(object):
             series = np.copy(trace.data)
             if c == 'D':
                 series = ChannelConverter.get_minutes_from_radians(series)
-            series[np.isnan(series)] = None
             # Converting numpy array to list required for JSON serialization
-            value_dict['values'] = series.tolist()
+            series = series.tolist()
+            series = [None if str(x) == 'nan' else x for x in series]
+            value_dict['values'] = series
             # TODO: Add flag metadata
         return values