What are some approaches to learning the Flickr API? My first suggestion is to look around the documentation and glance through the list of API methods here:
http://www.flickr.com/services/api/
While you are doing so, you should think back to all the things you know about Flickr
as an end user (aspects I discussed in Chapter 2) and see whether they are reflected in
the API. For example, can you come up with an API call to calculate the NSID of your own
account? What is a URL to return that information? Hint:
flickr.people.findByUsername
.
Perhaps the best way to learn about the API is to have a specific problem in mind and then let that problem drive your learning of the API. Don’t try to learn commit the entire API to memory—that’s what the documentation is for.
As I argued earlier, calls that require neither signing nor authorization (such as
flickr.photos.search
) are the easiest place to start. How would you
figure out which calls those are? You can make pretty good guesses from the names of
methods. For instance, you won’t be surprised that the method
flickr.photos.geo.setLocation
would need authorization: you would be
using it to change the geolocation of a photo, an act that would require Flickr to
determine whether you have the permission to do so. On the other hand, the method
flickr.groups.pools.getPhotos
allows you to retrieve photos for a given
group. A reasonably proficient Flickr user knows that there are public groups whose
photos would be visible to everybody, including those who are not logged in to Flickr at
all. Hence, it’s not surprising that this method would not require signing or
authorization.
You can get fairly far by eyeballing the list of Flickr methods for ones that do
not require any permission to execute. (Recall the levels of permissions within the
Flickr API: none, read
, write
, and delete
.)
It turns out that the Flickr API has a feature that you won’t find in too many
other web APIs: the Flickr API has methods that return information about
the API itself. flickr.reflection.?getMethods
returns a
list of all the Flickr methods available.
flickr.reflection.getMethodInfo
takes a given method name and
returns the following:
A description of the method
Whether the method needs to be signed
Whether the method needs to be authorized
The minimal permission level needed by the method (0 = none, 1 =
read
, 2= write
, 3=delete
)
The list of arguments for the method, including a description of the argument and whether it is optional
The list of possible errors arising from calling the method
For example, let’s look at what the Flickr API tells us about
flickr.photos.geo.setLocation
. You can use this format:
http://api.flickr.com/services/rest/?method= flickr.reflection.getMethodInfo&api_key={api-key}&method_name={method-name}
Specifically, you can use this:
http://api.flickr.com/services/rest/?method=flickr.reflection.getMethodInfo&api_key={api-key}&method_name=flickr.photos.geo.setLocation
to generate this:
<?xml version="1.0" encoding="utf-8" ?> <rsp stat="ok"> <method name="flickr.photos.geo.setLocation" needslogin="1" needssigning="1" requiredperms="2"> <description>Sets the geo data (latitude and longitude and, optionally, the accuracy level) for a photo. Before users may assign location data to a photo they must define who, by default, may view that information. Users can edit this preference at <a href="http://www.flickr.com/account/geo/privacy/">http://www.flickr.com /account/geo/privacy/</a>. If a user has not set this preference, the API method will return an error.</description> </method> <arguments> <argument name="api_key" optional="0">Your API application key. <a href="/services/api/misc.api_keys.html">See here</a> for more details.</argument> <argument name="photo_id" optional="0">The id of the photo to set location data for.</argument> <argument name="lat" optional="0">The latitude whose valid range is -90 to 90. Anything more than 6 decimal places will be truncated.</argument> <argument name="lon" optional="0">The longitude whose valid range is -180 to 180. Anything more than 6 decimal places will be truncated.</argument> <argument name="accuracy" optional="1">Recorded accuracy level of the location information. World level is 1, Country is ~3, Region ~6, City ~11, Street ~16. Current range is 1-16. Defaults to 16 if not specified.</argument> </arguments> <errors> <error code="1" message="Photo not found">The photo id was either invalid or was for a photo not viewable by the calling user.</error> <error code="2" message="Required arguments missing.">Some or all of the required arguments were not supplied.</error> <error code="3" message="Not a valid latitude.">The latitude argument failed validation.</error> <error code="4" message="Not a valid longitude.">The longitude argument failed validation.</error> <error code="5" message="Not a valid accuracy.">The accuracy argument failed validation.</error> <error code="6" message="Server error.">There was an unexpected problem setting location information to the photo.</error> <error code="7" message="User has not configured default viewing settings for location data.">Before users may assign location data to a photo they must define who, by default, may view that information. Users can edit this preference at <a href="http://www.flickr.com/account/geo/privacy/"> http://www.flickr.com/account/geo/privacy/</a></error> <error code="96" message="Invalid signature">The passed signature was invalid.</error> <error code="97" message="Missing signature">The call required signing but no signature was sent.</error> <error code="98" message="Login failed / Invalid auth token">The login details or auth token passed were invalid.</error> <error code="99" message="User not logged in / Insufficient permissions">The method requires user authentication but the user was not logged in, or the authenticated method call did not have the required permissions.</error> <error code="100" message="Invalid API Key">The API key passed was not valid or has expired.</error> <error code="105" message="Service currently unavailable">The requested service is temporarily unavailable.</error> <error code="111" message="Format "xxx" not found">The requested response format was not found.</error> <error code="112" message="Method "xxx" not found">The requested method was not found.</error> <error code="114" message="Invalid SOAP envelope">The SOAP envelope send in the request could not be parsed.</error> <error code="115" message="Invalid ?XML-?RPC Method Call">The ?XML-?RPC request document could not be parsed.</error> </errors> </rsp>
Note specifically that the following:
<method name="flickr.photos.geo.setLocation" needslogin="1" needssigning="1" requiredperms="2">
confirms what we had surmised—that it needs authorization and signing
because it requires a minimum permission level of write
. Compare that
to what we would get for flickr.photos.?search
, which is the method
that we have used throughout this chapter as an easy place to start in the
API:
<method name="flickr.photos.search" needslogin="0" needssigning="0" requiredperms="0">
These reflection methods give rise to many interesting possibilities, especially to those of us interested in the issue of automating and simplifying the way we access web APIs. Methods in the API are both similar and different from the other methods. It would be helpful to be able to query the API with the following specific questions:
What are all the methods that do not require any permissions to be used?
Which methods need to be signed?
What is an entire list of all arguments used in the Flickr API? Which method uses which argument? Which methods have in common the same arguments?
Caution | |
---|---|
These reflection methods in the Flickr API are useful only if they are kept
up-to-date and provide accurate information. In working with the
reflection APIs, I have run into some problems (for example, |
As a first step toward building a database of the Flickr API methods that would
support such queries, I wrote the following PHP script to generate a summary table
of the API methods. First there is a flickr_methods.php
class that has
functions to read the list of methods using flickr.methods.?get
Method
s
and, for each method, convert the data from
flickr.reflection.getMethodInfo
into a form that can be serialized
and unserialized from a local file.
<?php # flickr_methods.php # can use this class to return a $methods (an array of methods) and $methods_info -- # directly from the Flickr API or via a cached copy class flickr_methods { protected $api_key; public function __construct($api_key) { $this->api_key = $api_key; } public function test() { return $this->api_key; } # generic method for retrieving content for a given url. protected function getResource($url){ $chandle = curl_init(); curl_setopt($chandle, CURLOPT_URL, $url); curl_setopt($chandle, CURLOPT_RETURNTRANSFER, 1); $result = curl_exec($chandle); curl_close($chandle); return $result; } # return simplexml object for $url if successful with specified number of retries protected function flickrCall($url,$retries) { $success = false; for ($retry = 0; $retry < $retries; $retry++) { $rsp = $this->getResource($url); $xml = simplexml_load_string($rsp); if ($xml["stat"] == 'ok') { $success = true; break; } } // for if ($success) { return $xml; } else { throw new Exception("Could not successfully call Flickr"); } } # go through all the methods and list public function getMethods() { // would be useful to return this as an array (later on, I can have another // method to group them under common prefixes.) $url = "http://api.flickr.com/services/rest/?method=flickr.reflection.getMethods&api_key={$this->api_key}"; $xml = $this->flickrCall($url, 3); foreach ($xml->methods->method as $method) { //print "${method}\n"; $method_list[] = (string) $method; } return $method_list; } # get info about a given method($api_key, $method_name) public function getMethodInfo($method_name) { $url = "http://api.flickr.com/services/rest/?method=flickr.reflection.getMethodInfo&api_key={$this->api_key}&method_name={$method_name}"; $xml = $this->flickrCall($url, 3); return $xml; } # get directly from Flickr the method data # returns an array with data public function download_flickr_methods () { $methods = $this->getMethods(); // now loop to grab info for each method # this counter lets me limit the number of calls I make -- useful for testing $limit = 1000; $count = 0; foreach ($methods as $method) { $count += 1; if ($count > $limit) { break; } $xml = $this->getMethodInfo($method); $method_array["needslogin"] = (integer) $xml->method["needslogin"]; $method_array["needssigning"] = (integer) $xml->method["needssigning"]; $method_array["requiredperms"] = (integer) $xml->method["requiredperms"]; $method_array["description"] = (string) $xml->method->description; $method_array["response"] = (string) $xml->method->response; // loop through the arguments $args = array(); foreach ($xml->arguments->argument as $argument) { $arg["name"] = (string) $argument["name"]; $arg["optional"] = (integer) $argument["optional"]; $arg["text"] = (string) $argument; $args[] = $arg; } $method_array["arguments"] = $args; // loop through errors $errors = array(); foreach ($xml->errors->error as $error) { $err["code"] = (string) $error["code"]; $err["message"] = (integer) $error["message"]; $err["text"] = (string) $error; $errors[] = $err; } $method_array["errors"] = $errors; $methods_info[$method] = $method_array; } $to_store['methods'] = $methods; $to_store['methods_info'] = $methods_info; return $to_store; } // download_Flickr_API # store the data public function store_api_data($fname, $to_store) { $to_store_str = serialize($to_store); $fh = fopen($fname,'wb') OR die ("can't open $fname!"); $numbytes = fwrite($fh, $to_store_str); fclose($fh); } # convenience method for updating the cache public function update_api_data($fname) { $to_store = $this->download_flickr_methods(); $this->store_api_data($fname,$to_store); } # restore the data public function restore_api_data($fname) { $fh = fopen($fname,'rb') OR die ("can't open $fname!"); $contents = fread($fh, filesize($fname)); fclose($fh); return unserialize($contents); } } //flickr_methods ?>
This form of serialization in the flickr_method
class provides some
basic caching so that you don’t have to make more than 100 calls (one for each
method) each time you want to display a summary table—which is what the
following code does:
<?php require_once("flickr_methods.php"); $API_KEY = "[API_KEY]"; $fname = 'flickr.methods.info.txt'; $fm = new flickr_methods($API_KEY); if (!file_exists($fname)) { $fm->update_api_data($fname); } $m = $fm->restore_api_data($fname); $methods = $m["methods"]; $methods_info = $m["methods_info"]; header("Content-Type:text/html"); echo '<?xml version="1.0" encoding="utf-8"?>'; ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>Flickr methods</title> <meta ?http-?equiv="Content-Type" content="text/html;charset=utf-8" /> </head> <body> <table> <tr> <th>method name</th> <th>description</th> <th>needs login</th> <th>needs signing</th> <th>permissions</th> <th>args (mandatory)</th> <th>args (optional)</th> </tr> <?php foreach ($methods_info as $name=>$method) { $description = $method["description"]; # calc mandatory and optional arguments $m_args = ""; $o_args = ""; foreach ($method["arguments"] as $arg){ //print "arg: {$arg['name']}\n"; //print_r ($arg); // don't list api_key since it is mandatory for all calls if ($arg['name'] != 'api_key') { if ($arg["optional"] == '1') { $o_args .= " {$arg['name']}"; } else { $m_args .= " {$arg['name']}"; } } //if } print <<<EOT <tr> <td> <a href="http://www.flickr.com/services/api/{$name}.html">{$name}</a> </td> <td>{$description}</td> <td>{$method["needslogin"]}</td> <td>{$method["needssigning"]}</td> <td>{$method["requiredperms"]}</td> <td>{$m_args}</td> <td>{$o_args}</td> </tr> EOT; } ?> </table> </body> </html>
There’s certainly a lot more you can do with this code. Let me suggest a few ideas:
Store the data in a database (relational or XML) that can support a query language for making the queries that I listed earlier. (A poor person’s approach is to copy the output of the script into a spreadsheet and work from there.)
Create your own version of the Flickr API Explorer, perhaps as a desktop application, to help you learn about pieces of the API as you have specific questions.
Use the reflection
methods as the basis of a new
third-party API wrapper that is able to update itself as the API
changes.
Note | |
---|---|
In all the examples I have shown of the Flickr API, I have used HTTP
|