Tanstack Start

Quotas & plan limits

Single-file size, total storage, and API-key limits per plan.

Each account has plan limits enforced by the API: the largest single file, the total storage across all live files, and the number of API keys. The limits are real and checked on every upload — exceeding them returns a precise error before any bytes move.

Beta plan limits

LimitBeta plan
Largest single file100 MB
Total storage5 GB
API keys20

Beta gets the paid allowance for now

Billing is not wired up yet, so every account currently receives the paid-plan beta allowance for free. The quota machinery is fully active, so these limits apply now.

How limits are enforced

Single-file size

createUpload rejects a file larger than the plan's single-file limit before minting any upload URL, with code FILE_TOO_LARGE (HTTP 413).

This is separate from a route handler maxFileSize: the plan limit is the hard ceiling for your account; a route constraint can set a lower, per-route limit.

Total storage

Before reserving an upload, the API sums the size of all your live (ready and pending) files. If the new file would push the total over the plan quota, it rejects with QUOTA_EXCEEDED (HTTP 403).

Deleting files frees quota immediately — delete removes the bucket object and soft-deletes the row, and soft-deleted files do not count toward usage.

API keys

You can hold up to the plan's key limit at once (revoked keys do not count). Creating one beyond the limit fails — revoke an unused key first. See API keys.

Checking usage

Call usage to see where you stand:

const { usedBytes, fileCount, plan, limits } = await s3.usage();

const remaining = limits.maxTotalBytes - usedBytes;
console.log(`${plan}: ${fileCount} files, ${remaining} bytes free`);
{
  usedBytes: 524288000,
  fileCount: 42,
  plan: "beta",
  limits: { maxFileBytes: 104857600, maxTotalBytes: 5368709120, maxApiKeys: 20 }
}

The dashboard shows the same numbers on the Usage page. An AI agent can read them too via the getUsage tool.

Handling quota errors

import { S3DeliveryError } from "s3delivery";

try {
  await s3.upload(blob, { name: "big.zip" });
} catch (e) {
  if (e instanceof S3DeliveryError) {
    if (e.code === "FILE_TOO_LARGE") return showError("File exceeds the size limit.");
    if (e.code === "QUOTA_EXCEEDED") return showError("Storage is full — delete some files.");
  }
  throw e;
}

On this page