Introduction to Application Programming Interfaces

Published:
Updated:
The Confluence of Individual Knowledge and the Collective Intelligence
At this writing (summer 2013) the term API has made its way into the popular lexicon of the English language.  A few years ago, the term would only be recognized by oil companies and a few geeky programmers.  But today, the term gives relevance and meaning to the "rise of the machines."  The explosion of online storage and computing power has given us a host of new applications that perform highly valuable, highly specialized functions, and that enable direct machine-to-machine communication.  The output from these applications can be used by other applications to deliver rich internet application experiences that are customized and personalized.  The information from these functions can underpin business decisions in advertising and marketing, in shipping and transportation, in medical diagnosis, and many other data-intensive endeavors.

In the context of our discussion here, when we refer to "API" we mean "web API" -- specifically the collection and dissemination of information via HTTP protocols.  These APIs let servers talk to each other in ways that build powerful online applications with relatively little effort for the developers.

Governments and companies like Google, Yahoo, Weather.com, MapQuest, UnderTone Networks, and many others gather, analyze, store, collate and publish detailed information on a huge variety of topics.  As the publishers, these entities own the copyright in this information, and it is their prerogative to organize and publish their data in any form and for any price that the market may bear.  When they publish the data in a form that can be consumed by an external automated process, the publishing vehicle is called an Application Programming Interface.  It is understood that the publishers are making their data available in a predictable and well-documented format, so that it can be copied and used by other computers.  It is also understood that these online publications are permission-based and may require an API key as well as the payment of a subscription fee.  In discussing APIs we will refer to the parties as the publisher and the consumer.

What Makes a Great API
The folks over at Programmable Web have this article about API best practices.  And not surprisingly, there are API worst practices, too.

The "Interface" in the API
The API represents a sort of contract between the publisher and the consumer.  It exists as a common boundary between two separate systems, and it will define what kind of requests can be made and what kind of responses are appropriate.  This "interface" tells the software developers on both sides what to expect.  As such, a clear definition is important for a successful API.  The definition can be as complex as necessary to create elaborate and detailed responses or as simple as a request that gives a single stock symbol and a response that returns the current price per share.  The API consumer initiates the communication over the interface (a "request"), and the publisher answers with a "response."  The interface hides details on both sides of this cooperative effort, making available only the data that is needed to publish and consume.  The method of communication between the parties is defined by a protocol or software architecture.  I would also add that the communication should be defined by a Version.  More on version follows below.  Let's look at a protocol that had some inherent advantages, but was too difficult for popular use, and an architecture that is simplistic, but favors convention over configuration, and is wildly popular.

SOAP - a Bad Idea
"SOAP" is the abbreviation for Simple Object Access Protocol, an interface that has fallen into disuse because of its unnecessary complexity.  SOAP consists of several layers of specifications, protocol bindings and message processing models, most of which added little value to the process of information interchange.  It originated in the late 1990s and by 2005 had been mostly abandoned by the major web service providers.  Some legacy systems and bureaucratic entities are probably still using SOAP, but most organizations that are smart about client relationships have moved on to REST.

REST - a Good Idea
"REST" is the abbreviation for Representational State Transfer.  REST has emerged as the design model for modern web APIs.  In marked contrast to SOAP, REST not a protocol, but a style of software architecture that is amazingly simple to use.  REST is based on the client/server protocol.  In this design clients make requests and servers make responses.  Each request is atomic, complete, and stateless, and each response is complete and usually instantaneous.  One example of a simple system based on REST is the world-wide web.  You type a URL into a browser address bar and you get back a web page.  What could be easier?

Let's deconstruct these terms of art a little bit.  Atomic means that the request and the response are both the smallest and largest elements of communication over the API.
Complete tells us that the request or response is a standalone entity - no other information is needed to fulfill the communication.  Stateless means that the publisher need not carry or "remember" any additional information about the consumer; each request is its own entity, independent of every other request.

The response from a RESTful API could be as simple as a plain text character string or a string of comma-separated values ("CSV").  It's more likely to be a structured response, probably an XML document or JSON string.

Increasingly, API publishers have turned to JSON strings.  JSON has a little richer vocabulary than XML, and is more structurally compact since it does not use the closing tags required in XML.

WebService APIs
Now that we know the basic terms of art surrounding APIs we can begin to look at how they work in practice.  Let's say you're a developer at a major department store chain and one day the marketing manager comes into your office with an interesting idea.

"I think that weather may be having an influence on our same-store clothing sales."  "If I could tap into that knowledge, we could control inventory more accurately and avoid having excess sale items that have to be discounted at the end of the season."  Before you can think about this for a moment, she interrupts your train of thought.  "Last year we sold all of our clothing at an average of 85% of list price."  "With 12.4 billion dollars in sales, every one percent improvement in sales price throws off more than a hundred million dollars in free cash, straight to the bottom line."

Your eyes widen.  

She says, "I have minute-by-minute cash register receipts for every department and every store, but I don't know how to get the weather data.  Can you help?"

Knowing about weather all over the country minute by minute?  Easy!  You can choose one or two of the many Weather APIs and begin gathering the information that will make this "big-data" question easy to answer.

"Oh, by the way," she says as she turns for the door, "the CEO says if we can pull this off, we split a 10% bonus.  Your share is $5,000,000.  Let's get to work!"

Here is another example.  What if your company sells a product or service that cannot be delivered over the internet?  You will want to give people accurate directions to the nearest stores.  A Mapping API might be used to draw a map in real time, showing the client the best way to get there.  Several web services offer free and paid mapping and geocoding APIs.

What if you wanted to build a specialized dating web site like jDate or Christian Mingle?  There is a lot of work involved!  If you could shorten the path from concept to success, that would be great.  Fortunately someone else has thought of this and has already built the generalized solution for some important of the parts of an online dating profile, complete with photographer support.  Here is a link:
http://www.perfectdatingprofile.com/api/

Consuming APIs via PHP
With a well-designed RESTful API, your response can be gotten by simply reading from the URI.  This example works with the "get last name" API that is shown below.  Yes, it really is that easy!
 
$answer = file_get_contents('http://path/to.php?key=ABC&name=Ray');
                      var_dump($answer);

Open in new window


Unfortunately, it's not always that easy.  PHP file_get_contents() will "hang" if the URI is unavailable or does not present a response.  This hang condition will cause the hang time to be added to the script execution time.  A typical symptom of this failure is a timeout after 30 seconds.  If you want to avoid this risk, you can use cURL to read from the URI.  CURL allows you to set a timeout value so that your script can resume operation even if the URI has nothing to say.
 
<?php
                      /**
                       * Demonstrate the basics of cURL GET-method request
                       * http://curl.haxx.se/libcurl/c/libcurl-errors.html
                       */
                      error_reporting(E_ALL);
                      
                      
                      // READ FROM AN API WITH cURL
                      $response = new Get_Response_Object('http://path/to.php?key=ABC&name=Ray');
                      
                      // SHOW THE ERROR MESSAGE OR THE SUCCESS WORK PRODUCT
                      if (!$response->document) var_dump($response);
                      echo htmlentities($response->document);
                      
                      
                      Class GET_Response_Object
                      {
                          public $href, $title, $http_code, $errno, $info, $document;
                      
                          public function __construct($href, $get_array=[], $title=NULL)
                          {
                              // ACTIVATE THIS TO AVOID TIMEOUT FOR LONG RUNNING SCRIPT
                              // set_time_limit(10);
                      
                              // STORE THE CALL INFORMATION
                              $this->href  = $href;
                              $this->title = $title;
                      
                              // PREPARE THE GET STRING
                              $get_string = http_build_query($get_array);
                              if ($get_string) $get_string = '?' . $get_string;
                      
                              // MAKE THE REQUEST
                              if (!$this->my_curl($href, $get_string))
                              {
                                  // ACTIVATE THIS TO SEE THE ERRORS AS THEY OCCUR
                                  // trigger_error("Errno: $this->errno; HTTP: $this->http_code; URL: $this->href", E_USER_WARNING);
                              }
                          }
                      
                          protected function my_curl($url, $get_string, $timeout=3)
                          {
                              // PREPARE THE CURL CALL
                              $curl = curl_init();
                      
                              // HEADERS AND OPTIONS APPEAR TO BE A FIREFOX BROWSER REFERRED BY GOOGLE
                              $header[] = "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
                              $header[] = "Cache-Control: max-age=0";
                              $header[] = "Connection: keep-alive";
                              $header[] = "Keep-Alive: 300";
                              $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
                              $header[] = "Accept-Language: en-us,en;q=0.5";
                              $header[] = "Pragma: "; // BROWSERS USUALLY LEAVE THIS BLANK
                      
                              // SET THE CURL OPTIONS - SEE http://php.net/manual/en/function.curl-setopt.php
                              curl_setopt( $curl, CURLOPT_URL,            $url . $get_string  );
                              curl_setopt( $curl, CURLOPT_USERAGENT,      'Mozilla/5.0 (Windows NT 6.1; rv:22.0) Gecko/20100101 Firefox/22.0'  );
                              curl_setopt( $curl, CURLOPT_HTTPHEADER,     $header  );
                              curl_setopt( $curl, CURLOPT_REFERER,        'http://www.google.com'  );
                              curl_setopt( $curl, CURLOPT_ENCODING,       'gzip,deflate'  );
                              curl_setopt( $curl, CURLOPT_AUTOREFERER,    TRUE  );
                              curl_setopt( $curl, CURLOPT_RETURNTRANSFER, TRUE  );
                              curl_setopt( $curl, CURLOPT_FOLLOWLOCATION, TRUE  );
                              curl_setopt( $curl, CURLOPT_TIMEOUT,        $timeout  );
                              curl_setopt( $curl, CURLOPT_VERBOSE,        TRUE   );
                              curl_setopt( $curl, CURLOPT_FAILONERROR,    TRUE   );
                      
                              // SET THE LOCATION OF THE COOKIE JAR (WILL BE OVERWRITTEN)
                              curl_setopt( $curl, CURLOPT_COOKIEFILE,     'cookie.txt' );
                      		curl_setopt( $curl, CURLOPT_COOKIEJAR,      'cookie.txt' );
                      
                              // IF USING SSL, THIS INFORMATION MAY BE IMPORTANT
                              // http://php.net/manual/en/function.curl-setopt.php#110457
                              // http://php.net/manual/en/function.curl-setopt.php#115993
                              // http://php.net/manual/en/function.curl-setopt.php#113754
                              // REDACTED IN 2015 curl_setopt( $curl, CURLOPT_SSLVERSION, 3 );
                              curl_setopt( $curl, CURLOPT_SSL_VERIFYHOST, FALSE  );
                              curl_setopt( $curl, CURLOPT_SSL_VERIFYPEER, FALSE  );
                      
                              // RUN THE CURL REQUEST AND GET THE RESULTS
                              $this->document  = curl_exec($curl);
                              $this->errno     = curl_errno($curl);
                              $this->info      = curl_getinfo($curl);
                              $this->http_code = $this->info['http_code'];
                              curl_close($curl);
                      
                              // RETURN DOCUMENT OR FALSE ON CURL FAILURE
                              return $this->document;
                          }
                      }

Open in new window


When APIs Fail (hint: this will happen to you)
If your script depends on any external service, you must decide whether you're willing to treat the service as a black box and live or die on its terms, or whether you will try to create a work-around when the external service fails.  The reason this is worthy of careful consideration is pretty simple: the external service will fail eventually, in the same way that the power will go off, or a car won't start.  Ask yourself if you need a fallback plan.  Here are some of the things you might want to consider.

Is contemporaneous access to the API a necessity for my application?  Let's say your application is time-and-location dependent.  If there is a delay in the response, will the response be useless?  If that's the case, there may not be any point in trying to offer a soft failure mechanism.  Just tell the client that your service is down.  Nobody wants to hear that, but there really may not be any good answer.

Is there a good-enough fallback?  An example might be an application that calls an API that retrieves a geocode using the client IP address, and records these IP:geocode pairs in a data base (something like Maxmind or GEOIO comes to mind).  If the geocoding API is unavailable, what have you lost?  That would depend on the exact use of the data base.  If you're using it to draw a map of client locations, you have only lost one data point on the map, and this is not a very severe loss.  Certainly it is not something that should render your application unavailable.

In this latter case, you might create a soft-failure design.  Rather than having your script call the API directly, you might have your script start an asynchronous process, using cURL (POST method) or fsockopen().  The asynchronous process can call the API to update your data base.  If the asynchronous process fails, it does not cause a direct failure of your script.  Your script can rely on the data base to draw the map, and it will almost always be nearly perfect.

Publishing Your Own API in PHP
If you've got something important to say, or a well-built application that can be generalized for use by others, you may want to publish your own RESTful API.  This code snippet shows you how it's done.  Although the programming and example are very simple, the principles are common to all RESTful APIs.  You can test your RESTful API by typing a URL and looking at the browser output with "view source."

In this example, we accept three input arguments.  One of these is the API key.  A key is often required for API services, and this shows how the key is included in the request.  The second argument tells the API how to format the output.  If it's present and says "XML" the API will return the data in an XML document instead of a plain text.  The third argument is the input data, which in this case is a given name.  These input arguments can be in any order.  The API looks up the family name from the given name and returns the family name.

This is not a very sophisticated API.  It draws only upon its own data model.  And it does not provide a versioned response, but it illustrates the elegant simplicity of a RESTful design.
 
<?php // demo/REST_get_last_name.php
                      error_reporting(E_ALL);
                      
                      // DEMONSTRATE HOW A RESTFUL WEB SERVICE WORKS
                      // INPUT FIRST NAME, OUTPUT FAMILY NAME
                      // PHP CALLING EXAMPLE -- READ THE URI:
                      // file_get_contents('http://iconoun.com/demo/REST_get_last_name.php?key=ABC&resp=XML&name=Ray');
                      
                      // OUR DATA MODEL CONTAINS ALL THE ANSWERS - THIS COULD BE A DATA BASE - AS SIMPLE OR COMPLEX AS NEEDED
                      $dataModel
                      = array
                      ( 'Brian'   => 'Portlock'
                      , 'Ray'     => 'Paseur'
                      , 'COBOL'   => 'Dinosaur'
                      , 'Dave'    => 'Baldwin'
                      )
                      ;
                      
                      // RESPONSE CAN BE PLAIN TEXT OR XML FORMAT
                      $alpha = NULL;
                      $omega = NULL;
                      
                      // NORMALIZE AND TEST THE "resp=" ARGUMENT
                      if ( (isset($_GET["resp"])) && (strtoupper(trim($_GET["resp"])) == 'XML') )
                      {
                          // PREPARE THE XML WRAPPER
                          $alpha = '<?xml version="1.0" encoding="utf-8" ?>' . PHP_EOL . '<response>' . PHP_EOL;
                          $omega = PHP_EOL . '</response>';
                      }
                      
                      // TEST THE 'API KEY' - THIS COULD BE A DATA BASE VALIDATION LOOKUP - AS SIMPLE OR COMPLEX AS NEEDED
                      $key = (!empty($_GET["key"])) ? $_GET["key"] : FALSE;
                      if ($key !== 'ABC')
                      {
                          die( $alpha . 'INVALID API KEY' . $omega );
                      }
                      
                      // LOOKUP THE FAMILY NAME
                      $name = (!empty($_GET["name"])) ? $_GET["name"] : 'MISSING name= PARAMETER';
                      
                      // IF THE NAME FROM THE URL IS FOUND IN THE DATA MODEL
                      if (array_key_exists($name, $dataModel))
                      {
                          // RETURNS THE APPROPRIATE FAMILY NAME FROM THE DATA MODEL
                          die( $alpha . $dataModel[$name] . $omega );
                      }
                      else
                      {
                          // RETURNS THE UNKNOWN NAME INDICATOR
                          die( $alpha . "$name (UNKNOWN)" . $omega );
                      }

Open in new window


Using "Versioning" for Your API
When you are ready to publish your API, be sure to put the script into a version directory.  The reason for this goes to the relationship between the publishers and the consumers of the service.  As a publisher you want the maximum flexibility to grow and extend the value of your API.  But as a consumer, you want a consistent and stable service, one that never changes.  These competing ideals are best handled by establishing and maintaining separate versions for each new release of the API.

Versioning can be handled simply using URL paths on the server, something like this
path/to/APIv1?q=args
path/to/APIv2?q=args

You should maintain at least one test version, too.  If your script breaks, none of your consumers are hurt!
path/to/APIvX?q=args

Your published response should always include the version number of your API.  Here is a code example showing how to include the version and error information in the response document.  The response can be either XML or JSON.
 
<?php // demo/REST_version.php
                      error_reporting(E_ALL);
                      
                      
                      // DEMONSTRATE HOW A WEB SERVICE CAN PROVIDE ITS VERSION NUMBER IN THE RESPONSE
                      // PHP CALLING EXAMPLE -- READ THE URI:
                      // file_get_contents('http://iconoun.com/demo/REST_version.php?key=ABC&resp=JSON');
                      
                      
                      // PREPARE THE XML WRAPPER
                      $xml = <<<EOD
                      <?xml version="1.0" encoding="utf-8" ?>
                      <response>
                      </response>
                      EOD;
                      
                      // MAKE AN OBJECT FROM THE XML
                      $obj = SimpleXML_Load_String($xml);
                      
                      // ADD VERSION NUMBER
                      $obj->addChild('version');
                      $obj->version = '1.0';
                      
                      // ADD ERROR INFORMATION SHOWING NO ERRORS
                      $obj->addChild('error');
                      $obj->error->addChild('code');
                      $obj->error->code = 0;
                      $obj->error->addChild('message');
                      
                      // NORMALIZE AND TEST THE API KEY
                      $key = (!empty($_GET['key'])) ? strtoupper(trim($_GET['key'])) : NULL;
                      if ($key != 'ABC')
                      {
                          // IF THE KEY FAILS, SET AN ERROR INDICATOR
                          $obj->error->code    = 'Fatal';
                          $obj->error->message = 'Invalid API Key';
                      
                          // NO FURTHER PROCESSING - JUST RESPOND
                          goto PrepResponse;
                      }
                      
                      // ADD API RESPONSE INFORMATION (EXAMPLE: ABOUT A PERSON)
                      $obj->addChild('person');
                      $obj->person->addChild('name');
                      $obj->person->addChild('email');
                      $obj->person->name  = 'Ray';
                      $obj->person->email = 'Ray.Paseur@Gmail.com';
                      
                      
                      // PREPARE THE RESPONSE DOCUMENT FROM THE OBJECT
                      PrepResponse:
                      
                      // NORMALIZE AND TEST THE RESPONSE TYPE
                      $resp = (!empty($_GET['resp'])) ? strtoupper(trim($_GET['resp'])) : NULL;
                      
                      // CHOOSE JSON IF REQUESTED OR XML IF NOT
                      if ($resp == 'JSON')
                      {
                          $out = json_encode($obj);
                      }
                      else
                      {
                          $out = $obj->AsXML();
                      }
                      
                      // RETURN THE RESPONSE
                      echo $out;

Open in new window


Sending a Deprecation Notice in the Response Document
When you want to urge your consumers to move along to the most current version of your API, you can easily include a "nudge" in the content of your response document.  See line 53 of this snippet where we add the deprecation notice.
 
<?php // demo/REST_deprecation.php
                      error_reporting(E_ALL);
                      
                      
                      // DEMONSTRATE HOW A WEB SERVICE CAN PROVIDE A DEPRECATION NOTICE IN THE RESPONSE
                      // PHP CALLING EXAMPLE -- READ THE URI:
                      // file_get_contents('http://iconoun.com/demo/REST_deprecation.php?key=ABC&resp=JSON');
                      
                      
                      // PREPARE THE XML WRAPPER
                      $xml = <<<EOD
                      <?xml version="1.0" encoding="utf-8" ?>
                      <response>
                      </response>
                      EOD;
                      
                      // MAKE AN OBJECT FROM THE XML
                      $obj = SimpleXML_Load_String($xml);
                      
                      // ADD VERSION NUMBER
                      $obj->addChild('version');
                      $obj->version = '2.4';
                      
                      // ADD ERROR INFORMATION SHOWING NO ERRORS
                      $obj->addChild('error');
                      $obj->error->addChild('code');
                      $obj->error->code = 0;
                      $obj->error->addChild('message');
                      
                      // NORMALIZE AND TEST THE API KEY
                      $key = (!empty($_GET['key'])) ? strtoupper(trim($_GET['key'])) : NULL;
                      if ($key != 'ABC')
                      {
                          // IF THE KEY FAILS, SET AN ERROR INDICATOR
                          $obj->error->code    = 'Fatal';
                          $obj->error->message = 'Invalid API Key';
                      
                          // NO FURTHER PROCESSING - JUST RESPOND
                          goto PrepResponse;
                      }
                      
                      // ADD API RESPONSE INFORMATION (EXAMPLE: ABOUT A PERSON)
                      $obj->addChild('person');
                      $obj->person->addChild('name');
                      $obj->person->addChild('email');
                      $obj->person->name  = 'Ray';
                      $obj->person->email = 'Ray.Paseur@Gmail.com';
                      
                      
                      // PREPARE THE RESPONSE DOCUMENT FROM THE OBJECT
                      PrepResponse:
                      
                      // BECAUSE THIS API IS DEPRECATED, WE ADD A NOTICE TO THE ERROR INFORMATION FIELDS
                      $obj->error->code    = 'Notice';
                      $obj->error->message = 'This version of the API is Deprecated; please upgrade to Version 3';
                      
                      // NORMALIZE AND TEST THE RESPONSE TYPE
                      $resp = (!empty($_GET['resp'])) ? strtoupper(trim($_GET['resp'])) : NULL;
                      
                      // CHOOSE JSON IF REQUESTED OR XML IF NOT
                      if ($resp == 'JSON')
                      {
                          $out = json_encode($obj);
                      }
                      else
                      {
                          $out = $obj->AsXML();
                      }
                      
                      // RETURN THE RESPONSE
                      echo $out;

Open in new window


Conclusion
This article has shown us how to use PHP to publish and consume information over the Application Programming Interface.  APIs exist today for an amazingly large and ever-growing set of information and applications, from Facebook and Twitter, to weather, mapping and monitoring software.  As we develop our applications it's often a good idea to ask, "Where will my data come from?" with a view toward using the many machine-to-machine resources on the internet.  

Looking to the future, it's easy to envision APIs that could prepare legal documents, alert investors to unwanted market trends, interpret medical results and more.  As the publisher of an API, you might want to keep all of the information you receive from every API request.  If your service is popular, the requests will be numerous and the request/response data may provide the next "big data" that reveals a valuable trend.  As but one example of trends in API data, it was recently discovered that data from the Twitter API could predict the outcome of elections!

References and Further Reading
A Step Beyond: Mashups
An example of how difficult it can be to use SOAP
An example of a fairly good explanation of an API
An example of an API question and answer that completely misses the point
Link to the JavaScript Mapquest API
What happens when an API is disconnected

http://en.wikipedia.org/wiki/REST
http://json.org/
http://www.programmableweb.com/

Please give us your feedback!
If you found this article helpful, please click the "thumb's up" button below. Doing so lets the E-E community know what is valuable for E-E members and helps provide direction for future articles.  If you have questions or comments, please add them.  Thanks!
 
6
9,573 Views

Comments (4)

CERTIFIED EXPERT
Most Valuable Expert 2013

Commented:
Great stuff! I voted YES for helpful above!
Top Expert 2013

Commented:
I +1ed

Any article that uses my name for test data is bound to be a winner. ;^)

Seriously a good clear starter for something that can get really complex and easily messed up.

Cd&
APD TorontoSoftware Developer

Commented:
Good Explanation!
APD TorontoSoftware Developer

Commented:
Good Explanation!

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.