diff --git a/swiftclient/shell.py b/swiftclient/shell.py
index d10fc70a..3b3757c1 100755
--- a/swiftclient/shell.py
+++ b/swiftclient/shell.py
@@ -38,7 +38,7 @@ except ImportError:
 
 from swiftclient import Connection, RequestException
 from swiftclient import command_helpers
-from swiftclient.utils import config_true_value, prt_bytes
+from swiftclient.utils import config_true_value, prt_bytes, generate_temp_url
 from swiftclient.multithreading import MultiThreadingManager
 from swiftclient.exceptions import ClientException
 from swiftclient import __version__ as client_version
@@ -1240,6 +1240,45 @@ def st_capabilities(parser, args, thread_manager):
 st_info = st_capabilities
 
 
+st_tempurl_options = '<method> <seconds> <path> <key>'
+
+st_tempurl_help = '''
+Generates a temporary URL for a Swift object.
+
+Positions arguments:
+  [method]              An HTTP method to allow for this temporary URL.
+                        Usually 'GET' or 'PUT'.
+  [seconds]             The amount of time in seconds the temporary URL will
+                        be valid for.
+  [path]                The full path to the Swift object. Example:
+                        /v1/AUTH_account/c/o.
+  [key]                 The secret temporary URL key set on the Swift cluster.
+                        To set a key, run \'swift post -m
+                        "Temp-URL-Key:b3968d0207b54ece87cccc06515a89d4"\'
+'''.strip('\n')
+
+
+def st_tempurl(parser, args, thread_manager):
+    (options, args) = parse_args(parser, args)
+    args = args[1:]
+    if len(args) < 4:
+        thread_manager.error('Usage: %s tempurl %s\n%s', BASENAME,
+                             st_tempurl_options, st_tempurl_help)
+        return
+    method, seconds, path, key = args[:4]
+    try:
+        seconds = int(seconds)
+    except ValueError:
+        thread_manager.error('Seconds must be an integer')
+        return
+    if method.upper() not in ['GET', 'PUT', 'HEAD', 'POST', 'DELETE']:
+        thread_manager.print_msg('WARNING: Non default HTTP method %s for '
+                                 'tempurl specified, possibly an error' %
+                                 method.upper())
+    url = generate_temp_url(path, seconds, key, method)
+    thread_manager.print_msg(url)
+
+
 def split_headers(options, prefix='', thread_manager=None):
     """
     Splits 'Key: Value' strings and returns them as a dictionary.
@@ -1269,6 +1308,10 @@ def parse_args(parser, args, enforce_requires=True):
         args = ['-h']
     (options, args) = parser.parse_args(args)
 
+    # Short circuit for tempurl, which doesn't need auth
+    if len(args) > 0 and args[0] == 'tempurl':
+        return options, args
+
     if (not (options.auth and options.user and options.key)):
         # Use 2.0 auth if none of the old args are present
         options.auth_version = '2.0'
@@ -1370,6 +1413,7 @@ Positional arguments:
                          or object.
     upload               Uploads files or directories to the given container.
     capabilities         List cluster capabilities.
+    tempurl              Create a temporary URL
 
 
 Examples:
@@ -1509,7 +1553,7 @@ Examples:
     parser.enable_interspersed_args()
 
     commands = ('delete', 'download', 'list', 'post',
-                'stat', 'upload', 'capabilities', 'info')
+                'stat', 'upload', 'capabilities', 'info', 'tempurl')
     if not args or args[0] not in commands:
         parser.print_usage()
         if args:
diff --git a/swiftclient/utils.py b/swiftclient/utils.py
index 058181dc..0f442b39 100644
--- a/swiftclient/utils.py
+++ b/swiftclient/utils.py
@@ -13,6 +13,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 """Miscellaneous utility functions for use with Swift."""
+import hashlib
+import hmac
+import logging
+import time
 
 import six
 
@@ -59,6 +63,51 @@ def prt_bytes(bytes, human_flag):
     return(bytes)
 
 
+def generate_temp_url(path, seconds, key, method):
+    """ Generates a temporary URL that gives unauthenticated access to the
+    Swift object.
+
+    :param path: The full path to the Swift object. Example:
+    /v1/AUTH_account/c/o.
+    :param seconds: The amount of time in seconds the temporary URL will
+    be valid for.
+    :param key: The secret temporary URL key set on the Swift cluster.
+    To set a key, run 'swift post -m
+    "Temp-URL-Key:b3968d0207b54ece87cccc06515a89d4"'
+    :param method: A HTTP method, typically either GET or PUT, to allow for
+    this temporary URL.
+    :raises: ValueError if seconds is not a positive integer
+    :raises: TypeError if seconds is not an integer
+    :return: the path portion of a temporary URL
+    """
+    if seconds < 0:
+        raise ValueError('seconds must be a positive integer')
+    try:
+        expiration = int(time.time() + seconds)
+    except TypeError:
+        raise TypeError('seconds must be an integer')
+
+    standard_methods = ['GET', 'PUT', 'HEAD', 'POST', 'DELETE']
+    if method.upper() not in standard_methods:
+        logger = logging.getLogger("swiftclient")
+        logger.warning('Non default HTTP method %s for tempurl specified, '
+                       'possibly an error', method.upper())
+
+    hmac_body = '\n'.join([method.upper(), str(expiration), path])
+
+    # Encode to UTF-8 for py3 compatibility
+    sig = hmac.new(key.encode(),
+                   hmac_body.encode(),
+                   hashlib.sha1).hexdigest()
+
+    return ('{path}?temp_url_sig='
+            '{sig}&temp_url_expires={exp}'.format(
+                path=path,
+                sig=sig,
+                exp=expiration)
+            )
+
+
 class LengthWrapper(object):
 
     def __init__(self, readable, length):
diff --git a/tests/unit/test_shell.py b/tests/unit/test_shell.py
index 0c282973..33cc91a1 100644
--- a/tests/unit/test_shell.py
+++ b/tests/unit/test_shell.py
@@ -22,6 +22,7 @@ import six
 
 import swiftclient
 import swiftclient.shell
+import swiftclient.utils
 
 
 if six.PY2:
@@ -328,6 +329,16 @@ class TestShell(unittest.TestCase):
                 'Content-Type': 'text/plain',
                 'X-Object-Meta-Color': 'Blue'})
 
+    @mock.patch('swiftclient.shell.generate_temp_url')
+    def test_temp_url(self, temp_url):
+        argv = ["", "tempurl", "GET", "60", "/v1/AUTH_account/c/o",
+                "secret_key"
+                ]
+        temp_url.return_value = ""
+        swiftclient.shell.main(argv)
+        temp_url.assert_called_with(
+            '/v1/AUTH_account/c/o', 60, 'secret_key', 'GET')
+
     @mock.patch('swiftclient.shell.Connection')
     def test_capabilities(self, connection):
         argv = ["", "capabilities"]
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
index d9d74c5b..f072aed1 100644
--- a/tests/unit/test_utils.py
+++ b/tests/unit/test_utils.py
@@ -15,6 +15,7 @@
 
 import testtools
 
+import mock
 import six
 import tempfile
 
@@ -122,6 +123,44 @@ class TestPrtBytes(testtools.TestCase):
         self.assertEqual('1024Y', u.prt_bytes(bytes_, True).lstrip())
 
 
+class TestTempURL(testtools.TestCase):
+
+    def setUp(self):
+        super(TestTempURL, self).setUp()
+        self.url = '/v1/AUTH_account/c/o'
+        self.seconds = 3600
+        self.key = 'correcthorsebatterystaple'
+        self.method = 'GET'
+
+    @mock.patch('hmac.HMAC.hexdigest')
+    @mock.patch('time.time')
+    def test_generate_temp_url(self, time_mock, hmac_mock):
+        time_mock.return_value = 1400000000
+        hmac_mock.return_value = 'temp_url_signature'
+        expected_url = (
+            '/v1/AUTH_account/c/o?'
+            'temp_url_sig=temp_url_signature&'
+            'temp_url_expires=1400003600')
+        url = u.generate_temp_url(self.url, self.seconds, self.key,
+                                  self.method)
+        self.assertEqual(url, expected_url)
+
+    def test_generate_temp_url_bad_seconds(self):
+        self.assertRaises(TypeError,
+                          u.generate_temp_url,
+                          self.url,
+                          'not_an_int',
+                          self.key,
+                          self.method)
+
+        self.assertRaises(ValueError,
+                          u.generate_temp_url,
+                          self.url,
+                          -1,
+                          self.key,
+                          self.method)
+
+
 class TestLengthWrapper(testtools.TestCase):
 
     def test_stringio(self):