Recover Bitly Bundle Data

For some years I've been creating a bundle of links covering articles I recommend for various topics in the Zend Certified Engineer Exam (if you can here looking for the bundle itself, it's at http://lornajane.net/zce-links-collection). This was done using bitly's bundles feature, which I thought was a great way to share links. In fact I had 10-15 bundles that I had created from collating all the links in a particular training course or talk, so that people didn't have to try to write down URLs as I went along. Unfortunately they sunset their bundles and then removed them completely, and I missed the announcement (it all happened quite quickly, they'd seen some abuse of the feature, it's free, all totally reasonable) BUT they also didn't respond to my support questions about how to recover the data.

It turns out, it's an undocumented feature on their API, so here is everything I know about recovering your bundle data, including the script I used to rescue my own data.

Bundle Data Via API

The endpoint you want is /v3/user/link_history and the documentation is here: http://dev.bitly.com/user_info.html#v3_user_link_history. Many MANY thanks to @jehiah for tweeting this information at me, it was a huge help! What they omitted to mention is that you'll also find a "tags" array in the values for each link as well (I assume the documentation will update really soon).

Bitly also has an API browser so you may be able to get your data out through that without needing code. I'm a programmer though so I wrote a script ...

Bitly Bundle Rescue Script

Here's my scratty script to export data. You need to do 3 things to use this:

  1. Add guzzlehttp/guzzle to your composer.json file and install it
  2. Use the instructions in the comment at the top to get your access token (or use the bitly web interface https://bitly.com/a/oauth_apps)
  3. Look around line 55 and comment/uncomment as appropriate depending on whether you want a CSV export or an HTML output
<?php

require "vendor/autoload.php";
/*
 * To request access token:
 * curl -u "username:password" -X POST "https://api-ssl.bitly.com/oauth/access_token"
 *
 * Composer: composer require "guzzlehttp/guzzle": "~5.3"
 */

$access_token = 'GenerateYourOwn';

$client = new GuzzleHttp\Client();
$offset = 0;
$limit = 100;


while(true) {
    $response = $client->get('https://api-ssl.bitly.com/v3/user/link_history',
        ["query" => [
            'access_token' => $access_token,
            'limit' => $limit,
            'offset' => $offset,
        ]]);
    if($response->getStatusCode() == 200) {
        $responseData = json_decode($response->getBody(), true);
        if(!$responseData) {
            echo "Something went wrong, we got: ";
            echo $response->getBody();
            exit;
        }


        // actual processing can begin now
        foreach($responseData['data']['link_history'] as $link) {
            // skip private links, probably don't want to publish these
            if($link['private']) {
                continue;
            }

            // skip archived links, presumably we don't need them
            if($link['archived']) {
                continue;
            }

            // skip records with no tags
            if(empty($link['tags'])) {
                continue;
            }

            // to search for a specific tag
            // if(!in_array("ZCE 5_5 Study Links", $link['tags'])) {
            //     continue;
            // }

            // uncomment for CSV output
            echo $link['link'] . ", " . $link['long_url'] . ", " . $link['title'] . ", " . implode(", ", $link['tags']) . "\n";

            // uncomment for HTML output
            // echo "<li><a href=\"" . $link&#91;'long_url'&#93; . "\" target=\"_blank\">" . $link['title'] . "</a>";
            // echo " (" . implode(", ", $link['tags']) . ")";
            // echo "</li>\n";


        }
        if($responseData['data']['result_count'] > ($offset + $limit)) {
            $offset += $limit;
        } else {
            // no more results - stop looping
            break;
        }
    } else {
        var_dump($response); exit;
    }
}

This isn't the most elegant in the world but it does recover your data which is all I really wanted. I ended up putting my links onto a basic page on my wordpress blog - at least then nobody else can unexpectedly turn it off! I don't know how widely used the bundles were but if you would like to get the data that you had stored there, hopefully this helps! Other tips, tricks, suggestions and so on are all very welcome - just leave me a comment.

Leave a Reply

Please use [code] and [/code] around any source code you wish to share.