diff --git a/django/contrib/messages/api.py b/django/contrib/messages/api.py
index 4164f27..7159737 100644
--- a/django/contrib/messages/api.py
+++ b/django/contrib/messages/api.py
@@ -1,4 +1,5 @@
 from django.contrib.messages import constants
+from django.contrib.messages.storage import default_storage
 from django.utils.functional import lazy, memoize
 
 __all__ = (
@@ -44,6 +45,34 @@ def get_messages(request):
     return lazy(memoize(get_user().get_and_delete_messages, {}, 0), list)()
 
 
+def get_level(request):
+    """
+    Returns the minimum level of messages to be recorded.
+
+    The default level is the ``MESSAGE_LEVEL`` setting. If this is not found,
+    the ``INFO`` level is used.
+    """
+    if hasattr(request, '_messages'):
+        storage = request._messages
+    else:
+        storage = default_storage(request)
+    return storage.level
+
+
+def set_level(request, level):
+    """
+    Sets the minimum level of messages to be recorded, returning ``True`` if
+    the level was recorded successfully.
+
+    If set to ``None``, the default level will be used (see the ``get_level``
+    method).
+    """
+    if not hasattr(request, '_messages'):
+        return False
+    request._messages.level = level
+    return True
+
+
 def debug(request, message, extra_tags='', fail_silently=False):
     """
     Adds a message with the ``DEBUG`` level.
diff --git a/django/contrib/messages/tests/base.py b/django/contrib/messages/tests/base.py
index 6ca9185..d0edce9 100644
--- a/django/contrib/messages/tests/base.py
+++ b/django/contrib/messages/tests/base.py
@@ -3,11 +3,11 @@ from django.test import TestCase
 from django.conf import settings
 from django.utils.translation import ugettext_lazy
 from django.contrib.messages import constants, utils
+from django.contrib.messages.api import MessageFailure, get_level, set_level
 from django.contrib.messages.storage import default_storage, base
 from django.contrib.messages.storage.base import Message
 from django.core.urlresolvers import reverse
 from django.contrib.auth.models import User
-from django.contrib.messages.api import MessageFailure
 
 
 def add_level_messages(storage):
@@ -41,16 +41,19 @@ class BaseTest(TestCase):
             if hasattr(settings, setting):
                 self._remembered_settings[setting] = getattr(settings, setting)
                 delattr(settings._wrapped, setting)
-        # backup these manually because we do not want them deleted
+        # Backup these manually because we do not want them deleted.
         self._middleware_classes = settings.MIDDLEWARE_CLASSES
         self._template_context_processors = \
            settings.TEMPLATE_CONTEXT_PROCESSORS
         self._installed_apps = settings.INSTALLED_APPS
+        self._message_storage = settings.MESSAGE_STORAGE
+        settings.MESSAGE_STORAGE = '%s.%s' % (self.storage_class.__module__,
+                                              self.storage_class.__name__)
 
     def tearDown(self):
         for setting in self.restore_settings:
             self.restore_setting(setting)
-        # restore these manually (see above)
+        # Restore these manually (see above).
         settings.MIDDLEWARE_CLASSES = self._middleware_classes
         settings.TEMPLATE_CONTEXT_PROCESSORS = \
            self._template_context_processors
@@ -319,25 +322,48 @@ class BaseTest(TestCase):
         self.assert_(storage.added_new)
 
     def test_default_level(self):
+        # get_level works even with no storage on the request.
+        request = self.get_request()
+        self.assertEqual(get_level(request), constants.INFO)
+
+        # get_level returns the default level if it hasn't been set.
         storage = self.get_storage()
+        request._messages = storage
+        self.assertEqual(get_level(request), constants.INFO)
+
+        # Only messages of sufficient level get recorded.
         add_level_messages(storage)
         self.assertEqual(len(storage), 5)
 
     def test_low_level(self):
-        storage = self.get_storage()
-        storage.level = 5
+        request = self.get_request()
+        storage = self.storage_class(request)
+        request._messages = storage
+
+        self.assert_(set_level(request, 5))
+        self.assertEqual(get_level(request), 5)
+
         add_level_messages(storage)
         self.assertEqual(len(storage), 6)
 
     def test_high_level(self):
-        storage = self.get_storage()
-        storage.level = 30
+        request = self.get_request()
+        storage = self.storage_class(request)
+        request._messages = storage
+
+        self.assert_(set_level(request, 30))
+        self.assertEqual(get_level(request), 30)
+
         add_level_messages(storage)
         self.assertEqual(len(storage), 2)
 
     def test_settings_level(self):
+        request = self.get_request()
+        storage = self.storage_class(request)
+
         settings.MESSAGE_LEVEL = 29
-        storage = self.get_storage()
+        self.assertEqual(get_level(request), 29)
+
         add_level_messages(storage)
         self.assertEqual(len(storage), 3)
 
diff --git a/docs/ref/contrib/messages.txt b/docs/ref/contrib/messages.txt
index 7cfe4ae..554e70b 100644
--- a/docs/ref/contrib/messages.txt
+++ b/docs/ref/contrib/messages.txt
@@ -137,8 +137,11 @@ Constant    Purpose
 ``ERROR``   An action was **not** successful or some other failure occurred
 =========== ========
 
-The `MESSAGE_LEVEL`_ setting can be used to change the minimum recorded
-level. Attempts to add messages of a level less than this will be ignored.
+The `MESSAGE_LEVEL`_ setting can be used to change the minimum recorded level
+(or it can be `changed per request`_). Attempts to add messages of a level less
+than this will be ignored.
+
+.. _`changed per request`: `Changing the minimum recorded level per-request`_
 
 Message tags
 ------------
@@ -245,22 +248,27 @@ provide a mapping via the `MESSAGE_TAGS`_ setting.
 Changing the minimum recorded level per-request
 -----------------------------------------------
 
-The minimum recorded level can be set per request by changing the ``level``
-attribute of the messages storage instance::
+The minimum recorded level can be set per request via the ``set_level``
+method::
 
     from django.contrib import messages
 
     # Change the messages level to ensure the debug message is added.
-    messages.get_messages(request).level = messages.DEBUG
+    messages.set_level(request, messages.DEBUG)
     messages.debug(request, 'Test message...')
 
     # In another request, record only messages with a level of WARNING and higher
-    messages.get_messages(request).level = messages.WARNING
+    messages.set_level(request, messages.WARNING)
     messages.success(request, 'Your profile was updated.') # ignored
     messages.warning(request, 'Your account is about to expire.') # recorded
 
     # Set the messages level back to default.
-    messages.get_messages(request).level = None
+    messages.set_level(request, None)
+
+Similarly, the current effective level can be retrieved with ``get_level``::
+
+    from django.contrib import messages
+    current_level = messages.get_level(request)
 
 For more information on how the minimum recorded level functions, see
 `Message levels`_ above.
