Skip to content

Offer a free plan with a daily cap

Email and messaging APIs often give a free plan a monthly quota, and a daily cap so one sign-up can’t send the whole month’s mail in an hour. Paid plans get a bigger quota and no daily cap. With Limitry that’s three limits, and one check.

Give each plan its own subject kind, so each plan gets its own limits:

Limit subject.kind amount window start
Free monthly quota team-free 3000 month billing_date
Free daily cap team-free 100 day calendar
Pro monthly quota team-pro 50000 month billing_date
import { createClient } from "@limitry/sdk";
const limitry = createClient();
await limitry.limits.create({
name: "Free monthly quota",
subject: { kind: "team-free" },
action: "send-email",
amount: 3000,
unit: "emails",
window: "month",
start: "billing_date",
});
await limitry.limits.create({
name: "Free daily cap",
subject: { kind: "team-free" },
action: "send-email",
amount: 100,
unit: "emails",
window: "day",
});
await limitry.limits.create({
name: "Pro monthly quota",
subject: { kind: "team-pro" },
action: "send-email",
amount: 50000,
unit: "emails",
window: "month",
start: "billing_date",
});

The monthly quotas reset on each team’s own date (start: "billing_date"). Until you set a billing date, that’s the day of the team’s first counted check. Once it pays, set its billing date (step 3). The daily cap resets at midnight UTC. A tighter alternative against bursts is a rolling 24 hours: window: "day", rolling: true.

Send the team’s plan as the subject kind. The check has to fit every limit on that kind, so a free team is refused after 100 in a day, even with quota left for the month:

import { createClient, LimitReached } from "@limitry/sdk";
const limitry = createClient();
try {
await limitry.guard(
{
subject: { kind: `team-${team.plan}`, id: team.id },
action: "send-email",
},
() => sendEmail(message),
);
} catch (error) {
if (error instanceof LimitReached) {
// Tell the sender which limit, and when to try again.
console.log(error.retryAfterSeconds);
}
}

When a team subscribes, set its billing date to the subscription’s, so its quota resets on the day it pays. See Reset on the billing date.

A team that upgrades moves to the team-pro kind: the daily cap no longer applies, and the Pro quota starts from its billing date.

A customer reached their cap during a launch? Reset it at once, without moving their schedule:

import { createClient } from "@limitry/sdk";
const limitry = createClient();
await limitry.subjects.reset("team-free", "team_42", {});

See Reset now.