Skip to content

Commit 2820327

Browse files
authored
Ads API v7 (#251)
* 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
1 parent a092b78 commit 2820327

9 files changed

+154
-30
lines changed

examples/audience_summary.py

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from twitter_ads.client import Client
2+
from twitter_ads.targeting import AudienceSummary
3+
4+
CONSUMER_KEY = 'your consumer key'
5+
CONSUMER_SECRET = 'your consumer secret'
6+
ACCESS_TOKEN = 'access token'
7+
ACCESS_TOKEN_SECRET = 'access token secret'
8+
ACCOUNT_ID = 'account id'
9+
10+
# initialize the client
11+
client = Client(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
12+
13+
# load the advertiser account instance
14+
account = client.accounts(ACCOUNT_ID)
15+
16+
# targeting criteria params
17+
params = {
18+
"targeting_criteria": [
19+
{
20+
"targeting_type":"LOCATION",
21+
"targeting_value":"96683cc9126741d1"
22+
},
23+
{
24+
"targeting_type":"BROAD_KEYWORD",
25+
"targeting_value":"cats"
26+
},
27+
{
28+
"targeting_type":"SIMILAR_TO_FOLLOWERS_OF_USER",
29+
"targeting_value": "14230524"
30+
},
31+
{
32+
"targeting_type":"SIMILAR_TO_FOLLOWERS_OF_USER",
33+
"targeting_value": "90420314"
34+
}
35+
]
36+
}
37+
38+
audience_summary = AudienceSummary.load(account=account, params=params)
39+
40+
print (audience_summary.audience_size)

tests/fixtures/audience_summary.json

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"request": {
3+
"params": {
4+
"targeting_criteria": null,
5+
"account_id": "2iqph"
6+
}
7+
},
8+
"data": {
9+
"audience_size": {
10+
"min": 41133600,
11+
"max": 50274400
12+
}
13+
}
14+
}

tests/test_audience_summary.py

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import responses
2+
import unittest
3+
4+
from tests.support import with_resource, with_fixture, characters
5+
6+
from twitter_ads.account import Account
7+
from twitter_ads.client import Client
8+
from twitter_ads.targeting import AudienceSummary
9+
from twitter_ads import API_VERSION
10+
11+
12+
@responses.activate
13+
def test_audience_summary():
14+
responses.add(responses.GET,
15+
with_resource('/' + API_VERSION + '/accounts/2iqph'),
16+
body=with_fixture('accounts_load'),
17+
content_type='application/json')
18+
19+
responses.add(responses.POST,
20+
with_resource('/' + API_VERSION + '/accounts/2iqph/audience_summary'),
21+
body=with_fixture('audience_summary'),
22+
content_type='application/json')
23+
24+
client = Client(
25+
characters(40),
26+
characters(40),
27+
characters(40),
28+
characters(40)
29+
)
30+
31+
account = Account.load(client, '2iqph')
32+
33+
params = {
34+
"targeting_criteria": [
35+
{
36+
"targeting_type":"LOCATION",
37+
"targeting_value":"96683cc9126741d1"
38+
},
39+
{
40+
"targeting_type":"BROAD_KEYWORD",
41+
"targeting_value":"cats"
42+
},
43+
{
44+
"targeting_type":"SIMILAR_TO_FOLLOWERS_OF_USER",
45+
"targeting_value": "14230524"
46+
},
47+
{
48+
"targeting_type":"SIMILAR_TO_FOLLOWERS_OF_USER",
49+
"targeting_value": "90420314"
50+
}
51+
]
52+
}
53+
54+
audience_summary = AudienceSummary.load(
55+
account=account,
56+
params=params
57+
)
58+
59+
print (audience_summary)
60+
assert audience_summary is not None
61+
assert audience_summary.audience_size is not None
62+
assert audience_summary.audience_size['min'] == 41133600
63+
assert audience_summary.audience_size['max'] == 50274400

twitter_ads/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright (C) 2015 Twitter, Inc.
22

3-
VERSION = (6, 1, 0)
4-
API_VERSION = '6'
3+
VERSION = (7, 0, 0)
4+
API_VERSION = '7'
55

66
from twitter_ads.utils import get_version
77

twitter_ads/account.py

-18
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from twitter_ads.enum import TRANSFORM
77
from twitter_ads.http import Request
88
from twitter_ads.cursor import Cursor
9-
from twitter_ads.utils import Deprecated
109
from twitter_ads import API_VERSION
1110

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

3331
def __init__(self, client):
3432
self._client = client
@@ -156,22 +154,6 @@ def video_website_cards(self, id=None, **kwargs):
156154
"""
157155
return self._load_resource(VideoWebsiteCard, id, **kwargs)
158156

159-
@Deprecated('This method has been deprecated as of version 5'
160-
'and no longer works in the latest version.')
161-
def scoped_timeline(self, *id, **kwargs):
162-
"""
163-
Returns the most recent promotable Tweets created by the specified Twitter user.
164-
"""
165-
self._validate_loaded()
166-
167-
params = {'user_id': id}
168-
params.update(kwargs)
169-
170-
resource = self.SCOPED_TIMELINE.format(id=self.id)
171-
response = Request(self.client, 'get', resource, params=params).perform()
172-
173-
return response.body['data']
174-
175157

176158
# account properties
177159
resource_property(Account, 'id', readonly=True)

twitter_ads/campaign.py

+11
Original file line numberDiff line numberDiff line change
@@ -470,3 +470,14 @@ class IabCategories(Resource):
470470
resource_property(IabCategories, 'id', readonly=True)
471471
resource_property(IabCategories, 'name', readonly=True)
472472
resource_property(IabCategories, 'parent_id', readonly=True)
473+
474+
475+
class AdvertiserBusinessCategories(Resource):
476+
477+
PROPERTIES = {}
478+
RESOURCE_COLLECTION = '/' + API_VERSION + '/advertiser_business_categories'
479+
480+
481+
resource_property(ContentCategories, 'id', readonly=True)
482+
resource_property(ContentCategories, 'name', readonly=True)
483+
resource_property(ContentCategories, 'iab_categories', readonly=True)

twitter_ads/creative.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class MediaCreative(Analytics, Resource, Persistence):
117117
resource_property(MediaCreative, 'created_at', readonly=True, transform=TRANSFORM.TIME)
118118
resource_property(MediaCreative, 'deleted', readonly=True, transform=TRANSFORM.BOOL)
119119
resource_property(MediaCreative, 'id', readonly=True)
120-
resource_property(MediaCreative, 'serving_status', readonly=True)
120+
resource_property(MediaCreative, 'entity_status', readonly=True)
121121
resource_property(MediaCreative, 'updated_at', readonly=True, transform=TRANSFORM.TIME)
122122
# writable
123123
resource_property(MediaCreative, 'account_media_id')

twitter_ads/enum.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,12 @@ def enum(**enums):
155155
ALL_ON_TWITTER='ALL_ON_TWITTER',
156156
TWITTER_SEARCH='TWITTER_SEARCH',
157157
TWITTER_TIMELINE='TWITTER_TIMELINE',
158-
PUBLISHER_NETWORK='PUBLISHER_NETWORK'
158+
PUBLISHER_NETWORK='PUBLISHER_NETWORK',
159+
TAP_FULL='TAP_FULL',
160+
TAP_FULL_LANDSCAPE='TAP_FULL_LANDSCAPE',
161+
TAP_BANNER='TAP_BANNER',
162+
TAP_NATIVE='TAP_NATIVE',
163+
TAP_MRECT="TAP_MRECT"
159164
)
160165

161166
PRODUCT = enum(

twitter_ads/targeting.py

+17-8
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,28 @@
33
"""Container for all targeting related logic used by the Ads API SDK."""
44

55
from twitter_ads.http import Request
6+
from twitter_ads.resource import resource_property, Resource, Persistence
67
from twitter_ads import API_VERSION
8+
from twitter_ads.utils import FlattenParams
9+
import json
710

811

9-
class ReachEstimate(object):
12+
class AudienceSummary(Resource, Persistence):
13+
PROPERTIES = {}
1014

11-
RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/reach_estimate'
15+
RESOURCE = '/' + API_VERSION + '/accounts/{account_id}/audience_summary'
1216

1317
@classmethod
14-
def fetch(klass, account, product_type, objective, user_id, **kwargs):
15-
params = {'product_type': product_type, 'objective': objective, 'user_id': user_id}
16-
params.update(kwargs)
17-
18+
@FlattenParams
19+
def load(klass, account, params):
1820
resource = klass.RESOURCE.format(account_id=account.id)
19-
response = Request(account.client, 'get', resource, params=params).perform()
21+
headers = {'Content-Type': 'application/json'}
22+
response = Request(account.client,
23+
'post',
24+
resource,
25+
headers=headers,
26+
body=json.dumps(params)).perform()
27+
return klass(account).from_response(response.body['data'])
28+
2029

21-
return response.body['data']
30+
resource_property(AudienceSummary, 'audience_size')

0 commit comments

Comments
 (0)