Skip to content

Commit

Permalink
Ads API v7 (#251)
Browse files Browse the repository at this point in the history
* Removed scoped timeline endpoint

* added granular tap placements

* added advertiser business categories endpoint

* updated serving_status to entity_status for media creatives

* replaced reach estimate with audience summary

* added audience summary and tests
  • Loading branch information
tushdante authored Mar 17, 2020
1 parent a092b78 commit 2820327
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 30 deletions.
40 changes: 40 additions & 0 deletions examples/audience_summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from twitter_ads.client import Client
from twitter_ads.targeting import AudienceSummary

CONSUMER_KEY = 'your consumer key'
CONSUMER_SECRET = 'your consumer secret'
ACCESS_TOKEN = 'access token'
ACCESS_TOKEN_SECRET = 'access token secret'
ACCOUNT_ID = 'account id'

# initialize the client
client = Client(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET)

# load the advertiser account instance
account = client.accounts(ACCOUNT_ID)

# targeting criteria params
params = {
"targeting_criteria": [
{
"targeting_type":"LOCATION",
"targeting_value":"96683cc9126741d1"
},
{
"targeting_type":"BROAD_KEYWORD",
"targeting_value":"cats"
},
{
"targeting_type":"SIMILAR_TO_FOLLOWERS_OF_USER",
"targeting_value": "14230524"
},
{
"targeting_type":"SIMILAR_TO_FOLLOWERS_OF_USER",
"targeting_value": "90420314"
}
]
}

audience_summary = AudienceSummary.load(account=account, params=params)

print (audience_summary.audience_size)
14 changes: 14 additions & 0 deletions tests/fixtures/audience_summary.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"request": {
"params": {
"targeting_criteria": null,
"account_id": "2iqph"
}
},
"data": {
"audience_size": {
"min": 41133600,
"max": 50274400
}
}
}
63 changes: 63 additions & 0 deletions tests/test_audience_summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import responses
import unittest

from tests.support import with_resource, with_fixture, characters

from twitter_ads.account import Account
from twitter_ads.client import Client
from twitter_ads.targeting import AudienceSummary
from twitter_ads import API_VERSION


@responses.activate
def test_audience_summary():
responses.add(responses.GET,
with_resource('/' + API_VERSION + '/accounts/2iqph'),
body=with_fixture('accounts_load'),
content_type='application/json')

responses.add(responses.POST,
with_resource('/' + API_VERSION + '/accounts/2iqph/audience_summary'),
body=with_fixture('audience_summary'),
content_type='application/json')

client = Client(
characters(40),
characters(40),
characters(40),
characters(40)
)

account = Account.load(client, '2iqph')

params = {
"targeting_criteria": [
{
"targeting_type":"LOCATION",
"targeting_value":"96683cc9126741d1"
},
{
"targeting_type":"BROAD_KEYWORD",
"targeting_value":"cats"
},
{
"targeting_type":"SIMILAR_TO_FOLLOWERS_OF_USER",
"targeting_value": "14230524"
},
{
"targeting_type":"SIMILAR_TO_FOLLOWERS_OF_USER",
"targeting_value": "90420314"
}
]
}

audience_summary = AudienceSummary.load(
account=account,
params=params
)

print (audience_summary)
assert audience_summary is not None
assert audience_summary.audience_size is not None
assert audience_summary.audience_size['min'] == 41133600
assert audience_summary.audience_size['max'] == 50274400
4 changes: 2 additions & 2 deletions twitter_ads/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (C) 2015 Twitter, Inc.

VERSION = (6, 1, 0)
API_VERSION = '6'
VERSION = (7, 0, 0)
API_VERSION = '7'

from twitter_ads.utils import get_version

Expand Down
18 changes: 0 additions & 18 deletions twitter_ads/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from twitter_ads.enum import TRANSFORM
from twitter_ads.http import Request
from twitter_ads.cursor import Cursor
from twitter_ads.utils import Deprecated
from twitter_ads import API_VERSION

from twitter_ads.resource import resource_property, Resource
Expand All @@ -28,7 +27,6 @@ class Account(Resource):
RESOURCE_COLLECTION = '/' + API_VERSION + '/accounts'
RESOURCE = '/' + API_VERSION + '/accounts/{id}'
FEATURES = '/' + API_VERSION + '/accounts/{id}/features'
SCOPED_TIMELINE = '/5/accounts/{id}/scoped_timeline'

def __init__(self, client):
self._client = client
Expand Down Expand Up @@ -156,22 +154,6 @@ def video_website_cards(self, id=None, **kwargs):
"""
return self._load_resource(VideoWebsiteCard, id, **kwargs)

@Deprecated('This method has been deprecated as of version 5'
'and no longer works in the latest version.')
def scoped_timeline(self, *id, **kwargs):
"""
Returns the most recent promotable Tweets created by the specified Twitter user.
"""
self._validate_loaded()

params = {'user_id': id}
params.update(kwargs)

resource = self.SCOPED_TIMELINE.format(id=self.id)
response = Request(self.client, 'get', resource, params=params).perform()

return response.body['data']


# account properties
resource_property(Account, 'id', readonly=True)
Expand Down
11 changes: 11 additions & 0 deletions twitter_ads/campaign.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,3 +470,14 @@ class IabCategories(Resource):
resource_property(IabCategories, 'id', readonly=True)
resource_property(IabCategories, 'name', readonly=True)
resource_property(IabCategories, 'parent_id', readonly=True)


class AdvertiserBusinessCategories(Resource):

PROPERTIES = {}
RESOURCE_COLLECTION = '/' + API_VERSION + '/advertiser_business_categories'


resource_property(ContentCategories, 'id', readonly=True)
resource_property(ContentCategories, 'name', readonly=True)
resource_property(ContentCategories, 'iab_categories', readonly=True)
2 changes: 1 addition & 1 deletion twitter_ads/creative.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class MediaCreative(Analytics, Resource, Persistence):
resource_property(MediaCreative, 'created_at', readonly=True, transform=TRANSFORM.TIME)
resource_property(MediaCreative, 'deleted', readonly=True, transform=TRANSFORM.BOOL)
resource_property(MediaCreative, 'id', readonly=True)
resource_property(MediaCreative, 'serving_status', readonly=True)
resource_property(MediaCreative, 'entity_status', readonly=True)
resource_property(MediaCreative, 'updated_at', readonly=True, transform=TRANSFORM.TIME)
# writable
resource_property(MediaCreative, 'account_media_id')
Expand Down
7 changes: 6 additions & 1 deletion twitter_ads/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,12 @@ def enum(**enums):
ALL_ON_TWITTER='ALL_ON_TWITTER',
TWITTER_SEARCH='TWITTER_SEARCH',
TWITTER_TIMELINE='TWITTER_TIMELINE',
PUBLISHER_NETWORK='PUBLISHER_NETWORK'
PUBLISHER_NETWORK='PUBLISHER_NETWORK',
TAP_FULL='TAP_FULL',
TAP_FULL_LANDSCAPE='TAP_FULL_LANDSCAPE',
TAP_BANNER='TAP_BANNER',
TAP_NATIVE='TAP_NATIVE',
TAP_MRECT="TAP_MRECT"
)

PRODUCT = enum(
Expand Down
25 changes: 17 additions & 8 deletions twitter_ads/targeting.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,28 @@
"""Container for all targeting related logic used by the Ads API SDK."""

from twitter_ads.http import Request
from twitter_ads.resource import resource_property, Resource, Persistence
from twitter_ads import API_VERSION
from twitter_ads.utils import FlattenParams
import json


class ReachEstimate(object):
class AudienceSummary(Resource, Persistence):
PROPERTIES = {}

RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/reach_estimate'
RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/audience_summary'

@classmethod
def fetch(klass, account, product_type, objective, user_id, **kwargs):
params = {'product_type': product_type, 'objective': objective, 'user_id': user_id}
params.update(kwargs)

@FlattenParams
def load(klass, account, params):
resource = klass.RESOURCE.format(account_id=account.id)
response = Request(account.client, 'get', resource, params=params).perform()
headers = {'Content-Type': 'application/json'}
response = Request(account.client,
'post',
resource,
headers=headers,
body=json.dumps(params)).perform()
return klass(account).from_response(response.body['data'])


return response.body['data']
resource_property(AudienceSummary, 'audience_size')

0 comments on commit 2820327

Please sign in to comment.