Found this useful? Love this post
Migrating Bitly to YOURLS

Migrating from Bitly to YOURLS

A little while ago, I needed to move a client off Bitly to YOURLS. YOURLS is a self-hosted URL shortening service similar to Bitly, but with much more flexibility. The main reasons we moved across to YOURLS was for:

  • The ability to have 1 character long keywords for short url. e.g.
  • Full customisation of application
  • Great range of plugins
  • Decent out-of-the-box statistics
  • No API limits (we were doing a lot of querying against Bitly and hitting API limits).

The biggest hurdle to jump when moving to our YOURLS installation was the migration of existing short urls that had been created inside Bitly. We considered at one point, just leaving everything older than 3 months behind and starting fresh. However the client had a lot referral traffic from sites like Pinterest and Tumblr which was a couple of years old in some cases.

Below is a quick and simple PHP script that loops through your Bitly account and scrapes the link history and then imports it into your YOURLS application.

It doesn’t pull the link history in as YOURLS (at the time of writing) doesn’t have a native stats endpoint to write statistics to; and understandably so, there are not many use cases that would warrant writing volumes of link stats to your installation.

Importing your Bitly links into YOURLS

* Code by Dom Sammut
* Cannot be redistributed as part of any paid package or product. Open Source only :)

define( 'YOURLS_HOST', '' ); 
define( 'BITLY_TOKEN', '###' ); 
define( 'YOURLS_SIGNATURE', '##' );
$bitly_result = TRUE;
$origin       = '' . BITLY_TOKEN . '&limit=50'; 
$offset       = 0; 

while ( $bitly_result === TRUE ) {

        // Init the CURL request 
        $ch = curl_init(); 

        // Setup the bitly api url including offset variable 
        curl_setopt( $ch, CURLOPT_URL, $origin . '&offset=' . (string) $offset ); 

        // Don't return headers in output 
        curl_setopt( $ch, CURLOPT_HEADER, 0 ); 

        // Return response of CURL as string 
        curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); 

        // Execute the request and reutrn to $response variable 
        $response = curl_exec( $ch );
        // Close the request 
        curl_close( $ch );
        // Decode JSON response 
        $response = json_decode( $response, TRUE ); 

        // Check to see if there are anymore bitly links. 
        // If not, set the $bitly_result to FALSE so we 
        // can exit the while loop. Else we increment 
         // the $offset by 50. 

        if ( $response['data']['link_history'] > 50 ) { 
	        $bitly_result = FALSE; 
        } else {
	        $offset += 50; 
	// Let's now loop through our $response data from Bitly
	// and push each link into YOURLS via the API.
	foreach ( $response['data']['link_history'] as $link ) { 

		// Get the short link and strip everything except
		// for the actual subdirectory path. E.G
		//> Ye0sez
		$shortlink = preg_split( "/^.*\//", $link['link'] ); 

		// Get the title as provided by Bitly
		$title = $link['title']; 

		// Get the full URL that the shortlink redirects to
		$long_url = $link['long_url']; 

		// Init the CURL session 
		$ch = curl_init(); 

		// Set the YOURLS endpoint
		curl_setopt( $ch, CURLOPT_URL, "http://" . YOURLS_HOST . '/yourls-api.php' ); 
		// No header in the result 
		curl_setopt( $ch, CURLOPT_HEADER, 0 ); 
		// Return, do not echo result 
		curl_setopt( $ch, CURLOPT_RETURNTRANSFER, TRUE ); 

		// This is a POST request 
		curl_setopt( $ch, CURLOPT_POST, 1 ); 

		// Data to POST 'format' => 'value',
		curl_setopt( $ch, CURLOPT_POSTFIELDS, array( 
			'url'       => $long_url,
			'keyword'   => $shortlink[1],
			'title'     => $title,
			'action'    => 'shorturl',
			'signature' => YOURLS_SIGNATURE
		) );

		// Fetch and return content
		$yourlsResponse = curl_exec( $ch );

		// Close CURL request
		curl_close( $ch );

		// You can do something with $yourlsResponse



Be sure to take a look at the Bitly Rate Limiting page. You potentially might need to batch this script and have it pull 1000 URLs per hour for example, in which case you’d just need to add a counter to your while loop or use the $offset variable.

Importing Stats

I haven’t shared the script for this component as it exposes private client information. However, we scraped basic click statistics from Bitly and then wrote them directly into the YOURLS database.

This component required multiple API calls for each link and was only completed for the top 500 URLS due to the time and value of the information. For the remaining URLs we simply pulled the total clicks.

We executed this component AFTER we had migrated our domain to the YOURLS installation to ensure we didn’t miss any data. We did it roughly 7 days after, to ensure we had ironed out any issues with the installation. When writing the statistical data, we combined it with any new data that had been collected.

Hopefully this helps you out a little!


You'll only get 1 email per month containing new posts (I hate spam as much as you!). You can opt out at anytime.


Leave a Reply

Your email address will not be published. Required fields are marked *

Preview Comment

1 Comment

  1. Tim Nolte

    Tim Nolte

    This is exactly the sort of thing I was looking for since Bitly has now changed their services, and their API is changing, and I really need to move to my own YOURLS instance to have more control. Is there any way to get a cleaned up version of the stats script as I would love to have something as a starting point, even if I need to fill in gaps. Thanks so much for sharing this!