Rate Limiting with WordPress’ Transient API

November 4th, 2010  |  Published in Mind

I run a web app called leenk.me, it’s a Social Media Optimization application for WordPress. Basically it publishes your WordPress content to Twitter / Facebook / Google Buzz whenever you publish new content to your website. There are a lot of “advertisers” who have been signing up for the service and one in particular has been hitting the service hard — really hard — about 4000 API requests in an hour.

This is a pretty big problem, all these social networks rate limit their connections (close to 300 requests per hour). So making 4000 requests in an hour could cause them to ban the user, or worse, ban leenk.me. So I’ve had to implement a rate limit of my own. I thought about doing this with iptables to block anyone who exceeds a certain number of requests per hour, but the violating users wouldn’t know what happened. I really wanted needed a way to prevent them from over-connecting to the social networks.

This is where the WordPress Transients API comes in. The transients API is very similar to the Options API but with the added feature of an expiration time. This is basically how the transients API works in WordPress:

// Save a transient to the DB
set_transient( $transient_name, $transient_value, $expiration );
  • $transient_name – A unique identifier for your cached data.
  • $transient_value – Data to save, either a regular variable or an array/object. The API will handle serialization of complex data for you.
  • $expiration – Number of seconds to keep the data before refreshing.
// Get value of a transient from the DB
$transient_value = get_transient( $transient_name );
// Delete a transient from the DB
delete_transient( $transient_name );

The only problem with WP Transients is that there isn’t a good way to create “rolling transients” (as I call them). A rolling transient is a transient that rolls in time. In other words, I want to rate-limit a users connection by 1 hour from their current API call, but if the users does not make any API calls within the hour the transient should expire.

This is how I implemented “rolling transients”:

$call_limit = 350; // API calls (in an hour)
$time_limit = 60 * 60; // 1 hour (in seconds)
$transient_name = $host  . "_rate_limit"; // Using their host name as the unique identifier

// Check to see if there are any transients that match the name, if not create a new one
if ( false === ( $calls = get_transient( $transient_name ) ) ) {
	$calls[] = time();
	set_transient( $transient, $calls, $time_limit ); // Use an array of time() stamps for rolling effect
} else {
	// There is already a transient with this name
	$calls[] = time(); // Add a new time() stamp to the $calls array
	set_transient( $transient, $calls, $time_limit ); // Reset the transient (w/ expiration time)

	$call_count = count( $calls ); // How many calls have been made

	if ( $call_limit < $call_count ) { // If we're over the call limit, remove expired timestamps
		// Shift time from first element of array
		while ( $call = array_shift( $calls ) ) {
			// If time is >= current time - time limit, then it belongs in the array
			// Add it back and reset the transient
			if ( $call >= ( time() - $time_limit ) ) {
				array_unshift( $calls, $call );
				set_transient( $transient, $calls, $time_limit );
				break; // Stop processing, we're within the time_limit time now.
			}
		}

		// If we're still over the call limit, they've made too many requests in the time limit
		if ( $call_limit <= count( $calls ) ) {
			// The session needs to be killed
			die('Error: You have exceeded your rate limit for API calls, only ' . $call_limit . ' API calls are allowed every ' . $time_limit . ' seconds.');
		}
	}
}

With this code, you now have rolling transients... if your users exceed the number of calls within the past hour they will be rejected. If not, the transient will expire as it should. Let me know if you found this useful or if you have any tips for making it better (it seems a little "hacky" to me).

Tags: , ,

Notable Tech Posts – 2010.01.17

January 17th, 2010  |  Published in Mind

TiptTp jQuery plugin

Create a PHP  server load meter

jQuery Events: MouseOver – MouseOut vs MouseEnter – MouseLeave

15 useful jQuery plugins and tutorials

CSS important

Sexy jQuery drop down multi-level menu

jQuery quick guide

Take your HTML tables to a new level with Javascript frameworks

Javascript replace all using regex for

Tags: , , , , , , , , ,

Notable Tech Posts – 2009.12.13

December 13th, 2009  |  Published in Mind

Managing UI complexity

An idiot’s guide to accessible website design

Create custom reCaptcha images using their API

22 latest exceptional WordPress hacks

10 CSS snippets to save precious time

If post is older than – WordPress

Accessing jQuery methods using array syntax

Tags: , , , , , ,

Notable Tech Posts – 2009.12.06

December 6th, 2009  |  Published in Mind

10 usability crimes you really shouldn’t commit

Design WordPress theme scratch

10 useful code snippets and plugins to spice up WordPress avatar

How to create a simple API with PHP and MySQL

Star Wars HTML and CSS a New Hope

10 ways to make WordPress more useful

10 front end techniques to improve your site usability

The ultimate toolbox for iPhone development

Premium free fresh WordPress themes year 2009

10 WordPress security plugins to keep your blog safe

jQuery slider tutorials and  plugins

Tags: , , , , , , , , , , ,

Notable Tech Posts – 2009.10.11

October 11th, 2009  |  Published in Mind

22 Halloween Photoshop Brushes Vectors Fonts

95 Great Photoshop Tutorials to Learn Awesome Photo Manipulation Tricks

Simple Techniques to Lock Down Your Website

APIs for Creating Next Big Mashup

queryLoader a Full Page Preloader

25 Cheat Sheets Web Developer Should Have

Essential WordPress Plugin Development Resources Tutorials and Guides

Tags: , , , , , ,