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 subjectKind amount window start
Free monthly quota team-free 3000 month anchor
Free daily cap team-free 100 day calendar
Pro monthly quota team-pro 50000 month anchor
import { createClient } from "@limitry/sdk";
const limitry = createClient();
await limitry.limits.create({
name: "Free monthly quota",
subjectKind: "team-free",
action: "send-email",
amount: 3000,
unit: "emails",
window: "month",
start: "anchor",
});
await limitry.limits.create({
name: "Free daily cap",
subjectKind: "team-free",
action: "send-email",
amount: 100,
unit: "emails",
window: "day",
});
await limitry.limits.create({
name: "Pro monthly quota",
subjectKind: "team-pro",
action: "send-email",
amount: 50000,
unit: "emails",
window: "month",
start: "anchor",
});

The monthly quotas refill on each team’s own date (start: "anchor"). Until you set an anchor, that’s the day of the team’s first counted check. Once it pays, set the anchor to its billing date (step 3). The daily cap refills 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, LimitExceeded } 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 LimitExceeded) {
// Tell the sender which limit, and when to try again.
console.log(error.retryAfterSeconds);
}
}

When a team subscribes, set its anchor to the subscription’s billing date, so its quota refills on the day it pays. See Refill 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 hit their cap during a launch? Refill 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.