Best way to count own SaaS API limits/usage

Hey, So, currently I'm providing an API through RapidAPI. They deal with billing, users, limits, etc. I'm looking of moving that to Workers instead, and I'll deal with user sign-ups, limits for requests they can make, etc. Ideally, I would like all customer requests to my workers to add a header like "X-Requests-Remaining: 1000", and decrease that every time they GET the worker, with their auth-id. And then have a hard limit, so it responds with HTTP error 403/429 when they've made 1000 requests. There are multiple ways of doing this (KV, D1, Durable Objects, Analytics+KV?), and of course I want to limit the costs, but still provide a good solution to this, and of course make the worker respond as quick as possible. Currently I'm at ~100k requests per day, so 3 million calls per month, but I expect this to increase. Using Durable Objects seems to be the best solution for exact results, but that can quickly increase the pricing too. I'm okay with somewhat close numbers, so one solution I'm looking at is using KV to store "requests_remaining" for each customer, so that is read once per worker, but let's say it randomly writes -100 to requests_remaining, at a 1% chance. Just to limit writing, both to get it cheaper, but also to respond to the call quicker. Statistically they'll get the right amount of calls (could also be like -100 at 0.09% chance or something to make sure they aren't too unlucky). Exact counting is nice, but not necessary. I'm also considering using analytics (or queues but seems more expensive?) to do the same, read the requests_remaining from KV, to make sure they aren't overusing it, log a write to analytics, and then every X minutes, go through analytics, see how many requests they made, and remove that from requests_remaining. Then the usage will be correct, and they can only overuse it for X minutes until the KV requests_remaining is 0. Customers are globally distributed, so I would prefer to have something close to the workers. I've only been playing with workers for a short while, so I'm at a loss what would be the "best" solution, and what would be "okay", but more cost effective. Thanks<3
7 Replies
hfcRed
hfcRed2mo ago
Cloudflare Docs
Rate Limiting | Cloudflare Workers docs
Define rate limits and interact with them directly from your Cloudflare Worker
hfcRed
hfcRed2mo ago
Would this suit your needs Afaik the rate limiter object contains a count for requests which you can extract
export interface Ratelimit {
/*
* The namespace ID for the ratelimit
*/
namespaceId: string;

/*
* The limit value for the ratelimit
*/
limitVal: number;

/*
* The period for the ratelimit
*/
period: number;

/*
* The ratelimit buckets, contains the amount of requests made per rate limit key.
*/
buckets: Map<string, number>;

/*
* The time the user was last let through successfully.
*/
epoch: number;

/*
* The ratelimit function
* @param {RatelimitOptions} options
* @returns {Promise<RatelimitResponse>}
*/
limit: (options: RatelimitOptions) => Promise<RatelimitResponse>;
}

export interface RatelimitOptions {
/*
* The key to identify the user, can be an IP address, user ID, etc.
*/
key: string;
}

export interface RatelimitResponse {
/*
* The ratelimit success status
* @returns {boolean}
*/
success: boolean;
}
export interface Ratelimit {
/*
* The namespace ID for the ratelimit
*/
namespaceId: string;

/*
* The limit value for the ratelimit
*/
limitVal: number;

/*
* The period for the ratelimit
*/
period: number;

/*
* The ratelimit buckets, contains the amount of requests made per rate limit key.
*/
buckets: Map<string, number>;

/*
* The time the user was last let through successfully.
*/
epoch: number;

/*
* The ratelimit function
* @param {RatelimitOptions} options
* @returns {Promise<RatelimitResponse>}
*/
limit: (options: RatelimitOptions) => Promise<RatelimitResponse>;
}

export interface RatelimitOptions {
/*
* The key to identify the user, can be an IP address, user ID, etc.
*/
key: string;
}

export interface RatelimitResponse {
/*
* The ratelimit success status
* @returns {boolean}
*/
success: boolean;
}
I think the bucket contains the request count of every key, so you could get the key of the current request from the map and subtract the number from limitVal and that should give you the remaining limit
anders
andersOP2mo ago
But if it’s limited to 60 seconds I would have to have another worker checking it every 60 seconds (for each customer) to see how many were used (if any)
hfcRed
hfcRed2mo ago
Oh wait Is it Ah yeah forgot about that
anders
andersOP2mo ago
yeah, that's what I thought. since I don't want to just charge afterwards, but rather limit requests, and give a hint in the API response to how many requests they have left, I think I'll do the following: GET /?auth=1234 my worker asks KV for "requests_remaining" for id=1234, gets 1000 I allow the call, and log to Analytics Engine Then, every ~10 minutes, I call analytics engine for each customer, check how many requests was made by id=1234, and reduce that number from requests_remaining in KV
anders
andersOP2mo ago
ChatGPT
A conversational AI system that listens, learns, and challenges
anders
andersOP2mo ago
Calculations look correct, so should be good! 🙌
Want results from more Discord servers?
Add your server