Creating a Rate Limiter using Workers/KV

I have a free project built with Pages + Workers + KV offering a public API for people and I am trying to create a rate limit system to prevent abusers and excessive cost.

Unfortunately my options are quite limited and I am not sure what can be done to achieve this. I have written a small class that uses KV as POC, but this is not cost effective at all.

Imagine if a few hundred IP’s are using the API and this rate limiter constantly reads/gets/puts into KV… it might generate more cost than the whole API itself.

Please see below my code inspired from another website (I`m relatively new to workers/kv/typescript so apologies for any irregularities you may find):

import moment from 'moment';

const WINDOW_SIZE_IN_HOURS 			= 24;
const MAX_WINDOW_REQUEST_COUNT 		= 10;
const WINDOW_LOG_INTERVAL_IN_HOURS	= 1;

async function RateLimiter(request)
{
  try 
  {
	const clientIP 	= request.headers.get("CF-Connecting-IP");
	const cfdata 	= request.cf !== undefined ? request.cf : { error: "The `cf` object is not available inside the preview." }
	const currentRequestTime = moment();
  
    let FoundData = await RATE_LIMITER.get(clientIP);
	
	if(FoundData)
	{
		//if record is found, parse it's value and calculate number of requests users has made within the last window	
		console.log(FoundData);
		
		let data = JSON.parse(FoundData);
		let windowStartTimestamp = moment()
			.subtract(WINDOW_SIZE_IN_HOURS, 'hours')
			.unix();
			
		let requestsWithinWindow = data.filter(entry => {
			return entry.requestTimeStamp > windowStartTimestamp;
		});
		console.log('requestsWithinWindow', requestsWithinWindow);
		
		let totalWindowRequestsCount = requestsWithinWindow.reduce((accumulator, entry) => {
			return accumulator + entry.requestCount;
		}, 0);
		
		// if number of requests made is greater than or equal to the desired maximum, return error
		if (totalWindowRequestsCount >= MAX_WINDOW_REQUEST_COUNT) 
		{
			return `You have exceeded the ${MAX_WINDOW_REQUEST_COUNT} requests in ${WINDOW_SIZE_IN_HOURS} hrs limit!`;
		} 
		else 
		{
			// if number of requests made is less than allowed maximum, log new entry
			let lastRequestLog = data[data.length - 1];
			let potentialCurrentWindowIntervalStartTimeStamp = currentRequestTime
				.subtract(WINDOW_LOG_INTERVAL_IN_HOURS, 'hours')
				.unix();
				
			// if interval has not passed since last request log, increment counter
			if (lastRequestLog.requestTimeStamp > potentialCurrentWindowIntervalStartTimeStamp) 
			{
			  lastRequestLog.requestCount++;
			  data[data.length - 1] = lastRequestLog;
			  console.log('lastRequestLog', lastRequestLog);
			} 
			else 
			{
			  // if interval has passed, log new entry for current user and timestamp
			  data.push({
				requestTimeStamp: currentRequestTime.unix(),
				requestCount: 1
			  });
			}
        
			console.log('data', data);
			await RATE_LIMITER.put(clientIP, JSON.stringify(data));
			return null;
		}
	}
	else
	{
		// if no record is found, create a new record for user and store to KV
		let newRecord :any[] = [];
        let requestLog = {
          requestTimeStamp: currentRequestTime.unix(),
          requestCount: 1
        };
        newRecord.push(requestLog);
        await RATE_LIMITER.put(clientIP, JSON.stringify(newRecord));
        return null;
	}
  } 
  catch (error)
  {
    return error.message;
  }
};

export default RateLimiter

If you have any ideas on how to optimise this, or a better solution than this please advise. I`m sure many developers are looking for a good solution.

Thank you

KV is not really a good idea here due to it’s eventually consistent nature. You can however make a rate-limit from Durable Objects, which some others have already made from too.

Durable Objects are consistent which means you will always have the most up to date information, pretty critical for rate-limiting.

The downside is the fact they will cost, the pricing is not known yet but it should not be more expensive than what is listed here: https://blog.cloudflare.com/durable-objects-open-beta/

There is always the Rate Limiting product too, however this of course will also incur a cost.


There is no reliable way to create a rate-limiting system in Workers for free, DOs would be your best bet (and you could create it for “free” before GA).

Thank you, I didn’t know Durable Objects is an option. Would you be so kind to offer an example POC? Just for the sake of testing, I might go with it.

Regarding Rate Limiting feature from CF itself, it would be much more cost effective than using KV for instance. However… you can’t really customise it. For example, I want to allow people without API keys to perform 100/r per 1 hour, but people with API keys different limits.

If you join the Discord and ask there, I’m sure someone can share their code or maybe link to a GitHub repo with it.
Invite: https://workers.community

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.