diff --git a/registrasion/controllers/product.py b/registrasion/controllers/product.py
index d91a2541..88beb8f8 100644
--- a/registrasion/controllers/product.py
+++ b/registrasion/controllers/product.py
@@ -1,6 +1,7 @@
 import itertools
 
 from django.db.models import Q
+from django.db.models import Sum
 from registrasion import models as rego
 
 from conditions import ConditionController
@@ -42,17 +43,25 @@ class ProductController(object):
 
         carts = rego.Cart.objects.filter(user=user)
         items = rego.ProductItem.objects.filter(
-            product=self.product,
-            cart=carts)
+            cart=carts,
+        )
 
-        count = 0
-        for item in items:
-            count += item.quantity
+        prod_items = items.filter(product=self.product)
+        cat_items = items.filter(product__category=self.product.category)
 
-        if quantity + count > self.product.limit_per_user:
-            return False
-        else:
+        prod_count = prod_items.aggregate(Sum("quantity"))["quantity__sum"]
+        cat_count = cat_items.aggregate(Sum("quantity"))["quantity__sum"]
+
+        prod_limit = self.product.limit_per_user
+        prod_met = prod_limit is None or quantity + prod_count <= prod_limit
+
+        cat_limit = self.product.category.limit_per_user
+        cat_met = cat_limit is None or quantity + cat_count <= cat_limit
+
+        if prod_met and cat_met:
             return True
+        else:
+            return False
 
     def can_add_with_enabling_conditions(self, user, quantity):
         ''' Returns true if the user is able to add _quantity_ to their count
diff --git a/registrasion/migrations/0007_auto_20160326_2105.py b/registrasion/migrations/0007_auto_20160326_2105.py
new file mode 100644
index 00000000..dbcf2ac7
--- /dev/null
+++ b/registrasion/migrations/0007_auto_20160326_2105.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('registrasion', '0006_category_required'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='category',
+            name='limit_per_user',
+            field=models.PositiveIntegerField(null=True, verbose_name='Limit per user', blank=True),
+        ),
+        migrations.AlterField(
+            model_name='product',
+            name='limit_per_user',
+            field=models.PositiveIntegerField(null=True, verbose_name='Limit per user', blank=True),
+        ),
+    ]
diff --git a/registrasion/models.py b/registrasion/models.py
index 75949e24..c95e1740 100644
--- a/registrasion/models.py
+++ b/registrasion/models.py
@@ -132,6 +132,10 @@ class Category(models.Model):
     name = models.CharField(max_length=65, verbose_name=_("Name"))
     description = models.CharField(max_length=255,
                                    verbose_name=_("Description"))
+    limit_per_user = models.PositiveIntegerField(
+        null=True,
+        blank=True,
+        verbose_name=_("Limit per user"))
     required = models.BooleanField(blank=True)
     order = models.PositiveIntegerField(verbose_name=("Display order"))
     render_type = models.IntegerField(choices=CATEGORY_RENDER_TYPES,
@@ -153,6 +157,7 @@ class Product(models.Model):
                                 decimal_places=2,
                                 verbose_name=_("Price"))
     limit_per_user = models.PositiveIntegerField(
+        null=True,
         blank=True,
         verbose_name=_("Limit per user"))
     reservation_duration = models.DurationField(
diff --git a/registrasion/tests/test_cart.py b/registrasion/tests/test_cart.py
index b4ff3919..5989f6e2 100644
--- a/registrasion/tests/test_cart.py
+++ b/registrasion/tests/test_cart.py
@@ -35,7 +35,7 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
         cls.RESERVATION = datetime.timedelta(hours=1)
 
         cls.categories = []
-        for i in xrange(3):
+        for i in xrange(2):
             cat = rego.Category.objects.create(
                 name="Category " + str(i + 1),
                 description="This is a test category",
@@ -48,13 +48,12 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
 
         cls.CAT_1 = cls.categories[0]
         cls.CAT_2 = cls.categories[1]
-        cls.CAT_3 = cls.categories[2]
 
         cls.products = []
-        for i in xrange(6):
+        for i in xrange(4):
             prod = rego.Product.objects.create(
                 name="Product 1",
-                description="This is a test product."
+                description="This is a test product.",
                 category=cls.categories[i / 2],  # 2 products per category
                 price=Decimal("10.00"),
                 reservation_duration=cls.RESERVATION,
@@ -68,8 +67,6 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
         cls.PROD_2 = cls.products[1]
         cls.PROD_3 = cls.products[2]
         cls.PROD_4 = cls.products[3]
-        cls.PROD_5 = cls.products[4]
-        cls.PROD_6 = cls.products[5]
 
         cls.PROD_4.price = Decimal("5.00")
         cls.PROD_4.save()
@@ -208,3 +205,86 @@ class BasicCartTests(RegistrationCartTestCase):
         # Second user should not be affected by first user's limits
         second_user_cart = CartController.for_user(self.USER_2)
         second_user_cart.add_to_cart(self.PROD_1, 10)
+
+    def set_limits(self):
+        self.CAT_2.limit_per_user = 10
+        self.PROD_2.limit_per_user = None
+        self.PROD_3.limit_per_user = None
+        self.PROD_4.limit_per_user = 6
+
+        self.CAT_2.save()
+        self.PROD_2.save()
+        self.PROD_3.save()
+        self.PROD_4.save()
+
+    def test_per_user_product_limit_ignored_if_blank(self):
+        self.set_limits()
+
+        current_cart = CartController.for_user(self.USER_1)
+        # There is no product limit on PROD_2, and there is no cat limit
+        current_cart.add_to_cart(self.PROD_2, 1)
+        # There is no product limit on PROD_3, but there is a cat limit
+        current_cart.add_to_cart(self.PROD_3, 1)
+
+    def test_per_user_category_limit_ignored_if_blank(self):
+        self.set_limits()
+        current_cart = CartController.for_user(self.USER_1)
+        # There is no product limit on PROD_2, and there is no cat limit
+        current_cart.add_to_cart(self.PROD_2, 1)
+        # There is no cat limit on PROD_1, but there is a prod limit
+        current_cart.add_to_cart(self.PROD_1, 1)
+
+    def test_per_user_category_limit_only(self):
+        self.set_limits()
+
+        current_cart = CartController.for_user(self.USER_1)
+
+        # Cannot add to cart if category limit is filled by one product.
+        current_cart.set_quantity(self.PROD_3, 10)
+        with self.assertRaises(ValidationError):
+            current_cart.set_quantity(self.PROD_4, 1)
+
+        # Can add to cart if category limit is not filled by one product
+        current_cart.set_quantity(self.PROD_3, 5)
+        current_cart.set_quantity(self.PROD_4, 5)
+        # Cannot add to cart if category limit is filled by two products
+        with self.assertRaises(ValidationError):
+            current_cart.add_to_cart(self.PROD_3, 1)
+
+        current_cart.cart.active = False
+        current_cart.cart.save()
+
+        current_cart = CartController.for_user(self.USER_1)
+        # The category limit should extend across carts
+        with self.assertRaises(ValidationError):
+            current_cart.add_to_cart(self.PROD_3, 10)
+
+    def test_per_user_category_and_product_limits(self):
+        self.set_limits()
+
+        current_cart = CartController.for_user(self.USER_1)
+
+        # Hit both the product and category edges:
+        current_cart.set_quantity(self.PROD_3, 4)
+        current_cart.set_quantity(self.PROD_4, 6)
+        with self.assertRaises(ValidationError):
+            # There's unlimited PROD_3, but limited in the category
+            current_cart.add_to_cart(self.PROD_3, 1)
+
+        current_cart.set_quantity(self.PROD_3, 0)
+        with self.assertRaises(ValidationError):
+            # There's only 6 allowed of PROD_4
+            current_cart.add_to_cart(self.PROD_4, 1)
+
+        # The limits should extend across carts...
+        current_cart.cart.active = False
+        current_cart.cart.save()
+
+        current_cart = CartController.for_user(self.USER_1)
+        current_cart.set_quantity(self.PROD_3, 4)
+
+        with self.assertRaises(ValidationError):
+            current_cart.set_quantity(self.PROD_3, 5)
+
+        with self.assertRaises(ValidationError):
+            current_cart.set_quantity(self.PROD_4, 1)