diff --git a/mhcflurry/common.py b/mhcflurry/common.py
index 65cbb9e83e9aa573b67d228d35412f0c5e381c80..7ff1bdccf3f3dfcd041ea8e8b3d446585edc6809 100644
--- a/mhcflurry/common.py
+++ b/mhcflurry/common.py
@@ -12,6 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from __future__ import (
+    print_function,
+    division,
+    absolute_import,
+)
+
 def parse_int_list(s):
     return [int(part.strip() for part in s.split(","))]
 
diff --git a/mhcflurry/data_helpers.py b/mhcflurry/data_helpers.py
index b9b36248c89d6370b3ec110c8a29fb66cb2da1d0..7d7d5e1f90bf480af46dafd5cffd8202f6bc5e87 100644
--- a/mhcflurry/data_helpers.py
+++ b/mhcflurry/data_helpers.py
@@ -12,7 +12,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from __future__ import (
+    print_function,
+    division,
+    absolute_import,
+)
 from collections import namedtuple
+
 import pandas as pd
 import numpy as np
 
diff --git a/mhcflurry/feedforward.py b/mhcflurry/feedforward.py
index 7e3b850d621d36ce3817a5679edf9e2058ecf9c2..f9072d8cb4d05c8b87932405de9bbe4c0812dd54 100644
--- a/mhcflurry/feedforward.py
+++ b/mhcflurry/feedforward.py
@@ -12,6 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from __future__ import (
+    print_function,
+    division,
+    absolute_import,
+)
+
 import keras
 from keras.models import Sequential
 from keras.layers.core import Dense, Activation, Flatten, Dropout
diff --git a/mhcflurry/mhc1_binding_predictor.py b/mhcflurry/mhc1_binding_predictor.py
index d3e437e5649c62d82b2802c1392cf3d6fd4bba74..3a2061a847f66ad4ab7350752f499c8d53957c65 100644
--- a/mhcflurry/mhc1_binding_predictor.py
+++ b/mhcflurry/mhc1_binding_predictor.py
@@ -15,7 +15,11 @@
 """
 Allele specific MHC Class I binding affinity predictor
 """
-
+from __future__ import (
+    print_function,
+    division,
+    absolute_import,
+)
 from os import listdir
 from os.path import exists, join
 from itertools import groupby
@@ -110,7 +114,7 @@ class Mhc1BindingPredictor(object):
             return [
                 peptide[:i] + extra_amino_acid + peptide[i:]
                 for peptide in peptides
-                for i in xrange(3, 8)
+                for i in range(3, 8)
                 for extra_amino_acid in amino_acid_letters
             ]
         else:
@@ -141,7 +145,7 @@ class Mhc1BindingPredictor(object):
             raw_y = self._predict_9mer_peptides(expanded_peptides)
             median_y = np.zeros(n_group)
             # take the median of each group of log(IC50) values
-            for i in xrange(n_group):
+            for i in range(n_group):
                 start = i * expansion_factor
                 end = (i + 1) * expansion_factor
                 median_y[i] = np.median(raw_y[start:end])
diff --git a/scripts/create-combined-class1-dataset.py b/scripts/create-combined-class1-dataset.py
index 4544b819da62073cc173922567efc0049ddd2edf..cf8bbb6253519da9a815cd63748dba8fad0c8342 100755
--- a/scripts/create-combined-class1-dataset.py
+++ b/scripts/create-combined-class1-dataset.py
@@ -6,6 +6,12 @@ Combine 2013 Kim/Peters NetMHCpan dataset[*] with more recent IEDB entries
 * = "Dataset size and composition impact the reliability..."
 """
 
+from __future__ import (
+    print_function,
+    division,
+    absolute_import,
+    unicode_literals
+)
 from os.path import join
 import pickle
 from collections import Counter
diff --git a/scripts/mhcflurry-class1-web-server.py b/scripts/mhcflurry-class1-web-server.py
new file mode 100755
index 0000000000000000000000000000000000000000..ef2067b80025a2946a0a22666cd5a33bbbf09ff7
--- /dev/null
+++ b/scripts/mhcflurry-class1-web-server.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2015. Mount Sinai School of Medicine
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import (
+    print_function,
+    division,
+    absolute_import,
+    unicode_literals
+)
+import argparse
+
+from bottle import post, request, run
+
+from mhcflurry.common import (
+    split_uppercase_sequences,
+    split_allele_names,
+)
+from mhcflurry.class1 import predict
+
+parser = argparse.ArgumentParser()
+
+parser.add_argument("--host", default="0.0.0.0")
+parser.add_argument("--port", default=80, type=int)
+parser.add_argument("--debug", default=False, action="store_true")
+
+
+@post('/')
+def get_binding_value():
+    peptides_string = request.forms.get('peptide')
+    peptides_list = split_uppercase_sequences(peptides_string)
+    alleles_string = request.forms.get('allele')
+    alleles_list = split_allele_names(alleles_string)
+    result_df = predict(alleles=alleles_list, peptides=peptides_list)
+    return result_df.to_csv(sep="\t", index=False)
+
+if __name__ == "__main__":
+    args = parser.parse_args()
+    run(host=args.host, port=args.port, debug=args.debug)
\ No newline at end of file
diff --git a/scripts/mhcflurry-class1.py b/scripts/mhcflurry-class1.py
index a4f480d93bdaf23ee19a7e4bb40d6e567b6999ae..d1a9245801743cefe88e7891e7be8526966b2fae 100755
--- a/scripts/mhcflurry-class1.py
+++ b/scripts/mhcflurry-class1.py
@@ -15,16 +15,20 @@
 # limitations under the License.
 
 
+from __future__ import (
+    print_function,
+    division,
+    absolute_import,
+    # unicode_literals
+)
 import argparse
 
-import pandas as pd
-
 from mhcflurry.common import (
     parse_int_list,
     split_uppercase_sequences,
-    split_allele_names
+    split_allele_names,
 )
-from mhcflurry import Mhc1BindingPredictor
+from mhcflurry.class1 import predict
 
 parser = argparse.ArgumentParser()
 
@@ -48,10 +52,5 @@ parser.add_argument("--peptide-lengths",
 
 if __name__ == "__main__":
     args = parser.parse_args()
-    allele_dataframes = []
-    for allele in args.mhc:
-        model = Mhc1BindingPredictor(allele=allele)
-        df = model.predict_peptides(args.sequence)
-        allele_dataframes.append(df)
-    combined = pd.concat(allele_dataframes)
-    print combined
+    df = predict(alleles=args.mhc, peptides=args.sequence)
+    print(df.to_csv(sep="\t", index=False), end="")
diff --git a/scripts/train-class1-allele-specific-models.py b/scripts/train-class1-allele-specific-models.py
index e422588ce9b71fb0e1ecf2e7757a014acd75d16f..784e8ca222358b529eb28db40bd284dcc3ad4d7e 100755
--- a/scripts/train-class1-allele-specific-models.py
+++ b/scripts/train-class1-allele-specific-models.py
@@ -17,6 +17,12 @@ Using the following hyperparameters:
 Nielsen 2009 dataset.
 """
 
+from __future__ import (
+    print_function,
+    division,
+    absolute_import,
+    unicode_literals
+)
 from shutil import rmtree
 from os import makedirs
 from os.path import exists, join