Build Status

Zuck; use facebook's advertisement API with ruby

This is a little gem that makes access to facebook's ads API a little easier. Check out facebook's documentation for a nice diagram that explains how things work.

Usage

Not everything is supported yet. Here's what you can do currently.

Reading

# Let's set a default graph object with an access token
Zuck.graph = Koala::Facebook::API.new('my_access_token')

# Fetching all ad accounts associated to that user
accounts = Zuck::AdAccount.all

# Let's look at an account
my_account = accounts.first
=> #<Zuck::AdAccount id: "act_10150585630710217", account_id: "10150585630710217", name: "", account_status: 1, currency: "USD", timezone_id: 47, timezone_name: "Europe/Berlin", timezone_offset_hours_utc: 2, is_personal: 0, business_name: "Big Mike Alright UG (haftungsbeschr\u00e4nkt)", business_street: "Big Mike Alright UG (haftungsbeschr\u00e4nkt)", business_street2: "J\u00e4gerndorfer Zeile 61", business_city: "Berlin", business_state: "Berlin", business_zip: "12209", business_country_code: "DE", vat_status: 3, daily_spend_limit: 25000, users: [{"uid":501730216,"permissions":[1,2,3,4,5,7],"role":1001}], notification_settings: {"501730216":{"1000":{"1":1},"1001":{"1":1},"1002":{"1":1,"2":60},"1003":{"1":1,"2":60},"1004":{"1":1},"1005":{"1":1},"1006":{"1":1},"1009":{"1":1},"1010":{"1":1},"1011":{"1":1},"2000":{"1":1,"2":60},"2001":{"1":1,"2":60},"2002":{"2":60},"2003":{"1":1,"2":60},"2004":{"1":1,"2":60},"2005":{"1":1,"2":60},"3000":{"1":1,"2":60},"3001":{"1":1,"2":60},"3002":{"2":60},"3003":{"1":1,"2":60},"5000":{"1":1},"6000":{"1":1},"6001":{"1":1},"9000":{"1":1,"2":60},"8000":{"1":1,"2":60}}}, capabilities: [], balance: 0, moo_default_conversion_bid: 1000, moo_default_bid: 1000> 

# Aha. How do I access properties? The documented properties
# have getters:
my_account.currency
=> "USD"    

# But facebook also returns some non documented stuff
my_account[:moo_default_bid]
=> 1000

# Let's fetch the campaigns for this account
my_campaign = my_account.ad_campaigns.first

# Aha! Does this campaign have ad groups?
my_group = my_campaign.ad_groups.first
my_group.name
=> "Group names are silly"

# That was surprising. Just like the fact that ad groups
# have ad creatives associated to them:
my_creative = my_group.ad_creatives.first

# Let's go back up to the parent
my_creative.ad_group.name
=> "Group names are silly"

Writing

# Directly defining the creative as JSON
creative = '{"type":25,"action_spec":{"action.type":"like", "post":10150420410887685}}'

# Options for the ad group we want to create
o = { bid_type:  1,
      max_bid:   1,
      name:      "My first ad group",
      targeting: '{"countries":["US"]}',
      creative:  creative}

# Create it in the context of my_campaign
group = my_campaign.create_ad_group(o)
=> #<Zuck::AdGroup adgroup_id: 6005851390151, ad_id: 6005851390151, campaign_id: 6005851032951, name: "My first ad group", adgroup_status: 4, bid_type: 1, max_bid: "1", bid_info: {"1":"1"}, ad_status: 4, account_id: "10150585630710217", id: "6005851390151", creative_ids: [6005851371551], targeting: {"countries":["US"],"friends_of_connections":[{"id":"6005851366351","name":null}]}, conversion_specs: [{"action.type":"like","post":"10150420410887685"}], start_time: null, end_time: null, updated_time: 1343916568, created_time: 1343916568>

# Shoot, that was the wrong name
group.name = "My serious ad group"
group.save
=> true

# No wait, let's not spend money on facebook
group.destroy
=> true

# What does destroy mean? Changing the status!
group.ad_status
=> 3

AdInterest convenience methods

graph = Zuck.graph

# Search for keywords (to auto complete, for example) (yes, facebook sometimes returns ids as string and sometimes as numbers)
Zuck::AdInterest.search(graph, "Auto")
=>  [
      {:keyword=>"Auto", :id=>"6003156165433", :audience=>nil},
      {:keyword=>"#Automobile", :id=>6003176678152, :audience=>97900000},
      {:keyword=>"#Auto racing", :id=>6003146718552, :audience=>21800000},
      {:keyword=>"#Auto mechanic", :id=>6003109384433, :audience=>14600000}
    ]

# Quickly check if a keyword is valid
Zuck::AdInterest.validate(graph, '#Eminem')
=> {"#Eminem" => true}

# Quickly check a couple of keywords
Zuck::AdInterest.validate(graph, ['#Eminem', 'Wil Ferel', 'Bronson'])
=> {"#Eminem"=>true, "Bronson"=>true, "Wil Ferel"=>false}

# Make a best guess on how a keyword is called on Facebook
Zuck::AdInterest.best_guess(graph, 'Disney')
=> {:keyword=>"#The Walt Disney Company", :id=>6003270522085, :audience=>72500000}

# Sometimes a best guess does not return a keyword with a # prefix, and that
# means that we don't know the audience of that keyword:
Zuck::AdInterest.best_guess(graph, 'Moviepilot')
=> {:keyword=>"Moviepilot", :id=>6003327847780, :audience=>nil}

Supported objects

This gem supports basic CRUD on the objects of the facebook ads api. Here's a support chart:

Object .all .create .save .destroy parent.create_obj* Convenience methods**
Ad account ---
Ad account group ------
Ad campaign -
Ad creative -----
Ad group -
Ad image -----
Ad user ------

(*) This means that you can, for example, create a new ad group by calling my_campaign.create_ad_group(data) or not.

(**) Right now, everything goes right to facebook, but we'll want some convenience methods that tell you, for example, what ad_group.ad_status == 3 actually means

( ) These don't exist as their own objects in this gem but live in their parents. This means you can, for now, only read them:

Users don't exist as objects yet, but you can list all ad users of an account via my_ad_account.users and you will get an array of hashes.

To-Do

Add convenience stuff, right now everything is quite raw and directly sent over to facebook. Also, more tests directly to the api with a test user. Consolidate and test create code with other objects than AdGroup.