Using bit.ly’s REST service to shorten URLs

Posted:


(Note: Thanks to gizmo, I have corrected an abbreviation expansion problem.)

Uniform Resource Locators are an addressing scheme at the heart of the Web. Without them, there would be no stardard way to refer to a resource offered by a web server. URLs remove the ambiguity of addressing a resource, but at the cost of creating some rather formidable namespaces (e.g. https://addons.mozilla.org/en-US/firefox/addon/9549).

In general, long URLs aren’t a problem. Either through web page hyperlinks or web browser bookmarks, URLs fade into the background for most users. However, sometimes it is more convenient to have a shorter reference to a resource than the fully qualified URL. For example in the late nineties on IRC, it was common to see tiny.cc URLs pasted into chat rooms. Long URLs tend to clutter up already busy chat room windows. With the advent of text message-based systems like Twitter, which limit status updates to 140 characters, long URLs are actually consuming a valuable resource. The most common URL shortener used on Twitter.com appears to be bit.ly

There are several URL shortening services out there and they all work pretty much the same way. The user supplies the full URL. The service hashes the URL into something smaller and appends this to its own namespace. Using the bit.ly service, the mozilla URL becomes: http://bit.ly/g0Z9. When someone accesses this bit.ly URL, he will be seemlessly redirected to the original resource.

Bit.ly provides a REST interface to their service (API).
To use this, create an account on bit.ly’s system. Now you are ready to build a Perl REST client for the shorten service (http://api.bit.ly/shorten).

The following code is a listing of a small command line Perl script that expects to be passed a long URL. It uses the bit.ly REST service to return a shortened version.

use strict;
use LWP::UserAgent;
use Getopt::Std;
use HTTP::Request;
use URI;

my $VERSION = "1.0";
my $Opts = {};
my $bitly_api_url = q[http://api.bit.ly/shorten];
my $long_url = pop @ARGV;
getopts('u:p:?', $Opts);

if (!$long_url || $Opts->{'?'}) {
    print usage();
    exit;
}

set_defaults($Opts);

my $ua = LWP::UserAgent->new;
my $fetch_url = URI->new($bitly_api_url);
$fetch_url->query_form({'version' => "2.0.1",
                         'format'  => "xml",
                         'longUrl' => $long_url,
                     });
my $req = HTTP::Request->new(GET => $fetch_url);
$req->authorization_basic($Opts->{u} => $Opts->{p});

my $res = $ua->request($req);
if ($res->code == 200) {
    my ($url) = ($res->content 
       =~ m!([^<]+)!);
    unless ($url) {
        warn("FAIL: [". $res->content . "]\n");
    exit 1;
    }
    print "$url\n";
    exit;
} else {
    warn("FAIL:[".$res->content."]\n");
    exit 1;
}

#-----
# sub
#-----
sub usage {
    return <

OPTIONS

  ? - Display this screen
  u [USERNAME] - Bit.ly username
  p [PASSWORD] - Bit.ly password

EOT
}

sub set_defaults {
    my ($h) = @_;
    $h->{u} ||= "taskboy3000";
    $h->{p} ||= "s3c3rt";
}

This code uses the standard Perl module Getopt::Std to parse optinal command line arguments. The set_defaults function merely uses my bit.ly credentials if none are provided through optional parameters. Next, a new LWP::UserAgent object is created to make client HTTP calls. The bit.ly shorten service expects a GET request with optional arguments encoded as query parameters in the URL. The bit.ly service can respond to requests with data in various formats (e.g. XML, JSON). In this case, the format parameter is set to “xml.”

The URI class manages the extra parameters through the query_form method and urlencodes these into the new URL. A simple HTTP::Request object is passed the new URL and the bit.ly credentials are added to the HTTP request header using the authorization_basic method.

Once the HTTP request has all the information, it is ready to be sent to the bit.ly server. The HTTP::Request object is passed to the LWP::UserAgent::request method, which contacts the server and encodes the response as an HTTP::Response object.

If an error occurred in transmission, the response will have a HTTP status code other than 200. Even if the requests succeeds, the service might fail due to missing or bad credentials. A simple regex extracts the shortend URL from the XML message and reports on the command line for easy consumption by other command line tools.

This script will run on any platform supported by Perl.