Programmatically Managing an Evergreen Facebook Marketing Campaign

Thursday, November 30, 2017: Cindy Rogers & Theodore Chao

ClearBrain imports customer data, generates predictions and then exports prediction metadata to different destinations. One of the first outbound marketing exports we built was for Facebook Custom Audiences.  The goal was for our customers to export users who are predicted to do an event on their site into Facebook's Custom Audiences so they could run targeted ads on those users.

The application seemed straightforward: allow users to authorize their Facebook accounts, authenticate a customer user token, and leverage APIs to import or export data. However, with so many possible use cases and hundreds of pages of documentation, we found it tricky for a developer to find what was relevant to building the core components of a Facebook application. We’ve written this blog post in hopes of illuminating a clearer path to navigating the Facebook Marketing API, including how to: implement Facebook Login, generate a Facebook User Token, find Ad Accounts, export hashed user info, and run batch data exports.

Creating a Facebook Login and Long-Term Access Token

The first step to creating a Facebook Marketing API integration is authenticating your customer’s user account. This will allow you to access their respective Ad Accounts in Facebook, and manage any of the advertising objects associated with that ad account.

The programmatic flow to authenticating your customer’s account involves 3 steps:

Load the Facebook SDK

Add the initialization code that Facebook has in their quick start guide. In the FB.init() function, the appId should be set programmatically for each domain the page can represent. An App is the method by which you access a Facebook Resource programmatically. Each domain must have a different Facebook App (e.g. localhost, production domain).

Create a Customer Login

Your users need to grant your Facebook App permission to view & manage their Marketing API Resources (eg. Ad Accounts). To prompt the user to grant your app permission, make a Login call to the javascript Facebook SDK and specify the respective permission. In the case of ClearBrain’s Facebook integration, we passed in the permission “ads_management”. Note: It’s possible that the user won’t accept the facebook permissions when you call login, so re-check which permissions you have for that user before attempting to access their resources.

          FB.login(function() {

                    //Executes after login call

          }, {scope:'ads_management'})

Get a Long-Term Access Token

A successful FB.login() call will you grant you access to a user’s short term access token through the FB.getAccessToken() function. You can securely send this short term token to a backend that hosts an App Secret for the Facebook App the user’s granted permission to.

This token is only valid for a short period of time. To maintain access to your user’s account for longer, use the Facebook Token Exchange API to exchange the short term token for a long term token that will last up to 60 days on a Basic Access level. The Standard access level grants longer lasting tokens.

Call to the /oauth/access_tokenendpoint on the Facebook Graph API. See the documentation for more details.

Managing Facebook Audience Exports Programmatically

Once you’ve setup your app privileges, you can leverage the Marketing API to manage your customer’s respective Ad Accounts.

In the case of ClearBrain, we specifically enable our customers to select an Ad Account and export a list of predicted users by hashed-emails to retarget in Facebook Ads. If you have a similar application that necessitates managing your customer’s Ad Accounts and Audiences, you can use the Facebook Marketing API in the following steps.

Get a List of User Ad Accounts

Your customers likely have multiple Ad Accounts. All facebook users have a personal Ad Account and then may have additional business Ad Accounts attached to their Facebook user. You want them to select the Business Ad Account associated with your app. Store the Ad Account your customer selects in a structure that can be accessed at the same time as the customers long term exchange token.

Accordingly to list their Ad Accounts to them you can make a Facebook Graph API adaccounts call:

          FB.api(`${id}/adaccounts`, {fields:['account_id', 'name']},function(resp){

                    //Check for resp.error

                    //Display returned ad accounts

                    //Data returned in reps.data is an array of {account_id, name}

          })

Send Hashed User Records to an Ad Account

Once connected to your customer’s Ad Account, you can programmatically upload a set of users for them to retarget via the Custom Audience endpoint. Facebook Custom Audience API consumes User Hashes, not email user records. Luckily, the Facebook SDK will take care of hashing emails into the user hashes necessary for the add and delete APIs if you pass it an array of emails.

We’ve written the service that interfaces with the custom audience API in Python, so we use the Python Facebook Ads SDK. You can use any of the SDKs they’ve listed in their documentation.

Initialize the Ad Account API context from the Ad Account Id you gathered from your customer. Since you’ve gathered the raw account Id from the user, you need to prepend act_ to the ID when accessing it.

          api FacebookAdsApi.init(access_token=access_token,timeout=60)  

                    # initialize facebook ads api

          Ad_account = AdAccount('act_'+ str(ad_account_id))

Finally, the server either creates or updates a custom audience. For creation, you can initialize the Custom Audience by referencing the customer Ad Account. Then name the Custom Audience with, and denote the audience will be of type: custom.

          audience = CustomAudience(parent_id=self.ad_account.get_id_assured())

          audience.update({

               CustomAudience.Field.name: name,

               CustomAudience.Field.subtype: CustomAudience.Subtype.custom

          })

Deleting & Creating New Audiences

If you want to update an existing Custom Audience in the future to retarget an updated list of users, you must calculate the delta between the new users and existing users in the audience.

You’ll likely want to edit the audience in place if you’re re-targeting during an active ad campaign. One alternative to editing an audience in place is to delete the existing custom audience and replace it with a new one. Keep in mind that when an audience drops to zero users, any associated ad campaigns will halt. A halted evergreen marketing campaign can cause unpredictable fluctuations in the results of the campaign. We worked with our customers and decided that instead of generating a long list of Custom Audiences over time, they’d prefer we update just one. We’ve described the approach for editing a Custom Audience in place here, but you could create a new custom audience by not implementing the delete step.

Editing a Custom Audience in place requires the deletion of some users and then insertion of others. Because there’s no way to read which emails are in a Custom Audience efficiently, you’ll have to track which users you previously added to the audience, delete them, and then insert additional users to the audience.

In these examples, users_batch is an array of user emails.

Deletion:

          audience.remove_users(CustomAudience.Schema.email_hash, users_batch, is_raw=True)

Creation:

          audience.add_users(CustomAudience.Schema.email_hash, users_batch)

In summary, this guide should help you build the first steps of your Facebook Marketing API Integration. You can use the Facebook SDK to create a user login, and generate a long-term access token. With your user’s token, you can programmatically access their respective Ad Accounts and create Custom Audiences of hashed-user records for retargeting. And over the long-term you can use delete/create calls to maintain those Audiences in Facebook.

Expect a follow up to this post about Testing, Submitting an App for Basic Access Facebook Review, and Other Findings soon!