Flickr Services API is nothing new, and if you're using PHP, ASP.NET or any of the other major languages, there is plenty of documentation available to get you started with putting Flickr functionality in your web application. If you're using PL/SQL, however, you're stuck to coming up with a solution yourself. I have come up with a simple package that should get you started with using the Flickr API in your PL/SQL programs.

NOTE This post was published in 2007. The Flickr API has likely changed significantly and as such the code below is almost guaranteed not to work. The technique for using the API (making a HTTP request, parsing the XML response) is still valid, so I've kept the post in case that is of any use to someone.

This package basically uses the Flickr API to search Flickr for the 25 most interesting photos related to the tag Ireland. It could easily be expanded to allow the user to search for images, display more than 25 photos, or to perform other API functions such as uploading images.

To use this example, you will need to get a Flickr API key and replace the your_api_key_goes_here string in the code below with the key that Flickr supply you. Getting a key is simple, free and only takes a minute. Go to http://flickr.com/services/api/keys/apply/ to get started.

pkg_flickr.pak

SET ESCAPE '\';

CREATE OR REPLACE PACKAGE pkg_flickr AS

    PROCEDURE display_page;
    PROCEDURE print_images(doc xmldom.DOMDocument);
    PROCEDURE handle_xml(xmlData CLOB);

END pkg_flickr;

pkg_flickr.bod

CREATE OR REPLACE PACKAGE BODY pkg_flickr AS

    PROCEDURE display_page IS
        v_api_key       VARCHAR2(100);
        v_method        VARCHAR2(100);
        v_tags          VARCHAR2(100);
        v_format        VARCHAR2(100);
        v_per_page      VARCHAR2(3);
        v_sort_by       VARCHAR2(100);
        v_url           VARCHAR2(255);
        v_pieces        utl_http.html_pieces;
        v_response      CLOB;
    BEGIN
        /* Set our Flickr Services API related Variables */
        v_api_key := htf.escape_url('your_api_key_goes_here');
        v_method := htf.escape_url('flickr.photos.search');
        v_tags := htf.escape_url('Ireland');
        v_format := htf.escape_url('rest');
        v_per_page := htf.escape_url('25');
        v_sort_by := htf.escape_url('interestingness-desc');

        /* Create URL based on Flickr Variables */
        v_url := 'http://api.flickr.com/services/rest/?api_key='
            ||v_api_key||'\&method='||v_method||'\&tags='||v_tags
            ||'\&format='||v_format||'\&per_page='||v_per_page
            ||'\&sort='||v_sort_by;
        /* Retrieve the data in pieces */
        v_pieces := utl_http.request_pieces(v_url, 100);
        /* Piece back together the data */
        FOR x IN 1..v_pieces.count LOOP
            v_response := v_response||v_pieces(x);
        END LOOP;

        htp.p('<html><head><title>Flickr Photos</title></head><body>');
        htp.p('<body><h1>Flickr Photos</h1><p>');
        /* Parse the XML Response */
        handle_xml(v_response);
        htp.p('</p></body></html>');
    END display_page;

    PROCEDURE print_images(doc xmldom.DOMDocument) IS
        nl              xmldom.DOMNodeList;
        len1            NUMBER;
        len2            NUMBER;
        n               xmldom.DOMNode;
        e               xmldom.DOMElement;
        nnm             xmldom.DOMNamedNodeMap;
        attrname        VARCHAR2(100);
        attrval         VARCHAR2100);
        id              VARCHAR2(100);
        owner           VARCHAR(100);
        secret          VARCHAR2(100);
        server          VARCHAR2(255);
        farm            VARCHAR2(255);
        title           VARCHAR2(255);
    BEGIN
        /* Get all <photo> tags */
        nl := xmldom.getElementsByTagName(doc, 'photo');
        len1 := xmldom.getLength(nl);        
        /* Loop through elements */
        FOR j IN 0..len1-1 LOOP
            n := xmldom.item(nl, j);
            e := xmldom.makeElement(n);       
            /* Get all attributes */
            nnm := xmldom.getAttributes(n);        
            IF (xmldom.isNull(nnm) = FALSE) THEN
                len2 := xmldom.getLength(nnm);
                /* Loop through attributes */
                FOR i IN 0..len2-1 LOOP
                    n := xmldom.item(nnm, i);
                    attrname := xmldom.getNodeName(n);
                    attrval := xmldom.getNodeValue(n);
                    /* The following if block gets the attributes we need */
                    IF attrname = 'id' THEN
                        id := attrval;
                    ELSIF attrname = 'owner' THEN
                        owner := attrval;
                    ELSIF attrname = 'secret' THEN
                        secret := attrval;
                    ELSIF attrname = 'server' THEN
                        server := attrval;
                    ELSIF attrname = 'farm' THEN
                        farm := attrval;
                    ELSIF attrname = 'title' THEN
                        title := attrval;
                    END IF;
                END LOOP;
                /* Build the image and the link */
                htp.p('<a href="http://www.flickr.com/photos/'||owner||'/'||id||'/">');
                htp.p('<img src="http://farm'||farm||'.static.flickr.com/'||server
                    ||'/'||id||'_'||secret||'_s.jpg" border="0" width="75" height="75" alt="'
                    ||title||'" title="'||title||'" /></a>');
            END IF;
        END LOOP;
    END print_images;

    PROCEDURE handle_xml(xmlData CLOB) IS
        p           xmlparser.parser;
        doc         xmldom.DOMDocument;        
    BEGIN        
        /* New parser */
        p := xmlparser.newParser;        
        /* Parse input file */
        xmlparser.parseclob(p, xmlData);
        /* Get document */
        doc := xmlparser.getDocument(p);
        /* Print the Images from Flickr */
        print_images(doc);        
        /* Deal with exceptions */
        EXCEPTION        
            WHEN xmldom.INDEX_SIZE_ERR THEN
                raise_application_error(-20120, 'Index Size error');        
            WHEN xmldom.DOMSTRING_SIZE_ERR THEN
                raise_application_error(-20120, 'String Size error');        
            WHEN xmldom.HIERARCHY_REQUEST_ERR THEN
                raise_application_error(-20120, 'Hierarchy request error');        
            WHEN xmldom.WRONG_DOCUMENT_ERR THEN
                raise_application_error(-20120, 'Wrong doc error');        
            WHEN xmldom.INVALID_CHARACTER_ERR THEN
                raise_application_error(-20120, 'Invalid Char error');        
            WHEN xmldom.NO_DATA_ALLOWED_ERR THEN
                raise_application_error(-20120, 'No data allowed error');        
            WHEN xmldom.NO_MODIFICATION_ALLOWED_ERR THEN
                raise_application_error(-20120, 'No mod allowed error');        
            WHEN xmldom.NOT_FOUND_ERR THEN
                raise_application_error(-20120, 'Not found error');        
            WHEN xmldom.NOT_SUPPORTED_ERR THEN
                raise_application_error(-20120, 'Not supported error');        
            WHEN xmldom.INUSE_ATTRIBUTE_ERR THEN
                raise_application_error(-20120, 'In use attr error');    
    END handle_xml;

END pkg_flickr;

If you find this helpful or would like some assistance with getting this code to work, please leave a comment. Code is provided as is, free of charge, with no support offered, although I will try my best to answer any questions you have.