Storage Quotas

Storage quotas let you cap how much each tenant can store. Use them to enforce your subscription tiers, prevent abuse, and build quota-aware UIs for your users.

How quotas work

Off by default

Tenants have no storage limit until you set one. They can upload as much as your plan allows.

Enforced at upload

Tenantbox checks the quota before issuing a presigned URL. Uploads that would exceed the limit are rejected before anything reaches Tenantbox storage.

Adjustable anytime

Set, update, or remove a quota at any time via the API. Changes take effect on the next upload attempt.

Per tenant

Quotas are per tenant, not per project. Each user gets their own limit independent of other users in the same project.

Common quota sizes

The API expects quota values in bytes. Here are common sizes for reference:

SizeBytesTypical use case
100 MB104857600Free tier — light users, small files
500 MB524288000Free tier — moderate usage
1 GB1073741824Starter plan
5 GB5368709120Pro plan — typical SaaS user
10 GB10737418240Pro plan — power users
50 GB53687091200Enterprise — high volume tenants
No limitnullEnterprise — unlimited storage

Pre-upload quota check

While Tenantbox enforces quotas server-side, it's good practice to check usage before attempting an upload so you can show a meaningful error in your UI rather than catching a 413 response.

Python — check quota before uploading
import requests

def can_upload(tenant_id: str, file_size_bytes: int, api_key: str) -> bool:
    """
    Check if a tenant has enough quota before uploading.
    Call this before requesting a presigned URL.
    """
    response = requests.get(
        f"https://api.tenantbox.dev/api/storage/tenants/{tenant_id}/usage/",
        headers={"Authorization": f"Bearer {api_key}"},
    )
    response.raise_for_status()
    data = response.json()

    limit = data["storage_limit_bytes"]
    used  = data["storage_used_bytes"]

    # No limit set — always allow
    if limit is None:
        return True

    return (used + file_size_bytes) <= limit


# Usage
if can_upload("user_123", file.size, API_KEY):
    # Proceed with upload
    pass
else:
    raise Exception("Storage quota exceeded")

Tiered limits by plan

The most common pattern is to set a tenant's quota when they sign up or change their subscription plan. Here's how to implement that:

Python — set quota based on subscription tier
import requests

# Define your plan tiers in bytes
PLAN_LIMITS = {
    "free":       524288000,    # 500 MB
    "pro":        5368709120,   # 5 GB
    "enterprise": 53687091200,  # 50 GB
}

def set_tenant_quota(tenant_id: str, plan: str, api_key: str):
    """
    Set a tenant's storage limit based on their subscription plan.
    Call this when a user signs up or upgrades their plan.
    """
    limit_bytes = PLAN_LIMITS.get(plan)

    response = requests.patch(
        f"https://api.tenantbox.dev/api/storage/tenants/{tenant_id}/limit/",
        headers={
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
        },
        json={"storage_limit_bytes": limit_bytes},
    )
    response.raise_for_status()
    return response.json()


# New user signs up on free plan
set_tenant_quota("user_123", "free", API_KEY)

# User upgrades to pro
set_tenant_quota("user_123", "pro", API_KEY)

# Enterprise user — remove limit entirely
requests.patch(
    "https://api.tenantbox.dev/api/storage/tenants/user_123/limit/",
    headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
    json={"storage_limit_bytes": None},
)

Building a usage bar

Show your users how much storage they've used with a simple progress bar. Fetch the usage data from your backend and render it client-side.

Vue — storage usage bar component
<!-- Vue component — storage usage bar -->
<script setup>
const props = defineProps({
  used: Number,   // storage_used_bytes
  limit: Number,  // storage_limit_bytes
})

const percent = computed(() => {
  if (!props.limit) return 0
  return Math.min(Math.round((props.used / props.limit) * 100), 100)
})

const barColor = computed(() => {
  if (percent.value >= 90) return 'bg-red-500'
  if (percent.value >= 70) return 'bg-yellow-500'
  return 'bg-green-500'
})

function formatBytes(bytes) {
  if (bytes === 0) return '0 B'
  const k = 1024
  const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
}
</script>

<template>
  <div class="space-y-1.5">
    <div class="flex justify-between text-sm">
      <span>{{ formatBytes(used) }} used</span>
      <span v-if="limit">{{ percent }}% of {{ formatBytes(limit) }}</span>
      <span v-else>Unlimited</span>
    </div>
    <div v-if="limit" class="w-full h-2 bg-gray-200 rounded-full overflow-hidden">
      <div
        class="h-full rounded-full transition-all duration-500"
        :class="barColor"
        :style="{ width: percent + '%' }"
      />
    </div>
    <p v-if="percent >= 90" class="text-sm text-red-500">
      Storage almost full. Delete files or upgrade your plan.
    </p>
  </div>
</template>

Live preview

1.2 GB used24% of 5 GB
380 MB used76% of 500 MB

Running low on storage.

97 MB used97% of 100 MB

Storage almost full. Delete files or upgrade your plan.

14.3 GB usedUnlimited
No storage limit set

Best practices

Set quotas at sign-up. Don't wait until a user hits an undefined limit — set a sensible default immediately when they create an account.

Warn users early. Show a warning in your UI when a tenant reaches 70% of their quota, not 99%. Give them time to act.

Adjust on plan change. When a user upgrades or downgrades their subscription, update their Tenantbox quota immediately via the API.

Handle deletions. When a user deletes content in your app, delete the corresponding files via the Tenantbox API so quota is freed up accurately.

Don't cache usage forever. Fetch fresh usage data before showing quota meters in your UI — stale data leads to confusing displays.

Always handle 413 responses. Even if you check quota before uploading, always catch 413 errors gracefully — race conditions can still occur.

Quota exceeded error

When an upload would exceed a tenant's quota, Tenantbox returns a 413 before generating the presigned URL — nothing is uploaded to Tenantbox storage.

413Quota Exceeded
{
  "detail": "Storage quota exceeded for this tenant"
}

Next — Usage & tracking

Learn how to monitor storage usage across all tenants in your project.

Usage & tracking