Social media marketing is a multi-dimensional task.
Two key aspects to focus on include building engagement with new and existing customers, and growing brand visibility. A third is conversions.
As part of a multi-pronged social strategy, Social marketers should be consitently running social marketing initiatives which deliver measurable leads & conversions, and not just focusing on metrics (which are sometimes vanity metrics) which don’t directly map to business goals (eg retweets. reach).
Enter Social competitions – the magic bullet which – when executed well – is the trinty of engagement, visiblity and conversions.
Let’s consider some S,M.A.R.T Campaign Goals (Specific, Measurable, Achievable, Realistic, Time-based). What do we want to achieve, and how do we do it ?
Engagement – We will make the user perform some action which makes them interact with our Twitter account.
Conversions – We will make the user take action then prove it (Giving us their customer ID, username, etc.).
Visiblity – We will make the user post the status openly (not an @reply), and mention our brand and / or hashtags.
Competition – The prize must be something relevant and of value to our audience / target demographic.
A Freeroll is a poker tournament with no entry fee, but with a real-money prize-pool.
@888Poker runs a monthly $888 Freeroll. In order to get enter / get your seat in the tournament, you must first Tweet to enter (888 gets engagement, brand visiblity) and you must also tweet your 888 userID (so if you don’t already have one, you need to go to 888Poker and create an account.
New accounts = leads which can be nurtured to conversion [depositing customer] via smart CRM + email marketing, while for existing account holders, this initiative can also play a small part in an retention strategy.
888’s Landing page for the promotion is free of distractions, and well optimised for conversions and frictionless sharing:
If we do a search on Twitter advanced search for ‘I want to play in the $888 Twitter Freeroll’, it seems like there’s no shortage of people entering the competition, 24/7.

This is a superb example of a viral social competition, geared towards conversion, engagement and viibility by @888Poker.
Let’s try to quantify how effective this campaign actually is for 888Poker by doing using some graph theory a powerful social nework analysis tool (Nodexl Pro) to perform a social network analytis on Twitter interaction data we extract from the Twitter API (Tweets matching the search query ‘I want to play in the $888 Twitter Freeroll).

(you can also view the Graph on the NodeXL Graph Gallery here)
High Engagement – in just seven days 1361 Twitter accounts engaged / opted in to this promotion.
Reach & Visibility – potential reach of 107,489 (based on sum of followers of each vertext except the 888Poker vertext in the graph).
In fact, a separate analysis we performed of >1.5 Million ‘poker’related tweets (1st – 31st March 2017) showed 888 Poker in the top 10 most engaged Twitter accounts in the uber-competitive poker Niche.
Conversions – Each entrant is either an existing customer, or has created a new account and is entered into an email conversion funnel.
Cost – $888
So wouldn’t it be great if you could run competitions via on your own Twitter account, which delivered leads, customer engagement and brand visiblily – all on autopilot? Based on the data above, it seems like a high-ROI, low cost to win (as part of a balanced strategy of course).
Firstly We will use the PHP TwitterAuth API by Abraham library to interact with the Twitter API (once you have supplied it with your access tokens from your Twitter APP.)
We also need to set up and use a ‘Twitter app’ to get credentials to enable use to use the Twitter API. (Read our guide to setting up a Twitter app here).
We can use the Twitter API to get Tweet data from tweets that @mention our Twitter account.
Upon inspecting the Twitter API Documentation for Mentions it is evident that we can get 200 results at a time, and that we can use a since_id parameter to only get @mention tweets which were posted after the last time we used the API for a @mention search of our Twitter handle.
Our script will need access to storage (either a database or a csv file) in order to save data (Tweets, entries, usernames) and our since_id counter (which refers to the last tweet we returned via the API when our scheduler runs).
The returned data can be interpreted, and where apprporiate, we can save the data (for picking a winner, or for allocating entry tickets) or perform some other dynamic interaction (such as confirmation tweet, or DM).
Some Code:
function getMentions()
{
global $connection, $filename;
$params =[
'count' => 200,
];
$line=readlastline($filename); //Read last line of our tweets CSV, pull out the ID so we can use it in our API query
if ($line !="")
{
$aRow = explode(',',$line);
$params['since_id'] = $aRow[2];
echo "Retrieving all Tweet mentions since Tweet id: ".$aRow[2]."<br/>";
}
$result = $connection->get('statuses/mentions_timeline',$params,true);
//echo '<pre>'; print_r($result); echo '</pre>';
if (sizeof($result) >0)
{
if (!isset($result['errors']))
{
storeInteractionData($result);
echo "[OK] data processed. <br/>";
}
else
{
echo "[ERROR] Something went wrong.. Surprise attack killed him in his sleep that night.<br/>";
echo '<pre>'; print_r($result); echo '</pre>';
}
}
else
echo '[WARNING] No data to process. Do you not have any friends?<br/>';
}
We will store things to csv files, and we need to know the last tweetID we processed, which will be on the last line of the file:
//===============================================================================================================
function readlastline($fileName)
{
$t = "";
try
{
$fp = @fopen($fileName, "r");
if ( !file_exists($fileName) )
{
throw new Exception('File not found.');
}
$begining = fseek($fp, 0);
$pos = -1;
$nlcount = 0; //csv file ends in a \n so we need to parse to 2nd one..
while ($nlcount <2)
{
fseek($fp, $pos, SEEK_END);
if(ftell($fp) == $begining)
{
break;
}
$t = fgetc($fp);
if ($t == "\n")
$nlcount ++;
$pos = $pos - 1;
}
$t = fgets($fp);
fclose($fp);
}catch ( Exception $e )
{
//echo $e;
return '';
}
return $t;
}
//===============================================================================================================
Storing the data we need in a structured format to a CSV file.
function storeInteractionData($aTweetObjs)
{
$aTweetObjs= array_reverse($aTweetObjs);
global $filename;
$aTweets = [];
foreach ($aTweetObjs as $oTweet)
{
$a = [];
$a['created'] = $oTweet->created_at;
$a['timestamp'] = strtotime($oTweet->created_at);
$a['id'] = $oTweet->id;
$a['text'] = $oTweet->text;
$a['in_reply_to_tweet_id'] = $oTweet->in_reply_to_status_id_str;
$a['name'] = $oTweet->user->name;
$a['user_screen_name'] = $oTweet->user->screen_name;
$a['followers_count"'] = $oTweet->user->followers_count;
$aTweets[]=$a;
}
appendToCsv($aTweets, $filename);
}
function appendToCsv($aTweetObjs, $filename)
{
$fp = fopen($filename, 'a');
foreach($aTweetObjs as $values){
fputcsv($fp, $values);
}
fclose($fp);
}
Let’s update the above function to handle this, in a way that lets us add multiple filters, and specify output files for each filter, in addition to extracting data from the correct offset in any Tweet.
//===============================================================================================================
function storeInteractionData($aTweetObjs)
{
$aTweetObjs= array_reverse($aTweetObjs);
global $filename;
$aTweets = [];
foreach ($aTweetObjs as $oTweet)
{
$a = [];
$a['created'] = $oTweet->created_at;
$a['timestamp'] = strtotime($oTweet->created_at);
$a['id'] = $oTweet->id;
$a['text'] = $oTweet->text;
$a['in_reply_to_tweet_id'] = $oTweet->in_reply_to_status_id_str;
$a['name'] = $oTweet->user->name;
$a['user_screen_name'] = $oTweet->user->screen_name;
$a['followers_count"'] = $oTweet->user->followers_count;
$aTweets[]=$a;
}
appendToCsv($aTweets, $filename);
$aFilters = [
['file'=> getcwd().'/'.'888-freeroll.csv', 'search'=> 'i want to play in the $888 Twitter Freeroll', 'delimiter' =>'My @888poker username is ']
];
foreach ($aFilters as $aFilter)
{
foreach ($aTweetObjs as $oTweet)
{
$aResults=[];
if (strpos($oTweet->text, $aFilter['search']) !== FALSE )
{
$s= explode($aFilter['delimiter'], $oTweet->text);
$str = trim($s[1]);
$achunks= explode(" ", $str);
$id = $achunks[0];
//echo 'ID: '.$id.'<br/>';
$aResults[]= array ($ts =strtotime($oTweet->created_at), $twitID=$oTweet->user->screen_name, $id);
}
if (sizeof($aResults)>0)
{
appendToCsv($aResults, $aFilter['file']);
}
}
}
}
//===============================================================================================================
Executing this code at regular intervals (cron job) and all tweets to @yourhandle will get saved for future reference and analysis.
sample csv data
Time, Timestamp, Tweet_ID, text, inreplyto_tweet_id, display_name, screen_name, follower_coun "Wed Apr 26 18:07:19 +0000 2017",1493230039,857295039042637833,"@marketinghack3r looks good!",8572666295257088,"hutlersInc",hustlersinc123231,5375 "Wed Apr 26 23:51:11 +0000 2017",1493250671,857381574341533696,"My @888Poker Username is pokergirl2001 I want to play in the $888 Twitter Freeroll. ",,pokergirl,pokergirl12001,6252
If you added a filter like the one above, the competition data will be stored in the csv file specified (containing usernames, twitter IDs of competition entrants).
Timestamp, twitterID, 888_ID 1313250572, pokergirl2001, pokergirl2001 1469325901, jack, brokesoon 1493252922, marketinghack3r, bbv4l
Finally we need to set up a cron job to trigger the script at regular intervals. We can usually do this via hosting control panel.
<?php
/**
Social competition data extraction & Social Listening app for Twitter + PHP
(c) 2017 - https://marketinghacker.blog | twitter.com/marketinghack3r
**/
global $connection, $cwd, $filename;
require_once('twitteroauth/twitteroauth.php');
//===============================================================================================================
/** Fill in your Twitter APP Data https://marketinghacker.blog/blog/guide-creating-a-twitter-app-for-marketers/ **/
$cwd = getcwd();
require_once($cwd.'/twitteroauth-master/autoload.php'); //https://github.com/abraham/twitteroauth is your friend
use Abraham\TwitterOAuth\TwitterOAuth;
global $connection;
$filename = $cwd.'/'.'tweets.csv';
$connection = new TwitterOAuth(
$CONSUMER_KEY="INSERT-CONSUMER-KEY-HERE",
$CONSUMER_SECRET="INSERT-CONSUMER-SECRET-KEY-HERE",
$access_token="INSERT-ACCESS-TOKEN-HERE",
$access_token_secret="INSERT-ACCESS-TOKEN-SERET-HERE"
);
getMentions(); //Do Amazing Shit
//===============================================================================================================
function getMentions()
{
global $connection, $filename;
$params =[
'count' => 200,
];
$line=readlastline($filename);
if ($line !="")
{
$aRow = explode(',',$line);
$params['since_id'] = $aRow[2];
echo "Retrieving all Tweet mentions since Tweet id: ".$aRow[2]."<br/>";
}
$result = $connection->get('statuses/mentions_timeline',$params,true);
//echo '<pre>'; print_r($result); echo '</pre>';
if (sizeof($result) >0)
{
if (!isset($result['errors']))
{
storeInteractionData($result);
echo "[OK] data processed. <br/>";
}
else
{
echo "[ERROR] Something went wrong.. Surprise attack killed him in his sleep that night.<br/>";
echo '<pre>'; print_r($result); echo '</pre>';
}
}
else
echo '[WARNING] No data to process. Do you not have any friends?<br/>';
}
//===============================================================================================================
function storeInteractionData($aTweetObjs)
{
$aTweetObjs= array_reverse($aTweetObjs);
global $filename, $cwd;
$aTweets = [];
foreach ($aTweetObjs as $oTweet)
{
$a = [];
$a['created'] = $oTweet->created_at;
$a['timestamp'] = strtotime($oTweet->created_at);
$a['id'] = $oTweet->id;
$a['text'] = $oTweet->text;
$a['in_reply_to_tweet_id'] = $oTweet->in_reply_to_status_id_str;
$a['name'] = $oTweet->user->name;
$a['user_screen_name'] = $oTweet->user->screen_name;
$a['followers_count"'] = $oTweet->user->followers_count;
$aTweets[]=$a;
}
appendToCsv($aTweets, $filename);
$aFilters = [
['file'=> $cwd.'/'.'888-freeroll.csv', 'search'=> 'i want to play in the $888 Twitter Freeroll', 'delimiter' =>'My @888poker username is ']
];
foreach ($aFilters as $aFilter)
{
foreach ($aTweetObjs as $oTweet)
{
$aResults=[];
if (strpos($oTweet->text, $aFilter['search']) !== FALSE )
{
$s= explode($aFilter['delimiter'], $oTweet->text);
$str = trim($s[1]);
$achunks= explode(" ", $str);
$id = $achunks[0];
//echo 'ID: '.$id.'<br/>';
$aResults[]= array ($ts =strtotime($oTweet->created_at), $twitID=$oTweet->user->screen_name, $id);
}
if (sizeof($aResults)>0)
{
appendToCsv($aResults, $aFilter['file']);
}
}
}
}
//===============================================================================================================
function appendToCsv($aTweetObjs, $filename)
{
$fp = fopen($filename, 'a');
foreach($aTweetObjs as $values){
fputcsv($fp, $values);
}
fclose($fp);
}
//===============================================================================================================
function readlastline($fileName)
{
$t = "";
try
{
$fp = @fopen($fileName, "r");
if ( !file_exists($fileName) )
{
throw new Exception('File not found.');
}
$begining = fseek($fp, 0);
$pos = -1;
$nlcount = 0; //csv file ends in a \n so we need to parse to 2nd one..
while ($nlcount <2)
{
fseek($fp, $pos, SEEK_END);
if(ftell($fp) == $begining)
{
break;
}
$t = fgetc($fp);
if ($t == "\n")
$nlcount ++;
$pos = $pos - 1;
}
$t = fgets($fp);
fclose($fp);
}catch ( Exception $e )
{
//echo $e;
return '';
}
return $t;
}
//===============================================================================================================
?>