WPSuperDealer

Going off Track

We been keeping busy

Let’s pull back the masks a bit and talk about how we spend our time. Yes, we work with a lot of companies building websites for people and we develop a lot of WordPress PlugIns, some of those in the vehicle sales industries.

At first glance you might be tempted to think we spend all our time working on WordPress and vehicle sales. The truth is, we go way beyond WordPress development – it’s really just one small piece of a pretty awesome jigsaw puzzle and vehicle sales is more of a passion industry for us than anything.

Most of our focus is on stretching what’s possible when it comes to digital marketing. This means pulling in massive amounts of data, doing a bunch of crunchy stuff with it and shooting out the results or performing a series of automations based on the data. We love automations.

We mention this because most of this post is dedicated to some overly technical code type stuff that isn’t related to WordPress and might be beneficial to those who work in fields beyond vehicle sales.

If you want to know more about what we get involved with and lead generation is the kind of thing that gets your interest then pop over to WPSuperTeam.com and say “Hi!”

Brass Tacks

We love data and the story it tells. These days there’s a handful of companies that have a huge amount of control over certain kinds of data. One of those wonderful companies is Alphabet, formerly and forever known as Google.

A fair portion of what we do involves AdWords, Analytics and a few of their other services. 

We were recently delighted to discover that their old “AdWords API” was going away and was being replaced by the fancy new “Google Ads API”. This means a whole new set of client libraries. Having worked with their previous, giant sized libraries I can say that I personally was not overly excited about their new ones. Since we have a large code base using their old “AdWords API” it means we’ve spent a lot of time recently migrating all those tools to the new API.

I’ll spare you the saga of installing the new libraries, a hassle of it’s own, because we had no sooner installed the new libraries when we noticed their new API appeared to have significant improvements to their REST API interface. This means we had the choice to ditch the libraries and try migrating our top secret tools (shh!) using their REST API.

We needed the REST

We decided to give their REST API interface a try and whoever lead that particular project at Google should get some kind of hero award because it’s a bazillion times better than those libraries. Turns out we really needed the REST.

It’s far from perfect and it’s quite different from most REST APIs. The actual documentation, once you get into it and try doing stuff, is pretty sparse. That’s why we’re going to spend the rest of this post looking at some code samples and exploring the right way (well, “right” is more opinion based – let’s call it the easiest functional way we’ve found thus far) to use PHP to access the Google Ads REST API.

Sit back, grab some seaweed snacks or a carrot or something and let’s look at how small the code is when compared to those giant client libraries (the stuff of nightmares I tell you).

Getting an Access Token

To make any calls to the REST API you need your Client ID, Client Secret and a refresh token.

If you don’t know how to get a refresh token and have no clue what Client ID or Client Secret we’re talking about then you’ll need to look elsewhere, this post is going to focus on events specific to the REST API and using PHP to do some talking. If you’re not familiar with that stuff then there’s some great resources out there and once you get that sorted out you can come back here and bask in more of our gibberish.

We’ll go ahead and start with using a Refresh token to get an Access token. All these tokens! Oi, it can be confusing and kind of a pain, but only if you let it. Just remember that the refresh token lets you “refresh” the access token and you need the access token to “access” anything useful in the Google Ads REST API.

With that in mind let’s start building a function to get us the access token.

				
					    //= assume we're dropping this into a class and things like $this->access_token are already defined
    //= yes, using $this-> is probably not the best way to go about this, but $this-> doesn't matter to the overall point
    private function get_access_token() {
        //= if we already have an auth token then just return it
        if ( $this->access_token ) {
            return $this->access_token;
        }

        //= get our refresh token
        //= remember that whole thing about how you'd need that before we got into the code? yeah, that would be here.
        $refresh_token = $this->refresh_token;

        //= Gratis to the real Heroes
        //= curl from: https://developers.google.com/google-ads/api/rest/auth
        //= PHP Generated by curl-to-PHP: http://incarnate.github.io/curl-to-php/
        $ch = curl_init();

        //= MAke sure you're sending things to the right oauth URL to get the token
        curl_setopt( $ch, CURLOPT_URL, 'https://www.googleapis.com/oauth2/v3/token' );
        curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
        curl_setopt( $ch, CURLOPT_POST, 1 );
        
        //= This is where you'll need your Client ID and Client Secret
        curl_setopt( $ch, CURLOPT_POSTFIELDS, "grant_type=refresh_token&client_id=" . $this->client_id . "&client_secret=" . $this->client_secret . "&refresh_token=" . $refresh_token );

        $headers = array();
        $headers[] = 'Content-Type: application/x-www-form-urlencoded';
        curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );

        $result = curl_exec( $ch );

        if ( curl_errno( $ch ) ) {
            //= If there's an error echo it
            //= You should probably actually handle the errors, but that's on you ;-)
            echo 'Error:' . curl_error( $ch ); //= Introducing lazy error management - ta da!
        }
        curl_close( $ch );
        
        //= Decode the results - there's actually a lot of stuff here, you can var_dump to check it out
        //= Personally I like arrays in PHP, not sure why - but that's why the second parameter in json_decode() is true
        $results_array = json_decode( $result, true );
        
        //= Set our variable to the access_token
        $this->access_token = $results_array['access_token'];
        
        //= Return our access token so we can use it in our next call
        return $results_array['access_token'];
    }
				
			

Using the Access Token

You now have the power to get an access token. With this you can start making all kinds of calls to the Google Ads REST API. You can get data, add data, mutate data and more mwah-ha-ha!!!!

Before we start using it to attach laser beam labels to shark ads let’s start with something simple, sending a call to get some data.

We’re going to build this into a few different functions, that way you can reuse some of them. This will probably make more sense if we skip the small talk and dip back into the code.

Go ahead and flip down your code reading goggles and get some hot tea, or coffee if you’re into bean juice, and let’s get to it (well, you can’t look at code unless you’re comfortable right?).

Start with making a Query

At the core of all your calls for data is your query. Gone are the days of AWQL and welcome GAQL the new query language for access data with the new Google Ads API.

Google was kind enough to supply us with several GAQL Builders that take the pain out of building queries.

Here’s a link to the more important builder:

The Super Cool GAQL Builder Builder

Depending on the data you want to read or mutate (that’s Google’s fancy terminology for creating and updating) you might need to use one of the other builders. Once you click that link above you can look in the left hand menu and find all the other GAQL Builders that are available.

Here’s a function we’ll use to return our query.

				
					    private function query() {
        //= Build your query for ad_group_ad data here
        //= https://developers.google.com/google-ads/api/fields/v10/ad_group_ad_query_builder
        $sql .= "SELECT	";
            $sql .= "ad_group_ad.ad.type, ";
            $sql .= "campaign.name, ";
            $sql .= "ad_group.name, ";
            $sql .= "ad_group_ad.ad.id, ";
            $sql .= "ad_group_ad.ad.name, ";
            $sql .= "ad_group_ad.ad.resource_name, ";
            $sql .= "metrics.active_view_ctr, ";
            $sql .= "metrics.clicks, ";
            $sql .= "metrics.conversions, ";
            $sql .= "metrics.impressions, ";
            $sql .= "metrics.cost_per_conversion, ";
            $sql .= "metrics.cost_micros, ";
            $sql .= "metrics.cost_per_all_conversions, ";
            $sql .= "metrics.ctr ";
        $sql .= "FROM ad_group_ad ";
        $sql .= "WHERE ";
            $sql .= "ad_group_ad.status = 'ENABLED'	";
            $sql .= "AND ad_group.status = 'ENABLED' ";
            $sql .= "AND campaign.status = 'ENABLED' ";
            $sql .= "AND segments.date BETWEEN '" . $this->args['pastdate'] . "' AND '" . $this->args['yesterday'] . "'";
        return $sql;
	}
				
			

The Meat and Taters Function

The query is nice and all, but by itself it’s kind of worthless. Doesn’t really do much but return a string. A really nice string, but still just a string.

If we want that query to do something then we need another function. For the sake of simplicity let’s call it get_data().

In a perfect world we’d setup parameters to adjust the CURLOPT_URL and the query, then we could reuse this function over and over again for all of our GAQL calls. But for the sake of illustration we’ve dropped those inside the function so you can see them being called.

If you’re not accessing MCC data then you can skip assigning the $login_custom_id variable and you can pull that line out of the header.

Other than that it’s pretty straight forward. You send your developer token in the header along with the access token. The query itself is sent as a json string as part of the POSTFIELDS.

You should receive a json response that you can then decode into an array (or object) to do as you please.

We should point out that you should probably add some error handling to it and maybe clean it up a little to match your code style.

You may have noticed some annoying habits in the code samples (spaces before and after parens, excessive use of variables when not needed, etc). This particular style of code is great for explaining things but it is far from the most efficient method.

Assuming you got your variables setup correctly then you should be able to pull any data you need for the Google Ads API while using their REST interface.

				
					    private function get_data() {
        //= Don't forget you need that access token
        $token = $this->get_token();
        
        $client_id = $this->client_id;
        
        //= If you're accessing MCC data then you need to send you MCC customer ID
        $login_customer_id = $this->adwords_account_key();
        //= FYI - you need to send it without the dashes ;-)
        $login_customer_id = str_replace( '-', '', $login_customer_id );

        //= Create a variable to store our query
        //= This is the general format of the json you'll send in the POSTFIELDS
        $query = '{
                "query": "' . $this->query() . '"
            }';
        
        //= Start your curl code
        $curl = curl_init();
        curl_setopt_array($curl, array(
            CURLOPT_URL => 'https://googleads.googleapis.com/v10/customers/' . $client_id . '/googleAds:search',
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => '',
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 0,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => 'POST',
            CURLOPT_POSTFIELDS => $query,
            CURLOPT_HTTPHEADER => array(
                'developer-token: ' . DEVELOPER_TOKEN,
                'login-customer-id: ' . $login_customer_id,
                'Authorization: Bearer ' . $token,
                'Content-Type: application/json'
            ),
        ));

        //= This will have the response json in it
        $response = curl_exec( $curl );

        curl_close( $curl );
        
        //= Decode the json and make it an array (or object if you're so inclined)
        $json = json_decode( $response, true );
        
        //= Is there an error?
        if ( isset( $json['error'] ) ) {
            //= do stuff with errors
        }
        
        //= return our json so we can do wicked cool stuff with it
        return $json;
    }
				
			

Rise of the Mutates!

The Google Ads REST API is not CRUD and is quite different from a “standard” REST API. This means things like creating and updating records are done in a strange and unexpected fashion.

You don’t update data, you “Mutate” it. You don’t create data, you “Mutate” it.

It’s all about the Mutate baby! 

The key here is getting the right URL and using the proper format for your POSTFIELDS.

It starts off the same as our earlier request, but the URL is different – in this case we’re adding /labels:mutate to the end of it.

The “query” we send in this example is a json string containing a single “create” operation. You can actually string together a bunch of different operations all together, assuming they go to the same URL. Could be quite the time saver, so make sure you consider that when building advanced logic.

You’ll see we have a variable for label and color, with label pulling double duty as the description (because I’m really lazy).

That’s really all there is to it. All we’re really changing is the “query” that gets sent as the POSTFIELDS and then making sure we’re using the correct URL.

To be honest, the hardest part is figuring out what the right URL is. The documentation can be somewhat confusing on this and in a few cases a bit misleading and hitting the top level resource is no help. So depending on what you want to do that might be your biggest hang up.

Getting the formatting right on the POSTFIELDS isn’t too bad. If you’re hitting the right URL then the error messages, in a fantastic move on Google’s part, actually provide very clear directions on what went wrong. Honest, it’s one of the shining stars of their REST API and made it easy enough that even I figured it out.

				
					//= create a label in the customer's account if it doesn't exist
private function create_label( $label ) {
//= Gotta do that token thing
$token = $this->get_token();
//= This is the customer id for the customer you're mutating
$customer_id = self::$client_id;
$login_customer_id = $this->adwords_account_key();
//= Get rid of those pesky hyphens
$login_customer_id = str_replace( '-', '', $login_customer_id );

//= I like color and so does your label
$color = '#4baf4f';

$curl = curl_init();

//= Let's build this query here, instead of another function so you can see what it looks like with everything else
$query = '{
        "operations": [{
            "create": {
                "name": "' . $label . '",
                "text_label": {
                    "background_color": "' . $color . '",
                    "description": "' . $label . '"
                }
            }
        }]
    }';

//= Start your curl with the URL that mutates
curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://googleads.googleapis.com/v10/customers/' . $customer_id . '/labels:mutate',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => '',
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 0,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS => $query,
    CURLOPT_HTTPHEADER => array(
        'developer-token: ' . DEVELOPER_TOKEN,
        'login-customer-id: ' . $login_customer_id,
        'Authorization: Bearer ' . $token,
        'Content-Type: application/json'
    ),
));

$response = curl_exec( $curl );

curl_close( $curl );

$json = json_decode( $response, true );
return $json;
}
				
			

Updating an Ad with the Google REST API

Here we go true believers, the moment of truth. Can we take what we’ve learned and use it to update an ad? Let’s do two things, let’s add a label (like the one we created above) to the ad and then let’s pause it. First we gotta make sure we’re hitting the right URL. To ad a label to an ad we’ll want to hit the adGroupAdLabels resource and “mutate” it in the URL like this:
'https://googleads.googleapis.com/v10/customers/' . $customer_id . '/adGroupAdLabels:mutate'
The $customer_id variable would be the customer’s AdWords account ID without any hyphens in it. Next you’ll want to build out your “query” for the POSTFIELDS. We create an operation and send it a “create” with the AdGroupAd resource name and the Label resource name to link the two together. Here’s a tip, the AdGroupAd resource name isn’t what you might expect. It’s in this format, where the first number is the AdGroup ID and the second number is the Ad ID. Then you put them together with a tilda ~ symbol.
customers/8634059911/adGroupAds/42440864656~433227627090 (adGroupId~adId)

Put together the “query” would look like this:

				
					        $query = '{
                "operations": 
                [{
                    "create": {
                        "adGroupAd": "' . $ad_resource . '",
                        "label": "' . $label_resource . '"
                    }
                }]
            }';
				
			

If you use the function above, where we created the label, and you swap out the URL and the Query (plus clean out a few unused variables ~ I’m looking at you $color) and you should be able to modify it to add your label to an ad.

Once you’ve added the label you can work on pausing the ad.

Your URL for that is going to be:

'https://googleads.googleapis.com/v10/customers/' . $customer_id . '/adGroupAds:mutate'

Your “query” will look like this – we have to send it an “updateMask” to tell it what fields we’re updating and then we send the field with it’s new value along with the resourceName:

				
					        $query = '{
                "operations": 
                [{
                    "updateMask": "status",
                    "update": {
                        "resourceName": "' . $ad_resource_id . '",
                        "status": "PAUSED"
                    }
                }]
            }';
				
			

If you put all that together into a function it would look somewhat similar to this:

				
						public static function pause_ad( $adGroupId, $adId ) {
        $token = $this->get_token();
        //= No Dashes
        $customer_id = str_replace( '-', '', self::$client_id );
        $login_customer_id = $this->adwords_account_key();
        //= Didn't we just say no dashes?!
        $login_customer_id = str_replace( '-', '', $login_customer_id );

        //= Make a variable to build the resource name for the AdGroupAd
        $ad_resource_id = 'customers/' . $customer_id . '/adGroupAds/' . $adGroupId . '~' . $adId;

        //= Build our "query"
        $query = '{
                "operations": 
                [{
                    "updateMask": "status",
                    "update": {
                        "resourceName": "' . $ad_resource_id . '",
                        "status": "PAUSED"
                    }
                }]
            }';

        $curl = curl_init();
        
        curl_setopt_array($curl, array(
            CURLOPT_URL => 'https://googleads.googleapis.com/v10/customers/' . $customer_id . '/adGroupAds:mutate',
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => '',
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 0,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => 'POST',
            CURLOPT_POSTFIELDS => $query,
            CURLOPT_HTTPHEADER => array(
                'developer-token: ' . DEVELOPER_TOKEN,
                'login-customer-id: ' . $login_customer_id,
                'Authorization: Bearer ' . $token,
                'Content-Type: application/json'
            ),
        ));

        //= Enjoy the json response and make sure you handle any potential errors ;-p
        $response = curl_exec( $curl );

        curl_close( $curl );

        return $response;
	}
				
			

There you have it

Now you know all about the Google Ads API and using the REST interface. You’ve also learned we spend a lot of time tinkering around on things that aren’t WordPress specific. They’re not even vehicle sales specific, but they are right at the root of digital marketing and are a core part of the Super Powers we bring to our clients.

If you’d like to know more about the wicked sick powers we’ve been graced with then pop over to our contact us page and let us know.

 

Stay Awsome & Enjoy!