python-amazon-ad-api 1.1.3__py3-none-any.whl → 1.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ad_api/api/sb/__init__.py CHANGED
@@ -21,6 +21,8 @@ from .reports import Reports
21
21
  from .benchmarks import Benchmarks
22
22
  from .themes import Themes
23
23
  from .budget_rules import BudgetRules
24
+ from .optimization_rules import OptimizationRules
25
+ from .targeting_categories import TargetingCategories
24
26
 
25
27
  __all__ = [
26
28
  "Brands",
@@ -46,4 +48,6 @@ __all__ = [
46
48
  "Benchmarks",
47
49
  "Themes",
48
50
  "BudgetRules",
51
+ "OptimizationRules",
52
+ "TargetingCategories",
49
53
  ]
@@ -0,0 +1,153 @@
1
+ from ad_api.base import Client, sp_endpoint, ApiResponse, Utils
2
+
3
+
4
+ class OptimizationRules(Client):
5
+ """Sponsored Brands optimization rules.
6
+
7
+ Documentation: https://advertising.amazon.com/API/docs/en-us/sponsored-brands/4-0/openapi
8
+
9
+ Every route in this family takes a versioned media type on the way in, and the
10
+ service routes on it:
11
+
12
+ .. warning::
13
+ With ``application/json`` the router answers ``404 Could not find
14
+ resource`` on every route here, which reads like a missing route but is
15
+ only the wrong media type. Verified on ``list`` and ``recommendations``.
16
+
17
+ The two read routes were probed against a live account; the four write routes
18
+ are built from the contract and not exercised.
19
+ """
20
+
21
+ _MEDIA_RULES = 'application/vnd.sbruleoptimization.v4+json'
22
+ _MEDIA_RECS = 'application/vnd.sboptimizationrecommendationresource.v4+json'
23
+
24
+ @sp_endpoint('/sb/rules/optimization/list', method='POST')
25
+ def list_optimization_rules(self, **kwargs) -> ApiResponse:
26
+ """
27
+ Lists optimization rules.
28
+
29
+ Request Body [Optional]
30
+ | **entityFilter** ({}): ``entityType`` and ``entityId``.
31
+ | **optimizationRuleIdFilter** ({}): ``include``, a list of rule ids.
32
+ | **maxResults** (number): Page size.
33
+ | **nextToken** (string): Pagination token.
34
+
35
+ Returns:
36
+ | ApiResponse, ``200``, with ``optimizationRules`` and ``totalCount``.
37
+
38
+ Example::
39
+
40
+ {"optimizationRules": [], "totalCount": 0}
41
+ """
42
+ return self._request(kwargs.pop('path'),
43
+ data=Utils.convert_body(kwargs.pop('body', {}), False),
44
+ params=kwargs, headers={'Accept': self._MEDIA_RULES,
45
+ 'Content-Type': self._MEDIA_RULES})
46
+
47
+ @sp_endpoint('/sb/recommendations/optimization', method='POST')
48
+ def get_optimization_recommendation(self, costControlMetric, landingPages, **kwargs) -> ApiResponse:
49
+ """
50
+ Gets an optimization rule recommendation.
51
+
52
+ Request Body [Required]
53
+ | **costControlMetric** ({}): [required] Cost control metric.
54
+ | **landingPages** (array): [required] Each entry carries ``pageType``
55
+ and either ``asins`` or ``url``.
56
+
57
+ Returns:
58
+ | ApiResponse, status ``207``.
59
+
60
+ .. note::
61
+ An empty body answers ``400`` with both members named:
62
+ *"Value null at 'costControlMetric' failed to satisfy constraint"*.
63
+ """
64
+ return self._request(kwargs.pop('path'),
65
+ data=Utils.convert_body({'costControlMetric': costControlMetric,
66
+ 'landingPages': landingPages}, False),
67
+ params=kwargs, headers={'Accept': self._MEDIA_RECS,
68
+ 'Content-Type': self._MEDIA_RECS})
69
+
70
+ @sp_endpoint('/sb/rules/optimization', method='POST')
71
+ def create_optimization_rules(self, optimizationRules, **kwargs) -> ApiResponse:
72
+ """
73
+ Creates optimization rules.
74
+
75
+ Request Body [Required]
76
+ | **optimizationRules** (array): [required]
77
+ | **entityType** (string): Type of the entity the rule applies to.
78
+ | **entityId** (string): Identifier of that entity.
79
+ | **conditions** (array): Each entry needs ``attributeName`` and
80
+ ``criteria`` with ``comparisonOperator`` and ``value``.
81
+
82
+ Returns:
83
+ | ApiResponse, status ``207``.
84
+
85
+ .. note::
86
+ Not probed: creates optimization rules on the account.
87
+ """
88
+ return self._request(kwargs.pop('path'),
89
+ data=Utils.convert_body({'optimizationRules': optimizationRules}, False),
90
+ params=kwargs, headers={'Accept': self._MEDIA_RULES,
91
+ 'Content-Type': self._MEDIA_RULES})
92
+
93
+ @sp_endpoint('/sb/rules/optimization', method='PUT')
94
+ def update_optimization_rules(self, optimizationRules, **kwargs) -> ApiResponse:
95
+ """
96
+ Updates optimization rules.
97
+
98
+ Request Body [Required]
99
+ | **optimizationRules** (array): [required] Each entry needs
100
+ ``optimizationRuleId`` and ``conditions``.
101
+
102
+ Returns:
103
+ | ApiResponse, status ``207``.
104
+
105
+ .. note::
106
+ Not probed: updates live optimization rules.
107
+ """
108
+ return self._request(kwargs.pop('path'),
109
+ data=Utils.convert_body({'optimizationRules': optimizationRules}, False),
110
+ params=kwargs, headers={'Accept': self._MEDIA_RULES,
111
+ 'Content-Type': self._MEDIA_RULES})
112
+
113
+ @sp_endpoint('/sb/rules/optimization/associate', method='POST')
114
+ def associate_optimization_rules(self, optimizationRuleAssociations, **kwargs) -> ApiResponse:
115
+ """
116
+ Associates optimization rules with entities.
117
+
118
+ Request Body [Required]
119
+ | **optimizationRuleAssociations** (array): [required] Each entry needs
120
+ ``entityId``, ``entityType`` and ``optimizationRuleId``.
121
+
122
+ Returns:
123
+ | ApiResponse, status ``207``.
124
+
125
+ .. note::
126
+ Not probed: changes which rules apply to live campaigns.
127
+ """
128
+ return self._request(kwargs.pop('path'),
129
+ data=Utils.convert_body(
130
+ {'optimizationRuleAssociations': optimizationRuleAssociations}, False),
131
+ params=kwargs, headers={'Accept': self._MEDIA_RULES,
132
+ 'Content-Type': self._MEDIA_RULES})
133
+
134
+ @sp_endpoint('/sb/rules/optimization/disassociate', method='POST')
135
+ def disassociate_optimization_rules(self, optimizationRuleDisassociations, **kwargs) -> ApiResponse:
136
+ """
137
+ Disassociates optimization rules from entities.
138
+
139
+ Request Body [Required]
140
+ | **optimizationRuleDisassociations** (array): [required] Each entry
141
+ needs ``entityId``, ``entityType`` and ``optimizationRuleId``.
142
+
143
+ Returns:
144
+ | ApiResponse, status ``207``.
145
+
146
+ .. note::
147
+ Not probed: changes which rules apply to live campaigns.
148
+ """
149
+ return self._request(kwargs.pop('path'),
150
+ data=Utils.convert_body(
151
+ {'optimizationRuleDisassociations': optimizationRuleDisassociations}, False),
152
+ params=kwargs, headers={'Accept': self._MEDIA_RULES,
153
+ 'Content-Type': self._MEDIA_RULES})
@@ -0,0 +1,98 @@
1
+ from ad_api.base import Client, sp_endpoint, fill_query_params, ApiResponse, Utils
2
+
3
+
4
+ class TargetingCategories(Client):
5
+ """Sponsored Brands targetable categories.
6
+
7
+ Documentation: https://advertising.amazon.com/API/docs/en-us/sponsored-brands/4-0/openapi
8
+
9
+ All three routes are reads, and all three were probed against a live account.
10
+
11
+ .. note::
12
+ ``list_categories`` returns the tree under ``categoryTree``, which the
13
+ contract does not name. Each node carries ``categoryRefinementId``,
14
+ ``name``, ``parentCategoryRefinementId``, ``isTargetable`` and, where the
15
+ service knows it, ``asinCountRange`` with a ``min`` and ``max``.
16
+
17
+ .. note::
18
+ Large root categories come back with ``isTargetable`` set to ``false``:
19
+ they have to be narrowed down to their children before they can be used
20
+ as a target.
21
+ """
22
+
23
+ _MEDIA = 'application/vnd.sbtargeting.v4+json'
24
+
25
+ @sp_endpoint('/sb/targets/categories', method='GET')
26
+ def list_categories(self, supplySource, **kwargs) -> ApiResponse:
27
+ """
28
+ Gets the targetable category tree.
29
+
30
+ Keyword Args
31
+ | query **supplySource** (string): [required] ``AMAZON`` or
32
+ ``STREAMING_VIDEO``.
33
+ | query **locale** (string): [optional] Market locale, for instance
34
+ ``es_ES``, ``en_US`` or ``de_DE``.
35
+ | query **includeOnlyRootCategories** (boolean): [optional] Return
36
+ root categories only.
37
+ | query **parentCategoryRefinementId** (string): [optional] Return
38
+ the children of that category.
39
+ | query **nextToken** (string): [optional] Pagination token.
40
+
41
+ Returns:
42
+ | ApiResponse, ``200``, with the tree under ``categoryTree``.
43
+
44
+ Example node::
45
+
46
+ {"categoryRefinementId": "1909624031", "name": "Chupetes",
47
+ "asinCountRange": {"min": 1247, "max": 2078},
48
+ "isTargetable": true, "parentCategoryRefinementId": "1862267031"}
49
+ """
50
+ return self._request(kwargs.pop('path'), params=kwargs,
51
+ headers={'Accept': self._MEDIA, 'Content-Type': self._MEDIA})
52
+
53
+ @sp_endpoint('/sb/targets/categories/{}/refinements', method='GET')
54
+ def list_refinements(self, categoryRefinementId, **kwargs) -> ApiResponse:
55
+ """
56
+ Gets the refinements of a category.
57
+
58
+ Keyword Args
59
+ | path **categoryRefinementId** (string): [required]
60
+ | query **locale** (string): [optional]
61
+ | query **nextToken** (string): [optional]
62
+
63
+ Returns:
64
+ | ApiResponse, ``200``.
65
+ """
66
+ return self._request(fill_query_params(kwargs.pop('path'), categoryRefinementId),
67
+ params=kwargs,
68
+ headers={'Accept': self._MEDIA, 'Content-Type': self._MEDIA})
69
+
70
+ @sp_endpoint('/sb/targets/products/count', method='POST')
71
+ def get_product_count(self, category, **kwargs) -> ApiResponse:
72
+ """
73
+ Gets the number of products in a category.
74
+
75
+ Request Body [Required]
76
+ | **category** (string): [required] Category refinement identifier.
77
+ | **ageRanges** (array of string): [optional]
78
+ | **brands** (array of string): [optional]
79
+ | **genres** (array of string): [optional]
80
+ | **isPrimeShipping** (boolean): [optional]
81
+ | **ratingRange** ({}): [optional] ``min`` and ``max`` integers.
82
+ | **priceRange** ({}): [optional] ``min`` and ``max`` numbers.
83
+
84
+ Returns:
85
+ | ApiResponse, ``200``.
86
+
87
+ Raises:
88
+ | ``400`` when ``category`` is missing, naming that member.
89
+ """
90
+ body = {'category': category}
91
+ for key in ('ageRanges', 'brands', 'genres', 'isPrimeShipping',
92
+ 'ratingRange', 'priceRange'):
93
+ if key in kwargs:
94
+ body[key] = kwargs.pop(key)
95
+ return self._request(kwargs.pop('path'),
96
+ data=Utils.convert_body(body, False),
97
+ params=kwargs, headers={'Accept': self._MEDIA,
98
+ 'Content-Type': self._MEDIA})
ad_api/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "1.1.3"
1
+ __version__ = "1.1.5"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-amazon-ad-api
3
- Version: 1.1.3
3
+ Version: 1.1.5
4
4
  Summary: Python wrapper for the Amazon Advertising API
5
5
  Home-page: https://github.com/denisneuf/python-amazon-ad-api
6
6
  Author: Daniel Alvaro
@@ -1,5 +1,5 @@
1
1
  ad_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- ad_api/version.py,sha256=u9ExJqoMv3fQc8WmLTw4I2FnQo6u4xRrBc6DLy6G1IE,22
2
+ ad_api/version.py,sha256=KlgaVSJ1AQxCDJZgh4wFk-gpem-k9PfebW8U3hiKJD4,22
3
3
  ad_api/api/__init__.py,sha256=E9c0ZyK4vplMlKKEBF8CV5u7P4uSkTMSQsAuSQu5bfY,1514
4
4
  ad_api/api/account.py,sha256=CmOvCfHoPWaeTUJvtB0pNoNulZVSz4oMMCKIpVTpHos,2931
5
5
  ad_api/api/advertising_test_account.py,sha256=btOVbdk2tBDmyKSJaQbcIFyzNG8KFJVUmTe4p9xMhpU,1507
@@ -24,7 +24,7 @@ ad_api/api/reports.py,sha256=gNmDHLULD2uaJgsMpIbCX9kabwhMXRr9WfZBtHUdsOY,6043
24
24
  ad_api/api/stores.py,sha256=7W3RJKZyZOQ3nHzInvK9FDhWTXCH6Xe9a6dCBatEZ-o,2484
25
25
  ad_api/api/stream.py,sha256=JpnF8SKniOAHiEnEq1RPrh6tNy-Q2-8GPBTmnlxEXqQ,2989
26
26
  ad_api/api/validation_configurations.py,sha256=V3o06uWGwvA_Ly1x2lAiCk35Wo7DC_k8AtHgMdpm6wU,3830
27
- ad_api/api/sb/__init__.py,sha256=1M9GaWMkwb7_0G-JtgAEMjFRpsLs4Ls2-pVtFIyn9LM,1332
27
+ ad_api/api/sb/__init__.py,sha256=O5D20u5EZdftFRLJLaVmkaLo_Ucs0f52ZEL5b1gkfW0,1488
28
28
  ad_api/api/sb/ad_creatives_v4.py,sha256=86sF2lhSc3AC6d4P5TXyEnJMBjn9HvsJe4bbCQbSWK0,10508
29
29
  ad_api/api/sb/ad_groups.py,sha256=W06Fk0CCsuEYen1Bd8obR6aHpOMhk6tGWNWApecw59c,2882
30
30
  ad_api/api/sb/ad_groups_v4.py,sha256=f6KpOrJ2RsCa4aznP8vdGbXp0HGsCmLOvC9yXX2pESc,3669
@@ -42,10 +42,12 @@ ad_api/api/sb/landing_page_asins.py,sha256=j3qi2-KccuEzGLZrl6EfZBSikW2R4B4l7Tixp
42
42
  ad_api/api/sb/media.py,sha256=5LTeM4AHSDKw_58aTB2nEZhIYdWkztH0syBXaovS1rs,2529
43
43
  ad_api/api/sb/negative_keywords.py,sha256=ww3IRbawidDHAaYIkX5SsH2OZKJj4UnCylqqFeq3OoY,6918
44
44
  ad_api/api/sb/negative_product_targeting.py,sha256=QbK1EhhzmnNOzGtbkrAZ4HS5OqiLU3nZBmsl1scd2D0,5085
45
+ ad_api/api/sb/optimization_rules.py,sha256=aj9tZwKdaOzjnAN-8FHb49aiejNZKCCCcR8fjffH4k0,6802
45
46
  ad_api/api/sb/product_targeting.py,sha256=o6aB3hIh9jKvi_uTXFi--1hpnPFuXzQ-U9N7VUP_Bz4,5362
46
47
  ad_api/api/sb/recommendations.py,sha256=_21L8tZPuArmJ30XyR1SKQHQH_yswvF0IMqwD9sLhgY,347
47
48
  ad_api/api/sb/reports.py,sha256=icw5DpRrqfj0idEo0NN1GPDpIWQun2h_c2Uwc7CpNUA,3801
48
49
  ad_api/api/sb/stores.py,sha256=sLdA6Lp37VR-ldu_sTUZIqc8MbMzysPhNOh05VaKBcA,4371
50
+ ad_api/api/sb/targeting_categories.py,sha256=mTU3hMEW_orG_D2mlTqUX14DEDPn9A11vH-wntECklk,4212
49
51
  ad_api/api/sb/targeting_recommendations.py,sha256=_7NxD5kC-Od11QOuLT03qpLVxEOIZgy-BtzniWK4CI8,2862
50
52
  ad_api/api/sb/themes.py,sha256=rjxdGcllaqLOUMoTKKgBVSH5iKuxMdF0w-dHq0X3-yU,4618
51
53
  ad_api/api/sd/__init__.py,sha256=RulShFVpowt2L_ZMcifFVgVrQEg5Uf9_SlVRydUnTF4,911
@@ -105,8 +107,8 @@ ad_api/base/exceptions.py,sha256=9D3M40ZIlLS0fA-aQh1BTWPOW-Nvf0tRhbA85jFnDJ4,438
105
107
  ad_api/base/helpers.py,sha256=naUkDLK466x_OBCPXxMlzRc2nPC6iQ6H_vyUji0MJJ0,2282
106
108
  ad_api/base/marketplaces.py,sha256=44lvCcluXNpqTprWQ80e3weSQmQbJoWL-WDSOWVbCyQ,4657
107
109
  ad_api/base/utils.py,sha256=_qnCBOvyJTLgaZjJzUWTi38GGLHkQszXELs79133Rb4,5618
108
- python_amazon_ad_api-1.1.3.dist-info/licenses/LICENSE,sha256=h2rpqlmmLy-4l1tN6KpWAFxx1JEMYeEFrMrZUbx_B_o,1105
109
- python_amazon_ad_api-1.1.3.dist-info/METADATA,sha256=RB8aZ0VWmVNwW0nR_21GuKzyeNZQFCgFXSQgnKYIxdk,20486
110
- python_amazon_ad_api-1.1.3.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
111
- python_amazon_ad_api-1.1.3.dist-info/top_level.txt,sha256=nN9twUos-8pkP514ZwpvRU9YL9L2XyQyHsMQFfudGTU,7
112
- python_amazon_ad_api-1.1.3.dist-info/RECORD,,
110
+ python_amazon_ad_api-1.1.5.dist-info/licenses/LICENSE,sha256=h2rpqlmmLy-4l1tN6KpWAFxx1JEMYeEFrMrZUbx_B_o,1105
111
+ python_amazon_ad_api-1.1.5.dist-info/METADATA,sha256=Bv5SRj3TNj5OvxigNtRz_LataskuZX8W2VsBcKGrz_U,20486
112
+ python_amazon_ad_api-1.1.5.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
113
+ python_amazon_ad_api-1.1.5.dist-info/top_level.txt,sha256=nN9twUos-8pkP514ZwpvRU9YL9L2XyQyHsMQFfudGTU,7
114
+ python_amazon_ad_api-1.1.5.dist-info/RECORD,,