├── ad_api ├── __init__.py ├── version.py ├── api │ ├── dsp │ │ ├── access_token_client.py │ │ ├── __init__.py │ │ ├── client.py │ │ └── credential_provider.py │ ├── sb │ │ ├── forecast.py │ │ ├── recommendations.py │ │ ├── keywords_recommendations.py │ │ ├── moderation.py │ │ ├── landing_page_asins.py │ │ ├── brands.py │ │ ├── __init__.py │ │ ├── media.py │ │ ├── bid_recommendations.py │ │ ├── snapshots.py │ │ ├── stores.py │ │ ├── ad_groups.py │ │ └── targeting_recommendations.py │ ├── sp │ │ ├── product_recommendations.py │ │ ├── budget_initial_recommendation.py │ │ ├── campaign_consolidated_recommendations.py │ │ ├── campaigns_budget_usage.py │ │ ├── budget_recommendations.py │ │ ├── budget_rules_recommendations.py │ │ ├── schedule_based_bid_optimization.py │ │ ├── __init__.py │ │ └── snapshots.py │ ├── sd │ │ ├── forecast.py │ │ ├── campaigns_budget_usage.py │ │ ├── __init__.py │ │ ├── recommendations.py │ │ └── snapshots.py │ ├── advertising_test_account.py │ ├── __init__.py │ ├── invoices.py │ ├── history.py │ ├── stores.py │ ├── metadata.py │ ├── portfolios_v3.py │ ├── exports.py │ ├── insights.py │ ├── account.py │ └── stream.py ├── auth │ ├── __init__.py │ ├── credentials.py │ ├── access_token_response.py │ └── exceptions.py └── base │ ├── base_client.py │ ├── config.py │ ├── api_response.py │ ├── __init__.py │ └── helpers.py ├── docs ├── _static │ ├── leeme.txt │ └── css │ │ └── custom.css ├── api │ ├── billing.rst │ ├── recommendations.rst │ ├── portfoliosV3.rst │ ├── advertising_test_account.rst │ ├── validation_configurations.rst │ ├── attribution.rst │ ├── history.rst │ ├── metadata.rst │ └── brand_metrics.rst ├── requirements.txt ├── sb.rst ├── sb │ ├── bid_recommendations.rst │ ├── media.rst │ ├── stores.rst │ ├── targeting_recommendations.rst │ ├── moderation.rst │ ├── campaigns_v4.rst │ ├── ad_groups_v4.rst │ ├── snapshots.rst │ ├── product_targeting.rst │ ├── negative_keywords.rst │ ├── negative_product_targeting.rst │ ├── ad_groups.rst │ ├── ads_v4.rst │ ├── brands.rst │ ├── landing_page_asins.rst │ ├── campaigns.rst │ ├── keywords.rst │ └── reports.rst ├── sd │ ├── ad_groups.rst │ ├── campaigns.rst │ ├── forecast.rst │ ├── recommendations.rst │ ├── campaign_budget_usage.rst │ ├── bid_recommendations.rst │ ├── targeting_recommendations.rst │ ├── creatives.rst │ ├── brand_safety.rst │ ├── product_targeting.rst │ ├── negative_product_targeting.rst │ ├── budget_rules.rst │ └── reports.rst ├── sb_v4.rst ├── sp │ ├── campaign_negative_keywords.rst │ ├── keywords_v3.rst │ ├── suggested_keywords.rst │ ├── bid_recommendations.rst │ ├── negative_keywords_v3.rst │ ├── bid_recommendations_v3.rst │ ├── negative_product_targeting_v3.rst │ ├── keywords.rst │ ├── ad_groups.rst │ ├── product_ads.rst │ ├── negative_product_targeting.rst │ ├── negative_keywords.rst │ ├── ad_groups_v3.rst │ ├── campaigns_v3.rst │ ├── product_ads_v3.rst │ ├── campaign_budget_usage.rst │ ├── budget_recommendations.rst │ ├── product_recommendations.rst │ ├── budget_rules_recommendations.rst │ ├── campaigns_consolidated_recommendations.rst │ ├── campaign_negative_keywords_v3.rst │ ├── campaign_negative_targets.rst │ ├── product_targeting_v3.rst │ ├── budget_rules.rst │ └── snapshots.rst ├── credentials.rst ├── installation.rst ├── dsp.rst ├── sp.rst ├── sd.rst ├── sb_v3.rst ├── api.rst ├── example.py ├── Makefile ├── index.rst ├── sp_v3.rst ├── disclaimer.rst ├── code.rst ├── sp_v2.rst ├── variables.rst └── config.rst ├── test ├── audiences │ ├── query_general.json │ ├── query_taxonomy.json │ ├── result_query_taxonomy.json │ └── result_query_general.json ├── snapshots │ └── sp-sx-state-filter.json ├── codigo-QR.png ├── reports │ ├── dsp-sx-campaign-report.json │ ├── sd-sx-asins-report.json │ ├── sp-sx-ad_groups-report.json │ ├── sb-sx-campaigns-report.json │ └── sp-sx-advertised_product-report.json ├── brand_metrics │ ├── download.json │ ├── result.json │ └── location.json ├── localizations │ ├── no_result_product_v1.json │ ├── result_products_v1.json │ ├── request_currency.json │ ├── request_products_by_country_code.json │ ├── request_products_by_marketplace_id.json │ ├── result_currency_v1.json │ ├── request_keywords_locales.json │ ├── request_keywords_locale.json │ ├── no_result_product_v2.json │ ├── result_products_v2.json │ ├── no_result_targeting_expression_v1.json │ ├── no_result_targeting_expression_v2.json │ ├── request_keywords_marketplace_id.json │ ├── result_targeting_expression_v1.json │ ├── result_keywords_marketplace_id.json │ ├── result_targeting_expression_v2.json │ ├── result_keywords_locale.txt │ ├── result_keywords_locales.json │ ├── result_currency_extended_v1.json │ └── result_currency_v2.json ├── campaigns │ ├── sb-sx-edit-campaign.json │ ├── sb-sx-create-campaign-keywords.json │ └── sp-sx-create-campaigns.json ├── metadata │ └── products.json ├── keywords │ ├── sb-sx-edit-keywords.json │ └── sb-sx-create-keywords.json ├── portfolios │ ├── edit.json │ └── create.json ├── history │ └── query.json └── creative_assets │ └── search.json ├── requeriments.txt ├── pyproject.toml ├── .github ├── workflows │ ├── dependabot-automerge.yml │ ├── main.yml │ ├── python-publish.yml │ └── codeql-analysis.yml ├── dependabot.yml ├── FUNDING.yml └── ISSUE_TEMPLATE │ └── bug_report.md ├── .readthedocs.yaml ├── setup.py ├── CONTRIBUTING.md ├── setup.cfg ├── LICENSE └── .gitignore /ad_api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/_static/leeme.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ad_api/version.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.6.7" 2 | -------------------------------------------------------------------------------- /test/audiences/query_general.json: -------------------------------------------------------------------------------- 1 | { 2 | "adType": "SD" 3 | } -------------------------------------------------------------------------------- /test/snapshots/sp-sx-state-filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "stateFilter": "enabled" 3 | } -------------------------------------------------------------------------------- /test/codigo-QR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denisneuf/python-amazon-ad-api/HEAD/test/codigo-QR.png -------------------------------------------------------------------------------- /docs/_static/css/custom.css: -------------------------------------------------------------------------------- 1 | @import 'theme.css'; 2 | .strike { 3 | text-decoration: line-through; 4 | } -------------------------------------------------------------------------------- /docs/api/billing.rst: -------------------------------------------------------------------------------- 1 | Billing 2 | ======= 3 | 4 | .. autoclass:: ad_api.api.Billing 5 | :members: 6 | 7 | -------------------------------------------------------------------------------- /test/audiences/query_taxonomy.json: -------------------------------------------------------------------------------- 1 | { 2 | "adType": "SD", 3 | "categoryPath": [ 4 | "In-market" 5 | ] 6 | } -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx 2 | sphinx-rtd-theme 3 | cachetools 4 | pycryptodome 5 | python-dotenv 6 | pyyaml 7 | confuse 8 | -------------------------------------------------------------------------------- /requeriments.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | requests~=2.32.5 3 | six>=1.16,<2 4 | cachetools~=6.2.4 5 | pycryptodome~=3.23 6 | pytz~=2025.2 7 | confuse~=2.1.0 -------------------------------------------------------------------------------- /test/reports/dsp-sx-campaign-report.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "CAMPAIGN", 3 | "startDate": "2022-05-02", 4 | "endDate": "2022-05-30" 5 | } 6 | -------------------------------------------------------------------------------- /docs/sb.rst: -------------------------------------------------------------------------------- 1 | Sponsored Brands 2 | ================ 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | 7 | sb_v3 8 | sb_v4 9 | 10 | -------------------------------------------------------------------------------- /docs/sb/bid_recommendations.rst: -------------------------------------------------------------------------------- 1 | Bid Recommendations 2 | =================== 3 | 4 | .. autoclass:: ad_api.api.sb.BidRecommendations 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/sd/ad_groups.rst: -------------------------------------------------------------------------------- 1 | Ad Groups 2 | ========= 3 | 4 | .. autoclass:: ad_api.api.sd.AdGroups 5 | :members: 6 | 7 | Campaigns explanation goes here. 8 | -------------------------------------------------------------------------------- /docs/sd/campaigns.rst: -------------------------------------------------------------------------------- 1 | Campaigns 2 | ========= 3 | 4 | .. autoclass:: ad_api.api.sd.Campaigns 5 | :members: 6 | 7 | Campaigns explanation goes here. 8 | -------------------------------------------------------------------------------- /test/brand_metrics/download.json: -------------------------------------------------------------------------------- 1 | { 2 | "headers":{}, 3 | "next_token": None, 4 | "payload": "A1RLL123456789-820882fa-ff22-4772-99e1-6889e3ae97f8.csv" 5 | } -------------------------------------------------------------------------------- /docs/sb_v4.rst: -------------------------------------------------------------------------------- 1 | 4.0 2 | === 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | 7 | sb/campaigns_v4 8 | sb/ad_groups_v4 9 | sb/ads_v4 10 | 11 | -------------------------------------------------------------------------------- /docs/api/recommendations.rst: -------------------------------------------------------------------------------- 1 | Tactical recommendations beta 2 | ============================= 3 | 4 | .. autoclass:: ad_api.api.Recommendations 5 | :members: 6 | 7 | -------------------------------------------------------------------------------- /test/reports/sd-sx-asins-report.json: -------------------------------------------------------------------------------- 1 | { 2 | "tactic": "T00020", 3 | "reportDate": "20211101", 4 | "metrics": "campaignName,campaignId,adGroupName,adGroupId,asin" 5 | } -------------------------------------------------------------------------------- /docs/sb/media.rst: -------------------------------------------------------------------------------- 1 | Media 2 | ===== 3 | 4 | .. autoclass:: ad_api.api.sb.Media 5 | 6 | .. autofunction:: ad_api.api.sb.Media.create_media(self, **kwargs) -> ApiResponse: 7 | -------------------------------------------------------------------------------- /docs/sp/campaign_negative_keywords.rst: -------------------------------------------------------------------------------- 1 | Campaign Negative Keywords 2 | ========================== 3 | 4 | .. autoclass:: ad_api.api.sp.CampaignNegativeKeywords 5 | :members: 6 | 7 | -------------------------------------------------------------------------------- /docs/sp/keywords_v3.rst: -------------------------------------------------------------------------------- 1 | Keywords 2 | ======== 3 | 4 | .. warning:: 5 | 6 | This replaces the version 2 of SP Keywords 7 | 8 | .. autoclass:: ad_api.api.sp.KeywordsV3 9 | :members: -------------------------------------------------------------------------------- /docs/sp/suggested_keywords.rst: -------------------------------------------------------------------------------- 1 | Suggested Keywords 2 | ================== 3 | 4 | .. autoclass:: ad_api.api.sp.SuggestedKeywords 5 | :members: 6 | 7 | Campaigns explanation goes here. 8 | -------------------------------------------------------------------------------- /test/localizations/no_result_product_v1.json: -------------------------------------------------------------------------------- 1 | { 2 | "localizedProductResponses": [ 3 | {"errorCode": "INTERNAL_ERROR", "localizedProducts": {}, "status": "FAILURE"} 4 | ] 5 | } -------------------------------------------------------------------------------- /docs/sp/bid_recommendations.rst: -------------------------------------------------------------------------------- 1 | Bid Recommendations 2 | =================== 3 | 4 | .. autoclass:: ad_api.api.sp.BidRecommendations 5 | :members: 6 | 7 | Campaigns explanation goes here. 8 | -------------------------------------------------------------------------------- /test/campaigns/sb-sx-edit-campaign.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "campaignId": 144329325594765093, 4 | "name": "SB.Campaign.Keyword.Api.Edit", 5 | "budget": 110, 6 | "endDate": "20290131" 7 | } 8 | ] -------------------------------------------------------------------------------- /test/metadata/products.json: -------------------------------------------------------------------------------- 1 | { 2 | "asins": [ 3 | "B082MCG82X", 4 | "B07LBGG1K9" 5 | ], 6 | "checkItemDetails": true, 7 | "adType": "SP", 8 | "pageIndex": 0, 9 | "pageSize": 5 10 | } -------------------------------------------------------------------------------- /test/reports/sp-sx-ad_groups-report.json: -------------------------------------------------------------------------------- 1 | { 2 | "stateFilter": "enabled", 3 | "reportDate": "20210917", 4 | "metrics": "campaignName,campaignId,adGroupName,adGroupId,impressions,clicks,attributedConversions30d" 5 | } -------------------------------------------------------------------------------- /docs/credentials.rst: -------------------------------------------------------------------------------- 1 | Credentials 2 | =========== 3 | 4 | You can pass your credentials multiple ways, use one of them. 5 | 6 | .. toctree:: 7 | :maxdepth: 1 8 | 9 | config 10 | variables 11 | code -------------------------------------------------------------------------------- /test/reports/sb-sx-campaigns-report.json: -------------------------------------------------------------------------------- 1 | { 2 | "segment": "placement", 3 | "reportDate": "20211101", 4 | "metrics": "campaignName,campaignId,campaignStatus,campaignBudget,campaignBudgetType,campaignRuleBasedBudget" 5 | } -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | 5 | 6 | 7 | 8 | 9 | You can install using pip 10 | 11 | 12 | 13 | 14 | .. code-block:: bash 15 | 16 | pip install python-amazon-ad-api 17 | 18 | -------------------------------------------------------------------------------- /test/keywords/sb-sx-edit-keywords.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keywordId": 144351213232803965, 4 | "adGroupId": 144344914935156713, 5 | "campaignId": 144371273320927424, 6 | "state": "enabled", 7 | "bid": 0.51 8 | } 9 | ] -------------------------------------------------------------------------------- /docs/sp/negative_keywords_v3.rst: -------------------------------------------------------------------------------- 1 | Negative Keywords 2 | ================= 3 | 4 | .. warning:: 5 | 6 | This replaces the version 2 of Negative Keywords 7 | 8 | .. autoclass:: ad_api.api.sp.NegativeKeywordsV3 9 | :members: -------------------------------------------------------------------------------- /docs/sp/bid_recommendations_v3.rst: -------------------------------------------------------------------------------- 1 | Bid Recommendations 2 | =================== 3 | .. warning:: 4 | 5 | This replaces the version 2 of SP BidRecommendations 6 | 7 | .. autoclass:: ad_api.api.sp.BidRecommendationsV3 8 | :members: 9 | -------------------------------------------------------------------------------- /docs/sb/stores.rst: -------------------------------------------------------------------------------- 1 | Stores 2 | ====== 3 | 4 | .. autoclass:: ad_api.api.sb.Stores 5 | 6 | .. autofunction:: ad_api.api.sb.Stores.list_assets(self, **kwargs) -> ApiResponse: 7 | 8 | .. autofunction:: ad_api.api.sb.Stores.list_stores(self, **kwargs) -> ApiResponse: -------------------------------------------------------------------------------- /docs/sp/negative_product_targeting_v3.rst: -------------------------------------------------------------------------------- 1 | Negative Product Targeting 2 | ========================== 3 | 4 | .. warning:: 5 | 6 | This replaces the version 2 of Negative Product Targeting 7 | 8 | .. autoclass:: ad_api.api.sp.NegativeTargetsV3 9 | :members: -------------------------------------------------------------------------------- /test/brand_metrics/result.json: -------------------------------------------------------------------------------- 1 | { 2 | "expiration": 300000, 3 | "format": "CSV", 4 | "location": "", 5 | "reportId": "820882fa-ff22-4772-99e1-6889e3ae97f8", 6 | "status": "IN_PROGRESS", 7 | "statusDetails": "Generation of the report is in progress!" 8 | } -------------------------------------------------------------------------------- /ad_api/api/dsp/access_token_client.py: -------------------------------------------------------------------------------- 1 | from ad_api.api.dsp.credential_provider import DspCredentialProvider 2 | from ad_api.auth.access_token_client import AccessTokenClient 3 | 4 | 5 | class DspAccessTokenClient(AccessTokenClient): 6 | credential_provider_class = DspCredentialProvider 7 | -------------------------------------------------------------------------------- /ad_api/auth/__init__.py: -------------------------------------------------------------------------------- 1 | from .access_token_client import AccessTokenClient 2 | from .access_token_response import AccessTokenResponse 3 | from .credentials import Credentials 4 | 5 | __all__ = [ 6 | 'AccessTokenResponse', 7 | 'AccessTokenClient', 8 | 'Credentials', 9 | ] 10 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 119 3 | target-version = ['py39'] 4 | include = '\.pyi?$' 5 | exclude = ''' 6 | /( 7 | \.eggs 8 | | \.git 9 | | \.hg 10 | | \.mypy_cache 11 | | \.tox 12 | | \.venv 13 | | _build 14 | | buck-out 15 | | build 16 | | dist 17 | )/ 18 | ''' -------------------------------------------------------------------------------- /test/keywords/sb-sx-create-keywords.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "adGroupId": 144356535815171236, 4 | "campaignId": 144329324494765093, 5 | "keywordText": "frenos", 6 | "nativeLanguageKeyword": "brakes", 7 | "nativeLanguageLocale": "en_GB", 8 | "matchType": "exact", 9 | "bid": 0.4 10 | } 11 | ] -------------------------------------------------------------------------------- /ad_api/auth/credentials.py: -------------------------------------------------------------------------------- 1 | class Credentials: 2 | def __init__(self, credentials): 3 | self.client_id = credentials['client_id'] 4 | self.client_secret = credentials['client_secret'] 5 | self.refresh_token = credentials['refresh_token'] 6 | self.profile_id = credentials.get('profile_id') 7 | -------------------------------------------------------------------------------- /docs/sp/keywords.rst: -------------------------------------------------------------------------------- 1 | Keywords 2 | ======== 3 | 4 | .. deprecated:: 4.0.2 5 | 6 | .. warning:: 7 | 8 | There is a new version 3 of Sponsored Product API, please check the `migration guide`_. 9 | 10 | 11 | .. autoclass:: ad_api.api.sp.Keywords 12 | :members: 13 | 14 | Campaigns explanation goes here. 15 | -------------------------------------------------------------------------------- /docs/sp/ad_groups.rst: -------------------------------------------------------------------------------- 1 | Ad Groups 2 | ========= 3 | 4 | .. deprecated:: 4.0.2 5 | 6 | .. warning:: 7 | 8 | There is a new version 3 of Sponsored Product API, please check the `migration guide`_. 9 | 10 | 11 | .. autoclass:: ad_api.api.sp.AdGroups 12 | :members: 13 | 14 | Campaigns explanation goes here. 15 | -------------------------------------------------------------------------------- /docs/sp/product_ads.rst: -------------------------------------------------------------------------------- 1 | Product Ads 2 | =========== 3 | 4 | .. deprecated:: 4.0.2 5 | 6 | .. warning:: 7 | 8 | There is a new version 3 of Sponsored Product API, please check the `migration guide`_. 9 | 10 | .. autoclass:: ad_api.api.sp.ProductAds 11 | :members: 12 | 13 | Campaigns explanation goes here. 14 | -------------------------------------------------------------------------------- /ad_api/auth/access_token_response.py: -------------------------------------------------------------------------------- 1 | class AccessTokenResponse: 2 | def __init__(self, **kwargs): 3 | self.access_token = kwargs.get('access_token') 4 | self.refresh_token = kwargs.get('refresh_token') 5 | self.expires_in = kwargs.get('expires_in') 6 | self.token_type = kwargs.get('token_type') 7 | -------------------------------------------------------------------------------- /docs/sp/negative_product_targeting.rst: -------------------------------------------------------------------------------- 1 | Negative Product Targeting 2 | ========================== 3 | 4 | .. deprecated:: 4.0.2 5 | 6 | .. warning:: 7 | 8 | There is a new version 3 of Sponsored Product API, please check the `migration guide`_. 9 | 10 | 11 | .. autoclass:: ad_api.api.sp.NegativeTargets 12 | :members: 13 | 14 | -------------------------------------------------------------------------------- /ad_api/api/dsp/__init__.py: -------------------------------------------------------------------------------- 1 | from .reports import Reports 2 | from .client import DspClient 3 | from .access_token_client import DspAccessTokenClient 4 | from .credential_provider import DspCredentialProvider 5 | 6 | __all__ = [ 7 | "Reports", 8 | "DspClient", 9 | "DspAccessTokenClient", 10 | "DspCredentialProvider", 11 | ] 12 | -------------------------------------------------------------------------------- /docs/sp/negative_keywords.rst: -------------------------------------------------------------------------------- 1 | Negative Keywords 2 | ================= 3 | 4 | .. deprecated:: 4.0.2 5 | 6 | .. warning:: 7 | 8 | There is a new version 3 of Sponsored Product API, please check the `migration guide`_. 9 | 10 | 11 | .. autoclass:: ad_api.api.sp.NegativeKeywords 12 | :members: 13 | 14 | Campaigns explanation goes here. 15 | -------------------------------------------------------------------------------- /test/portfolios/edit.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "portfolioId": 183826157455614, 4 | "name": "Apple-iMac", 5 | "budget": { 6 | "amount": 80.5, 7 | "policy": "monthlyRecurring", 8 | "startDate": "20220418" 9 | }, 10 | "inBudget": true, 11 | "state": "enabled" 12 | } 13 | ] 14 | 15 | -------------------------------------------------------------------------------- /ad_api/api/sb/forecast.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class Forecast(Client): 5 | @sp_endpoint('/sb/campaigns/shopperSegments/forecast', method='POST') 6 | def list_forecast(self, **kwargs) -> ApiResponse: 7 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs) 8 | -------------------------------------------------------------------------------- /test/localizations/result_products_v1.json: -------------------------------------------------------------------------------- 1 | { 2 | "localizedProductResponses": [ 3 | { 4 | "localizedProducts": { 5 | "A1F83G8C2ARO7P": { 6 | "asin": "B000000000", 7 | "sku": "SKU-OF-B000000000" 8 | } 9 | }, 10 | "status": "SUCCESS" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /test/localizations/request_currency.json: -------------------------------------------------------------------------------- 1 | { 2 | "localizeCurrencyRequests": [ 3 | {"currency": {"amount": 10}}, 4 | {"currency": {"amount": 15}} 5 | ], 6 | "targetCountryCodes": ["GB", "US", "JP"], 7 | "sourceCountryCode": "DE", 8 | "sourceMarketplaceId": "A1PA6795UKMFR9", 9 | "targetMarketplaces": ["A1F83G8C2ARO7P", "ATVPDKIKX0DER", "A1VC38T7YXB528"] 10 | } -------------------------------------------------------------------------------- /ad_api/api/sb/recommendations.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class Recommendations(Client): 5 | @sp_endpoint('/sb​/recommendations​/creative​/headline', method='POST') 6 | def list_recommendations(self, **kwargs) -> ApiResponse: 7 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs) 8 | -------------------------------------------------------------------------------- /ad_api/api/sb/keywords_recommendations.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class KeywordsRecommendations(Client): 5 | @sp_endpoint('/sb/recommendations/keyword', method='POST') 6 | def list_keywords_recommendations(self, **kwargs) -> ApiResponse: 7 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs) 8 | -------------------------------------------------------------------------------- /docs/dsp.rst: -------------------------------------------------------------------------------- 1 | Amazon DSP 2 | ====================== 3 | 4 | Amazon Advertising API - DSP 5 | 6 | Documentation: https://advertising.amazon.com/API/docs/en-us/dsp-reports-beta-3p/#/Reports 7 | Amazon DSP is a demand-side platform (DSP) that enables advertisers to programmatically buy display, video, and audio ads both on and off Amazon. 8 | 9 | 10 | .. toctree:: 11 | :maxdepth: 1 12 | 13 | dsp/reports 14 | -------------------------------------------------------------------------------- /test/history/query.json: -------------------------------------------------------------------------------- 1 | { 2 | "fromDate": 1626739199000, 3 | "toDate": 1632095999000, 4 | "pageOffset": 10, 5 | "count": 60, 6 | "sort": { 7 | "key": "DATE", 8 | "direction": "DESC" 9 | }, 10 | "eventTypes": { 11 | "CAMPAIGN": { 12 | "filters": [ 13 | "BUDGET_AMOUNT" 14 | ], 15 | "eventTypeIds": [ 16 | "137359064782313" 17 | ] 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /docs/sp.rst: -------------------------------------------------------------------------------- 1 | Sponsored Products 2 | ================== 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | 7 | sp_v2 8 | sp_v3 9 | 10 | .. warning:: 11 | 12 | Version 2 is planned deprecation on 6/30/2023. There is a new version 3 of Sponsored Product API, please check the `migration guide`_. 13 | 14 | 15 | .. _`migration guide`: https://advertising.amazon.com/API/docs/en-us/sponsored-products/v3-migration-guide -------------------------------------------------------------------------------- /ad_api/base/base_client.py: -------------------------------------------------------------------------------- 1 | import ad_api.version as vd 2 | 3 | 4 | class BaseClient: 5 | scheme = 'https://' 6 | method = 'GET' 7 | content_type = 'application/x-www-form-urlencoded;charset=UTF-8' 8 | user_agent = 'python-ad-api' 9 | 10 | def __init__(self): 11 | try: 12 | version = vd.__version__ 13 | self.user_agent += f'-{version}' 14 | except Exception: 15 | pass 16 | -------------------------------------------------------------------------------- /docs/sd/forecast.rst: -------------------------------------------------------------------------------- 1 | Forecasts 2 | ========= 3 | 4 | 5 | .. autoclass:: ad_api.api.sd.Forecast 6 | 7 | Endpoints available 8 | 9 | .. csv-table:: 10 | :widths: 10, 35, 50 11 | :header: "Method", "Endpoint", "Description" 12 | 13 | "POST", "/sd/forecasts", "Return forecasts for an ad group that may or may not exist." 14 | 15 | 16 | .. autofunction:: ad_api.api.sd.Forecast.list_forecasts(self, **kwargs) -> ApiResponse: 17 | 18 | -------------------------------------------------------------------------------- /ad_api/api/dsp/client.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | from ad_api.base import Client 3 | 4 | 5 | class DspClient(Client): 6 | @property 7 | def headers(self) -> Dict[str, str]: 8 | return { 9 | 'User-Agent': self.user_agent, 10 | 'Amazon-Advertising-API-ClientId': self.credentials['client_id'], 11 | 'Authorization': f'Bearer {self.auth.access_token}', 12 | 'Content-Type': 'application/json', 13 | } 14 | -------------------------------------------------------------------------------- /docs/sd.rst: -------------------------------------------------------------------------------- 1 | Sponsored Display 2 | ================= 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | 7 | sd/campaigns 8 | sd/ad_groups 9 | sd/product_ads 10 | sd/reports 11 | sd/product_targeting 12 | sd/targeting_recommendations 13 | sd/bid_recommendations 14 | sd/negative_product_targeting 15 | sd/creatives 16 | sd/brand_safety 17 | sd/budget_rules 18 | sd/campaign_budget_usage 19 | sd/forecast 20 | sd/recommendations -------------------------------------------------------------------------------- /test/reports/sp-sx-advertised_product-report.json: -------------------------------------------------------------------------------- 1 | { 2 | "startDate": "2022-11-01", 3 | "endDate": "2022-11-01", 4 | "configuration": { 5 | "adProduct": "SPONSORED_PRODUCTS", 6 | "groupBy": ["advertiser"], 7 | "columns": ["impressions", "clicks", "cost", "campaignStatus", "advertisedAsin", "date"], 8 | "reportTypeId": "spAdvertisedProduct", 9 | "timeUnit": "DAILY", 10 | "format": "GZIP_JSON" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /docs/sp/ad_groups_v3.rst: -------------------------------------------------------------------------------- 1 | Ad Groups 2 | ========= 3 | 4 | .. autoclass:: ad_api.api.sp.AdGroupsV3 5 | 6 | .. warning:: 7 | 8 | This replaces the version 2 of Ad Groups 9 | 10 | .. autofunction:: ad_api.api.sp.AdGroupsV3.create_ad_groups 11 | 12 | .. autofunction:: ad_api.api.sp.AdGroupsV3.edit_ad_groups 13 | 14 | .. autofunction:: ad_api.api.sp.AdGroupsV3.delete_ad_groups 15 | 16 | .. autofunction:: ad_api.api.sp.AdGroupsV3.list_ad_groups 17 | -------------------------------------------------------------------------------- /docs/sb_v3.rst: -------------------------------------------------------------------------------- 1 | 3.0 2 | === 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | 7 | sb/campaigns 8 | sb/ad_groups 9 | sb/keywords 10 | sb/negative_keywords 11 | sb/product_targeting 12 | sb/negative_product_targeting 13 | sb/targeting_recommendations 14 | sb/bid_recommendations 15 | sb/stores 16 | sb/landing_page_asins 17 | sb/media 18 | sb/brands 19 | sb/moderation 20 | sb/reports 21 | sb/snapshots 22 | sb/themes 23 | 24 | -------------------------------------------------------------------------------- /docs/sp/campaigns_v3.rst: -------------------------------------------------------------------------------- 1 | Campaigns 2 | ========= 3 | 4 | .. autoclass:: ad_api.api.sp.CampaignsV3 5 | 6 | .. warning:: 7 | 8 | This replaces the version 2 of Campaigns 9 | 10 | .. autofunction:: ad_api.api.sp.CampaignsV3.create_campaigns 11 | 12 | .. autofunction:: ad_api.api.sp.CampaignsV3.edit_campaigns 13 | 14 | .. autofunction:: ad_api.api.sp.CampaignsV3.list_campaigns 15 | 16 | .. autofunction:: ad_api.api.sp.CampaignsV3.delete_campaigns 17 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | Common Resources 2 | ================ 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | 7 | api/profiles 8 | api/manager_accounts 9 | api/invoices 10 | api/billing 11 | api/metadata 12 | api/eligibility 13 | api/history 14 | api/creative_assets 15 | api/localization 16 | api/audiences 17 | api/portfolios 18 | api/portfoliosV3 19 | api/insights 20 | api/reports 21 | api/validation_configurations 22 | api/recommendations 23 | -------------------------------------------------------------------------------- /docs/sp/product_ads_v3.rst: -------------------------------------------------------------------------------- 1 | Product Ads 2 | =========== 3 | 4 | .. autoclass:: ad_api.api.sp.ProductAdsV3 5 | 6 | .. warning:: 7 | 8 | This replaces the version 2 of Product Ads 9 | 10 | .. autofunction:: ad_api.api.sp.ProductAdsV3.list_product_ads 11 | 12 | .. autofunction:: ad_api.api.sp.ProductAdsV3.create_product_ads 13 | 14 | .. autofunction:: ad_api.api.sp.ProductAdsV3.edit_product_ads 15 | 16 | .. autofunction:: ad_api.api.sp.ProductAdsV3.delete_product_ads 17 | -------------------------------------------------------------------------------- /ad_api/auth/exceptions.py: -------------------------------------------------------------------------------- 1 | class AuthorizationError(Exception): 2 | """ 3 | Authorization Error 4 | 5 | Parameters: 6 | 7 | error_code: str Error code from amazon auth api 8 | error_msg: str Error sm 9 | status_code: integer Response status code from amazon auth api 10 | """ 11 | 12 | def __init__(self, error_code, error_msg, status_code): 13 | self.error_code = error_code 14 | self.message = error_msg 15 | self.status_code = status_code 16 | -------------------------------------------------------------------------------- /test/localizations/request_products_by_country_code.json: -------------------------------------------------------------------------------- 1 | { 2 | "localizeProductRequests": [ 3 | { 4 | "product": { 5 | "asin": "B000000000", 6 | "sku": "SKU-OF-B000000000" 7 | } 8 | } 9 | ], 10 | "adType": "SPONSORED_PRODUCTS", 11 | "entityType": "SELLER", 12 | "sourceCountryCode": "ES", 13 | "sourceAdvertiserId": "AD9EUOBWMS33M", 14 | "targetDetails": [{"countryCode": "FR", "advertiserId": "AD9EUOBWMS33M"}] 15 | } -------------------------------------------------------------------------------- /docs/sb/targeting_recommendations.rst: -------------------------------------------------------------------------------- 1 | Targeting Recommendations 2 | ========================= 3 | 4 | .. autoclass:: ad_api.api.sb.TargetsRecommendations 5 | 6 | .. autofunction:: ad_api.api.sb.TargetsRecommendations.list_products_targets(self, **kwargs) -> ApiResponse: 7 | 8 | .. autofunction:: ad_api.api.sb.TargetsRecommendations.list_category_targets(self, **kwargs) -> ApiResponse: 9 | 10 | .. autofunction:: ad_api.api.sb.TargetsRecommendations.list_brand_targets(self, **kwargs) -> ApiResponse: 11 | -------------------------------------------------------------------------------- /test/brand_metrics/location.json: -------------------------------------------------------------------------------- 1 | { 2 | "brandsInfo": 3 | [ 4 | { 5 | "id": "BRAND|BRAND_AID|34cf8a28-77f0-44dd-kl98-c419a6203257", 6 | "name": "BMW" 7 | } 8 | ], 9 | "expiration": 300000, 10 | "format": "CSV", 11 | "location": "https://infrastack-prod-eu-eu-we-generatedreportsbucket49-96329mbigajd.s3.eu-west-1.amazonaws.com/[...]/[...]", 12 | "reportId": "820882fa-ff22-4772-99e1-6889e3ae97f8", 13 | "status": "SUCCESSFUL", 14 | "statusDetails": "Generation of the report was successful!" 15 | } -------------------------------------------------------------------------------- /docs/sd/recommendations.rst: -------------------------------------------------------------------------------- 1 | Recommendations 2 | =============== 3 | 4 | 5 | .. autoclass:: ad_api.api.sd.Recommendations 6 | 7 | Endpoints available 8 | 9 | .. csv-table:: 10 | :widths: 10, 35, 50 11 | :header: "Method", "Endpoint", "Description" 12 | 13 | "POST", "/sd/recommendations/creative/headline", "Retrieve creative headline recommendations." 14 | 15 | 16 | .. autofunction:: ad_api.api.sd.Recommendations.list_headline_recommendations(self, **kwargs) -> ApiResponse: 17 | 18 | -------------------------------------------------------------------------------- /docs/example.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from ad_api.base import AdvertisingApiException 3 | from ad_api.api.sp import Campaigns 4 | 5 | logging.basicConfig(level=logging.DEBUG, format="%(asctime)s:%(levelname)s:%(message)s") 6 | 7 | try: 8 | states = 'enabled' 9 | result = Campaigns().list_campaigns_extended_request(stateFilter=states) 10 | 11 | campaigns = result.payload 12 | for campaign in campaigns: 13 | logging.info(campaign) 14 | 15 | 16 | except AdvertisingApiException as e: 17 | logging.error(e) 18 | -------------------------------------------------------------------------------- /docs/sd/campaign_budget_usage.rst: -------------------------------------------------------------------------------- 1 | Campaigns Budget Usage 2 | ====================== 3 | 4 | 5 | .. autoclass:: ad_api.api.sd.CampaignsBudgetUsage 6 | 7 | Endpoints available 8 | 9 | .. csv-table:: 10 | :widths: 10, 35, 50 11 | :header: "Method", "Endpoint", "Description" 12 | 13 | "POST", "/sd/campaigns/budget/usage", "Budget usage API for SP campaigns" 14 | 15 | 16 | .. autofunction:: ad_api.api.sd.CampaignsBudgetUsage.list_campaigns_budget_usage(self, version: int = 1, **kwargs) -> ApiResponse: 17 | 18 | -------------------------------------------------------------------------------- /test/localizations/request_products_by_marketplace_id.json: -------------------------------------------------------------------------------- 1 | { 2 | "localizeProductRequests": [ 3 | { 4 | "product": { 5 | "asin": "B000000000", 6 | "sku": "SKU-OF-B000000000" 7 | } 8 | } 9 | ], 10 | "adType": "SPONSORED_PRODUCTS", 11 | "entityType": "SELLER", 12 | "sourceMarketplaceId": "A1RKKUPIHCS9HS", 13 | "sourceAdvertiserId": "AD9EUOBWMS33M", 14 | "targetDetails": [ 15 | {"marketplaceId": "A2Q3Y263D00KWC", "advertiserId": "AD9EUOBWMS33M"} 16 | ] 17 | } -------------------------------------------------------------------------------- /ad_api/api/sp/product_recommendations.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class ProductRecommendations(Client): 5 | @sp_endpoint('/sp/targets/products/recommendations', method='POST') 6 | def list_products_recommendations(self, **kwargs) -> ApiResponse: 7 | contentType = 'application/vnd.spproductrecommendation.v3+json' 8 | headers = {'Content-Type': contentType} 9 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 10 | -------------------------------------------------------------------------------- /docs/sb/moderation.rst: -------------------------------------------------------------------------------- 1 | Moderation 2 | ========== 3 | 4 | .. autoclass:: ad_api.api.sb.Moderation 5 | 6 | .. autofunction:: ad_api.api.sb.Moderation.get_moderation(self, campaignId, **kwargs) -> ApiResponse: 7 | 8 | .. warning:: 9 | Note that this resource is only available for campaigns in the US marketplace. 10 | 11 | ###Example Python 12 | 13 | .. code-block:: python 14 | 15 | campaign_id = 144329324494765093 16 | 17 | result = Moderation().get_moderation( 18 | campaignId=campaign_id 19 | ) 20 | 21 | print(result) -------------------------------------------------------------------------------- /.github/workflows/dependabot-automerge.yml: -------------------------------------------------------------------------------- 1 | name: auto-merge 2 | 3 | on: 4 | pull_request_target: 5 | 6 | jobs: 7 | auto-merge: 8 | runs-on: ubuntu-latest 9 | 10 | if: | 11 | (github.event_name == 'pull_request_target' && github.actor == 'dependabot[bot]') || 12 | (github.event_name != 'pull_request_target' && github.actor != 'dependabot[bot]') 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: ahmadnassri/action-dependabot-auto-merge@v2 16 | with: 17 | target: minor 18 | github-token: ${{ secrets.MY_TOKEN }} 19 | -------------------------------------------------------------------------------- /docs/sb/campaigns_v4.rst: -------------------------------------------------------------------------------- 1 | Campaigns 2 | ========= 3 | 4 | .. autoclass:: ad_api.api.sb.CampaignsV4 5 | 6 | .. autofunction:: ad_api.api.sb.CampaignsV4.create_campaigns(self, version: int = 4, **kwargs) -> ApiResponse: 7 | 8 | .. autofunction:: ad_api.api.sb.CampaignsV4.edit_campaigns(self, version: int = 4, **kwargs) -> ApiResponse: 9 | 10 | .. autofunction:: ad_api.api.sb.CampaignsV4.delete_campaigns(self, version: int = 4, **kwargs) -> ApiResponse: 11 | 12 | .. autofunction:: ad_api.api.sb.CampaignsV4.list_campaigns(self, version: int = 4, **kwargs) -> ApiResponse: 13 | 14 | -------------------------------------------------------------------------------- /ad_api/api/sd/forecast.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class Forecast(Client): 5 | @sp_endpoint('/sd/forecasts', method='POST') 6 | def list_forecasts(self, **kwargs) -> ApiResponse: 7 | r""" 8 | Return forecasts for an ad group that may or may not exist. 9 | 10 | Request Body 11 | | SDForecastRequest { 12 | | } 13 | 14 | Returns 15 | ApiResponse 16 | """ 17 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs) 18 | -------------------------------------------------------------------------------- /ad_api/api/sp/budget_initial_recommendation.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class InitialBudgetRecommendation(Client): 5 | @sp_endpoint('/sp/campaigns/initialbudgetrecommendation', method='POST') 6 | def initial_campaign_budget_recommendation(self, **kwargs) -> ApiResponse: 7 | json_version = 'application/vnd.spinitialbudgetrecommendation.v3.4+json' 8 | 9 | headers = {"Content-Type": json_version} 10 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 11 | -------------------------------------------------------------------------------- /docs/sb/ad_groups_v4.rst: -------------------------------------------------------------------------------- 1 | Ad Groups 2 | ========= 3 | 4 | .. autoclass:: ad_api.api.sb.AdGroupsV4 5 | 6 | .. autofunction:: ad_api.api.sb.AdGroupsV4.create_ad_groups(self, version: int = 4, **kwargs) -> ApiResponse: 7 | 8 | .. autofunction:: ad_api.api.sb.AdGroupsV4.update_ad_groups(self, version: int = 4, **kwargs) -> ApiResponse: 9 | 10 | .. autofunction:: ad_api.api.sb.AdGroupsV4.list_ad_groups(self, version: int = 4, **kwargs) -> ApiResponse: 11 | 12 | .. autofunction:: ad_api.api.sb.AdGroupsV4.delete_ad_groups(self, version: int = 4, **kwargs) -> ApiResponse: 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/sd/bid_recommendations.rst: -------------------------------------------------------------------------------- 1 | Bid Recommendations 2 | =================== 3 | 4 | .. warning:: 5 | 6 | Sponsored Display is not available for Sandbox endpoint 7 | 8 | .. autoclass:: ad_api.api.sd.BidRecommendations 9 | 10 | Endpoint available 11 | 12 | .. csv-table:: 13 | :widths: 25, 25, 50 14 | :header: "Method", "Endpoint", "Description" 15 | 16 | "POST", "/sd/targets/bid/recommendations", "Returns a set of bid recommendations for targeting clauses" 17 | 18 | .. autofunction:: ad_api.api.sd.BidRecommendations.list_targets_bid_recommendations(self, **kwargs) -> ApiResponse: 19 | 20 | 21 | -------------------------------------------------------------------------------- /ad_api/api/dsp/credential_provider.py: -------------------------------------------------------------------------------- 1 | from ad_api.base.config import BaseConfig 2 | from ad_api.base.credential_provider import CredentialProvider 3 | 4 | 5 | class DspCredentialProvider(CredentialProvider): 6 | config_class = BaseConfig 7 | 8 | def from_env(self): 9 | account_data = dict( 10 | refresh_token=self._get_env('AD_API_REFRESH_TOKEN'), 11 | client_id=self._get_env('AD_API_CLIENT_ID'), 12 | client_secret=self._get_env('AD_API_CLIENT_SECRET'), 13 | ) 14 | self.credentials = self.config_class(**account_data) 15 | return len(self.credentials.check_config()) == 0 16 | -------------------------------------------------------------------------------- /docs/sd/targeting_recommendations.rst: -------------------------------------------------------------------------------- 1 | Targeting Recommendations 2 | ========================= 3 | 4 | .. warning:: 5 | 6 | Sponsored Display is not available for Sandbox endpoint 7 | 8 | .. autoclass:: ad_api.api.sd.TargetsRecommendations 9 | 10 | Endpoint available 11 | 12 | .. csv-table:: 13 | :widths: 25, 25, 50 14 | :header: "Method", "Endpoint", "Description" 15 | 16 | "POST", "/sd/targets/recommendations", "Returns a set of bid recommendations for targeting clauses" 17 | 18 | .. autofunction:: ad_api.api.sd.TargetsRecommendations.list_targets_recommendations(self, **kwargs) -> ApiResponse: 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/sb/snapshots.rst: -------------------------------------------------------------------------------- 1 | Snapshots 2 | ========= 3 | 4 | .. warning:: 5 | 6 | Currently, the Ads API does not support snapshots for Sponsored Brands video campaigns or campaigns created using the version 4 endpoints. Snapshots include records for version 3, non-video campaigns only. 7 | 8 | .. autoclass:: ad_api.api.sb.Snapshots 9 | 10 | .. autofunction:: ad_api.api.sb.Snapshots.post_snapshot(self, recordType, **kwargs) -> ApiResponse: 11 | 12 | .. autofunction:: ad_api.api.sb.Snapshots.get_snapshot(self, reportId, **kwargs) -> ApiResponse: 13 | 14 | .. autofunction:: ad_api.api.sb.Snapshots.download_snapshot(self, **kwargs) -> ApiResponse: 15 | -------------------------------------------------------------------------------- /docs/sb/product_targeting.rst: -------------------------------------------------------------------------------- 1 | Product Targeting 2 | ================= 3 | 4 | .. autoclass:: ad_api.api.sb.Targets 5 | 6 | .. autofunction:: ad_api.api.sb.Targets.list_products_targets(self, **kwargs) -> ApiResponse: 7 | 8 | .. autofunction:: ad_api.api.sb.Targets.edit_products_targets(self, **kwargs) -> ApiResponse: 9 | 10 | .. autofunction:: ad_api.api.sb.Targets.create_products_targets(self, **kwargs) -> ApiResponse: 11 | 12 | .. autofunction:: ad_api.api.sb.Targets.get_products_target(self, targetId, **kwargs) -> ApiResponse: 13 | 14 | .. autofunction:: ad_api.api.sb.Targets.delete_products_target(self, targetId, **kwargs) -> ApiResponse: -------------------------------------------------------------------------------- /test/localizations/result_currency_v1.json: -------------------------------------------------------------------------------- 1 | { 2 | "localizedCurrencyResponses": [ 3 | { 4 | "localizedCurrencies": { 5 | "A1F83G8C2ARO7P": {"amount": 8.29}, 6 | "ATVPDKIKX0DER": {"amount": 10.83}, 7 | "A1VC38T7YXB528": {"amount": 1363.0} 8 | }, 9 | "status": "SUCCESS" 10 | }, 11 | { 12 | "localizedCurrencies": { 13 | "A1F83G8C2ARO7P": {"amount": 12.43}, 14 | "ATVPDKIKX0DER": {"amount": 16.25}, 15 | "A1VC38T7YXB528": {"amount": 2045.0} 16 | }, 17 | "status": "SUCCESS" 18 | }, 19 | ] 20 | } -------------------------------------------------------------------------------- /test/creative_assets/search.json: -------------------------------------------------------------------------------- 1 | { 2 | "text": "string", 3 | "filterCriteria": { 4 | "valueFilters": [ 5 | { 6 | "values": [ 7 | "string" 8 | ], 9 | "valueField": "TAG" 10 | } 11 | ], 12 | "rangeFilters": [ 13 | { 14 | "range": [ 15 | { 16 | "start": "string", 17 | "end": "string" 18 | } 19 | ] 20 | } 21 | ] 22 | }, 23 | "sortCriteria": { 24 | "field": "CREATED_TIME", 25 | "order": "ASC" 26 | }, 27 | "pageCriteria": { 28 | "identifier": { 29 | "pageNumber": 0, 30 | "token": "string" 31 | }, 32 | "size": 0 33 | } 34 | } -------------------------------------------------------------------------------- /ad_api/base/config.py: -------------------------------------------------------------------------------- 1 | class BaseConfig: 2 | def __init__(self, refresh_token, client_id, client_secret): 3 | self.refresh_token = refresh_token 4 | self.client_id = client_id 5 | self.client_secret = client_secret 6 | 7 | def check_config(self): 8 | errors = [] 9 | for k, v in self.__dict__.items(): 10 | if not v and k != 'refresh_token': 11 | errors.append(k) 12 | return errors 13 | 14 | 15 | class Config(BaseConfig): 16 | def __init__(self, refresh_token, client_id, client_secret, profile_id): 17 | super().__init__(refresh_token, client_id, client_secret) 18 | self.profile_id = profile_id 19 | -------------------------------------------------------------------------------- /docs/sp/campaign_budget_usage.rst: -------------------------------------------------------------------------------- 1 | Campaigns Budget Usage 2 | ====================== 3 | 4 | .. warning:: 5 | 6 | Sponsored Product v3 is not available for Sandbox endpoint 7 | 8 | .. note:: 9 | 10 | This API is version 3.0 11 | 12 | 13 | .. autoclass:: ad_api.api.sp.CampaignsBudgetUsage 14 | 15 | Endpoints available 16 | 17 | .. csv-table:: 18 | :widths: 10, 35, 50 19 | :header: "Method", "Endpoint", "Description" 20 | 21 | "POST", "/sp/campaigns/budget/usage", "Budget usage API for SP campaigns" 22 | 23 | 24 | .. autofunction:: ad_api.api.sp.CampaignsBudgetUsage.list_campaigns_budget_usage(self, version: int = 1, **kwargs) -> ApiResponse: 25 | 26 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "pip" # See documentation for possible values 2 9 | directory: "/" # Location of package manifests 10 | target-branch: "main" 11 | schedule: 12 | interval: "daily" 13 | assignees: 14 | - "denisneuf" 15 | commit-message: 16 | prefix: "DEP" 17 | include: "scope" -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file for Sphinx projects 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | # Required 5 | version: 2 6 | 7 | # Set the OS, Python version and other tools you might need 8 | build: 9 | os: ubuntu-22.04 10 | tools: 11 | python: "3.11" 12 | 13 | # Build documentation in the "docs/" directory with Sphinx 14 | sphinx: 15 | configuration: docs/conf.py 16 | 17 | # Optional but recommended, declare the Python requirements required 18 | # to build your documentation 19 | # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 20 | python: 21 | install: 22 | - requirements: docs/requirements.txt 23 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = autodoc-example 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Has to be explicit, otherwise we don't get "make" without targets right. 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | # You can add custom targets here. 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/sb/negative_keywords.rst: -------------------------------------------------------------------------------- 1 | Negative Keywords 2 | ================= 3 | 4 | .. autoclass:: ad_api.api.sb.NegativeKeywords 5 | 6 | .. autofunction:: ad_api.api.sb.NegativeKeywords.list_negative_keywords(self, **kwargs) -> ApiResponse: 7 | 8 | .. autofunction:: ad_api.api.sb.NegativeKeywords.edit_negative_keywords(self, **kwargs) -> ApiResponse: 9 | 10 | .. autofunction:: ad_api.api.sb.NegativeKeywords.create_negative_keywords(self, **kwargs) -> ApiResponse: 11 | 12 | .. autofunction:: ad_api.api.sb.NegativeKeywords.get_negative_keyword(self, keywordId, **kwargs) -> ApiResponse: 13 | 14 | .. autofunction:: ad_api.api.sb.NegativeKeywords.delete_negative_keyword(self, keywordId, **kwargs) -> ApiResponse: -------------------------------------------------------------------------------- /test/localizations/request_keywords_locales.json: -------------------------------------------------------------------------------- 1 | { 2 | "localizeKeywordRequests": [ 3 | {"localizationKeyword": {"keyword": "máquina"}}, 4 | {"localizationKeyword": {"keyword": "diagnosis"}}, 5 | {"localizationKeyword": {"keyword": "diagnosis multimarca"}}, 6 | {"localizationKeyword": {"keyword": "coche"}}, 7 | {"localizationKeyword": {"keyword": "automóvil"}}, 8 | {"localizationKeyword": {"keyword": "frenos"}}, 9 | {"localizationKeyword": {"keyword": "presión"}}, 10 | {"localizationKeyword": {"keyword": "neumáticos"}} 11 | ], 12 | "sourceDetails": {"locale": "es_ES"}, 13 | "targetDetails": {"countryCodes": ["GB", "FR", "IT", "DE", "NL", "SE"]} 14 | } -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Manual Python Package 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | deploy: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Set up Python 13 | uses: actions/setup-python@v2 14 | with: 15 | python-version: '3.9' 16 | - name: Install dependencies 17 | run: | 18 | python -m pip install --upgrade pip 19 | pip install build 20 | - name: Build package 21 | run: python -m build 22 | - name: Publish package 23 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 24 | with: 25 | user: __token__ 26 | password: ${{ secrets.PYPI_API_TOKEN }} 27 | -------------------------------------------------------------------------------- /docs/sb/negative_product_targeting.rst: -------------------------------------------------------------------------------- 1 | Negative Product Targeting 2 | ========================== 3 | 4 | .. autoclass:: ad_api.api.sb.NegativeTargets 5 | 6 | .. autofunction:: ad_api.api.sb.NegativeTargets.list_negative_targets(self, **kwargs) -> ApiResponse: 7 | 8 | .. autofunction:: ad_api.api.sb.NegativeTargets.create_negative_targets(self, **kwargs) -> ApiResponse: 9 | 10 | .. autofunction:: ad_api.api.sb.NegativeTargets.edit_negative_targets(self, **kwargs) -> ApiResponse: 11 | 12 | .. autofunction:: ad_api.api.sb.NegativeTargets.get_negative_target(self, targetId, **kwargs) -> ApiResponse: 13 | 14 | .. autofunction:: ad_api.api.sb.NegativeTargets.delete_negative_target(self, targetId, **kwargs) -> ApiResponse: -------------------------------------------------------------------------------- /docs/sp/budget_recommendations.rst: -------------------------------------------------------------------------------- 1 | Product Recommendation Service 2 | ============================== 3 | 4 | .. warning:: 5 | 6 | Sponsored Product v3 is not available for Sandbox endpoint 7 | 8 | .. note:: 9 | 10 | This API is version 3.0 11 | 12 | 13 | 14 | .. autoclass:: ad_api.api.sp.ProductRecommendations 15 | 16 | Endpoint available 17 | 18 | .. csv-table:: 19 | :widths: 10, 35, 50 20 | :header: "Method", "Endpoint", "Description" 21 | 22 | "POST", "/sp/targets/products/recommendations", "Suggested target ASINs for your advertised product." 23 | 24 | 25 | .. autofunction:: ad_api.api.sp.ProductRecommendations.list_products_recommendations(self, **kwargs) -> ApiResponse: 26 | 27 | 28 | -------------------------------------------------------------------------------- /test/audiences/result_query_taxonomy.json: -------------------------------------------------------------------------------- 1 | { 2 | "categoryPath": ["Lifestyle"], 3 | "categories": [ 4 | {"category": "Automotive Ownership", "audienceCount": 65}, 5 | {"category": "Business & Industry", "audienceCount": 19}, 6 | {"category": "New to Category", "audienceCount": 11}, 7 | {"category": "Entertainment", "audienceCount": 8}, 8 | {"category": "Shoppers", "audienceCount": 4}, 9 | {"category": "Students & Professionals", "audienceCount": 3}, 10 | {"category": "Characteristics", "audienceCount": 2}, 11 | {"category": "Conscious Consumption", "audienceCount": 1}, 12 | {"category": "Family", "audienceCount": 1}, 13 | {"category": "Subscriptions", "audienceCount": 1}, 14 | ], 15 | } -------------------------------------------------------------------------------- /test/localizations/request_keywords_locale.json: -------------------------------------------------------------------------------- 1 | { 2 | "localizeKeywordRequests": [ 3 | {"localizationKeyword": {"keyword": "máquina"}}, 4 | {"localizationKeyword": {"keyword": "diagnosis"}}, 5 | {"localizationKeyword": {"keyword": "diagnosis multimarca"}}, 6 | {"localizationKeyword": {"keyword": "coche"}}, 7 | {"localizationKeyword": {"keyword": "automóvil"}}, 8 | {"localizationKeyword": {"keyword": "frenos"}}, 9 | {"localizationKeyword": {"keyword": "presión"}}, 10 | {"localizationKeyword": {"keyword": "neumáticos"}} 11 | ], 12 | "sourceDetails": {"countryCode": "ES"}, 13 | "targetDetails": { 14 | "locales": ["en_GB", "fr_FR", "it_IT", "de_DE", "nl_NL", "sv_SE"] 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /test/localizations/no_result_product_v2.json: -------------------------------------------------------------------------------- 1 | { 2 | "localizedProductResponses": [ 3 | { 4 | "errorCode": "INTERNAL_ERROR", 5 | "localizedProductResults": { 6 | "BR": { 7 | "errorCode": "INTERNAL_ERROR", 8 | "messages": [ 9 | "Product(asin=B000000000, sku=SKU-OF-B000000000) failed to localize from A1RKKUPIHCS9HS to BR" 10 | ], 11 | "status": "FAILURE" 12 | } 13 | }, 14 | "localizedProducts": {}, 15 | "sourceProduct": { 16 | "asin": "B000000000", 17 | "sku": "SKU-OF-B000000000" 18 | }, 19 | "status": "FAILURE" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: denisneuf 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /docs/sp/product_recommendations.rst: -------------------------------------------------------------------------------- 1 | Budget Recommendations and Missed Opportunities 2 | =============================================== 3 | 4 | .. warning:: 5 | 6 | Sponsored Product v3 is not available for Sandbox endpoint 7 | 8 | .. note:: 9 | 10 | This API is version 3.0 11 | 12 | 13 | 14 | .. autoclass:: ad_api.api.sp.BudgetRecommendations 15 | 16 | Endpoint available 17 | 18 | .. csv-table:: 19 | :widths: 10, 35, 50 20 | :header: "Method", "Endpoint", "Description" 21 | 22 | "POST", "/sp/campaigns/budgetRecommendations", "Get recommended daily budget and estimated missed opportunities for campaigns." 23 | 24 | 25 | .. autofunction:: ad_api.api.sp.BudgetRecommendations.list_campaigns_budget_recommendations(self, **kwargs) -> ApiResponse: 26 | 27 | 28 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Python Amazon Advertising API documentation 2 | =========================================== 3 | 4 | This project helps you using python 3.8 to use the *Python Amazon Advertising API*. 5 | 6 | 7 | .. toctree:: 8 | :maxdepth: 1 9 | 10 | installation 11 | credentials 12 | api 13 | api/attribution 14 | api/brand_metrics 15 | sp 16 | sb 17 | sd 18 | dsp 19 | api/advertising_test_account 20 | disclaimer 21 | 22 | 23 | .. versionadded:: 0.4.3 24 | The v4 of *Sponsored Brand* 25 | Deprecated *Sponsored Products v2* 26 | Created new endpoints in *Sponsored Display*: Brand Safety, Budget Rules, Campaigns Budget Usage, Forecast and Recommendations. 27 | Created new endpoint in *Common Resources*: Tactical recommendations beta 28 | 29 | .. literalinclude:: example.py 30 | -------------------------------------------------------------------------------- /docs/api/portfoliosV3.rst: -------------------------------------------------------------------------------- 1 | PortfoliosV3 2 | ============ 3 | `https://dtrnk0o2zy01c.cloudfront.net/openapi/en-us/dest/Portfolios_prod_3p.json`_ 4 | 5 | .. _https://dtrnk0o2zy01c.cloudfront.net/openapi/en-us/dest/Portfolios_prod_3p.json: https://dtrnk0o2zy01c.cloudfront.net/openapi/en-us/dest/Portfolios_prod_3p.json 6 | 7 | Manage portfolios that group together campaigns. 8 | 9 | .. autoclass:: ad_api.api.PortfoliosV3 10 | 11 | .. note:: 12 | 13 | This API **Portfolios** can be used as sandbox environment for testing 14 | 15 | .. autofunction:: ad_api.api.PortfoliosV3.list_portfolios 16 | 17 | .. autofunction:: ad_api.api.PortfoliosV3.create_portfolios 18 | 19 | .. autofunction:: ad_api.api.PortfoliosV3.edit_portfolios 20 | 21 | .. autofunction:: ad_api.api.PortfoliosV3.get_budget_usage_for_portfolios -------------------------------------------------------------------------------- /ad_api/api/sb/moderation.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, fill_query_params, ApiResponse 2 | 3 | 4 | class Moderation(Client): 5 | """ 6 | Use this interface to request and retrieve store information. This can be used for Sponsored Brands campaign creation, to pull the store URL information, and for asset registration for Stores. 7 | """ 8 | 9 | @sp_endpoint('/sb/moderation/campaigns/{}', method='GET') 10 | def get_moderation(self, campaignId, **kwargs) -> ApiResponse: 11 | """ 12 | Gets the moderation result for a campaign specified by identifier. 13 | 14 | Keyword Args 15 | | path **campaignId** (integer): The campaign identifier. [required] 16 | 17 | """ 18 | return self._request(fill_query_params(kwargs.pop('path'), campaignId), params=kwargs) 19 | -------------------------------------------------------------------------------- /docs/sp/budget_rules_recommendations.rst: -------------------------------------------------------------------------------- 1 | Budget Rules Recommendations 2 | ============================ 3 | 4 | .. warning:: 5 | 6 | Sponsored Product v3 is not available for Sandbox endpoint 7 | 8 | .. note:: 9 | 10 | This API is version 3.0 11 | 12 | 13 | 14 | .. autoclass:: ad_api.api.sp.BudgetRulesRecommendations 15 | 16 | Endpoint available 17 | 18 | .. csv-table:: 19 | :widths: 10, 35, 50 20 | :header: "Method", "Endpoint", "Description" 21 | 22 | "POST", "/sp/campaigns/budgetRules/recommendations", "Gets a list of special events with suggested date range and suggested budget increase for a campaign specified by identifier." 23 | 24 | 25 | .. autofunction:: ad_api.api.sp.BudgetRulesRecommendations.list_campaigns_budget_rules_recommendations(self, **kwargs) -> ApiResponse: 26 | 27 | 28 | -------------------------------------------------------------------------------- /docs/sb/ad_groups.rst: -------------------------------------------------------------------------------- 1 | Ad Groups 2 | ========= 3 | 4 | .. autoclass:: ad_api.api.sb.AdGroups 5 | 6 | .. autofunction:: ad_api.api.sb.AdGroups.list_ad_groups(self, **kwargs) -> ApiResponse: 7 | 8 | ### Example python 9 | 10 | .. code-block:: python 11 | 12 | from ad_api.api.sb.ad_groups import AdGroups 13 | 14 | res = AdGroups().list_ad_groups() 15 | 16 | print(result) 17 | 18 | 19 | .. autofunction:: ad_api.api.sb.AdGroups.get_ad_group(self, adGroupId, **kwargs) -> ApiResponse: 20 | 21 | ### Example python 22 | 23 | .. code-block:: python 24 | 25 | from ad_api.api.sb.ad_groups import AdGroups 26 | 27 | ad_group_id = 144356535815171236 28 | 29 | res = AdGroups().get_ad_group( 30 | adGroupId=ad_group_id 31 | ) 32 | 33 | print(res) 34 | 35 | 36 | -------------------------------------------------------------------------------- /test/localizations/result_products_v2.json: -------------------------------------------------------------------------------- 1 | { 2 | "localizedProductResponses": [ 3 | { 4 | "localizedProductResults": { 5 | "FR": { 6 | "localizedProduct": { 7 | "asin": "B000000000", 8 | "sku": "SKU-OF-B000000000" 9 | }, 10 | "status": "SUCCESS" 11 | } 12 | }, 13 | "localizedProducts": { 14 | "FR": { 15 | "asin": "B000000000", 16 | "sku": "SKU-OF-B000000000" 17 | } 18 | }, 19 | "sourceProduct": { 20 | "asin": "B000000000", 21 | "sku": "SKU-OF-B000000000" 22 | }, 23 | "status": "SUCCESS" 24 | } 25 | ] 26 | } 27 | 28 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name='python-amazon-ad-api', 5 | version='0.6.7', 6 | install_requires=[ 7 | "requests>=2.27.1,<2.33.0", 8 | "six>=1.16,<1.18", 9 | "cachetools>=5.0,<6.3", 10 | "pycryptodome>=3.13,<3.24", 11 | "pytz>=2021.3,<2026.0", 12 | "confuse>=1.7,<2.2", 13 | ], 14 | packages=[ 15 | 'ad_api', 16 | 'ad_api.api', 17 | 'ad_api.auth', 18 | 'ad_api.base', 19 | 'ad_api.api.sp', 20 | 'ad_api.api.sb', 21 | 'ad_api.api.sd', 22 | 'ad_api.api.dsp', 23 | ], 24 | url='https://github.com/denisneuf/python-amazon-ad-api', 25 | license='MIT', 26 | author='Daniel Alvaro', 27 | author_email='denisneuf@hotmail.com', 28 | description='Python wrapper for the Amazon Advertising API', 29 | ) 30 | -------------------------------------------------------------------------------- /test/localizations/no_result_targeting_expression_v1.json: -------------------------------------------------------------------------------- 1 | { 2 | "responses": [ 3 | { 4 | "sourceTargetingExpression": { 5 | "expression": [{"value": "B000000000", "type": "asinSameAs"}], 6 | "isForNegativeTargeting": true 7 | }, 8 | "localizedTargetingExpressions": {}, 9 | "localizedTargetingExpressionResults": { 10 | "A1RKKUPIHCS9HS": { 11 | "status": "FAILURE", 12 | "messages": [ 13 | "No match for ASIN B000000000 could be found in marketplace A1RKKUPIHCS9HS." 14 | ], 15 | "errorCode": "INTERNAL_ERROR" 16 | } 17 | }, 18 | "status": "FAILURE", 19 | "errorCode": "INTERNAL_ERROR" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /ad_api/base/api_response.py: -------------------------------------------------------------------------------- 1 | import pprint 2 | 3 | 4 | class ApiResponse: 5 | def __init__(self, payload=None, nextToken=None, **kwargs): 6 | # self.payload = payload or kwargs 7 | self.payload = payload 8 | self.headers = kwargs 9 | self.next_token = nextToken 10 | 11 | def __str__(self): 12 | return pprint.pformat(self.__dict__) 13 | 14 | def set_next_token(self, nextToken=None): 15 | if nextToken: 16 | self.next_token = nextToken 17 | else: 18 | self.next_token = self.payload.get("NextToken", None) 19 | return self.next_token 20 | 21 | @staticmethod 22 | def set_rate_limit(headers: dict = None): 23 | try: 24 | return headers["x-amzn-RateLimit-Limit"] 25 | except (AttributeError, KeyError, TypeError): 26 | return None 27 | -------------------------------------------------------------------------------- /test/localizations/no_result_targeting_expression_v2.json: -------------------------------------------------------------------------------- 1 | { 2 | "responses": [ 3 | { 4 | "sourceTargetingExpression": { 5 | "expression": [{"value": "1730635034", "type": "asinCategorySameAs"}], 6 | "isForNegativeTargeting": false 7 | }, 8 | "localizedTargetingExpressions": {}, 9 | "localizedTargetingExpressionResults": { 10 | "ES": { 11 | "status": "FAILURE", 12 | "messages": [ 13 | "There is no category node with the ID 1730635034 in the source marketplace A1F83G8C2ARO7P." 14 | ], 15 | "errorCode": "INTERNAL_ERROR" 16 | } 17 | }, 18 | "status": "FAILURE", 19 | "errorCode": "INTERNAL_ERROR" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /ad_api/api/sp/campaign_consolidated_recommendations.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse 2 | 3 | 4 | class CampaignsRecommendations(Client): 5 | @sp_endpoint('/sp/campaign/recommendations', method='GET') 6 | def list_campaigns_recommendations(self, **kwargs) -> ApiResponse: 7 | r""" 8 | Gets the top consolidated recommendations across bid, budget, targeting for SP campaigns given an advertiser profile id. The recommendations are refreshed everyday. 9 | 10 | Param: 11 | | query **nextToken** (string). Optional. Token to retrieve subsequent page of results. 12 | | query **maxResults** (string). Optional. Limits the number of items to return in the response. 13 | 14 | Returns: 15 | 16 | ApiResponse 17 | 18 | """ 19 | return self._request(kwargs.pop('path'), params=kwargs) 20 | -------------------------------------------------------------------------------- /docs/sb/ads_v4.rst: -------------------------------------------------------------------------------- 1 | Ads 2 | === 3 | 4 | .. autoclass:: ad_api.api.sb.AdsV4 5 | 6 | .. autofunction:: ad_api.api.sb.AdsV4.list_ads(self, version: int = 4, **kwargs) -> ApiResponse: 7 | 8 | .. autofunction:: ad_api.api.sb.AdsV4.create_video_ads(self, version: int = 4, **kwargs) -> ApiResponse: 9 | 10 | .. autofunction:: ad_api.api.sb.AdsV4.create_product_collection_ads(self, version: int = 4, **kwargs) -> ApiResponse: 11 | 12 | .. autofunction:: ad_api.api.sb.AdsV4.create_brand_video_ads(self, version: int = 4, **kwargs) -> ApiResponse: 13 | 14 | .. autofunction:: ad_api.api.sb.AdsV4.create_store_spotlight_ads(self, version: int = 4, **kwargs) -> ApiResponse: 15 | 16 | .. autofunction:: ad_api.api.sb.AdsV4.update_ads(self, version: int = 4, **kwargs) -> ApiResponse: 17 | 18 | .. autofunction:: ad_api.api.sb.AdsV4.delete_ads(self, version: int = 4, **kwargs) -> ApiResponse: 19 | 20 | 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to PYTHON-AMAZON-AD-API [AMAZON ADVERTISING] 2 | ========================================================= 3 | 4 | You are here to help on unofficial Python AmazonAdvertising Api? 5 | Awesome, feel welcome and read the following sections in order to know how to ask questions and how to work on something. 6 | 7 | All members of our community are expected to follow our :doc:`/code-of-conduct`. 8 | Please make sure you are welcoming and friendly in all of our spaces. 9 | 10 | Get in touch 11 | ------------ 12 | 13 | - Report bugs, suggest features or view the source code `on GitHub`_. 14 | 15 | .. _on GitHub: https://github.com/denisneuf/python-amazon-ad-api 16 | 17 | 18 | Contributing to development 19 | --------------------------- 20 | 21 | If you want to deep dive and help out with development on Read the Docs, then 22 | first get the project installed locally according to the CONTRIBUTING.md. -------------------------------------------------------------------------------- /test/portfolios/create.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Apple", 4 | "budget": { 5 | "amount": 100, 6 | "currencyCode": "EUR", 7 | "policy": "monthlyRecurring", 8 | "startDate": "20220418" 9 | }, 10 | "inBudget": true, 11 | "state": "enabled" 12 | }, 13 | { 14 | "name": "Huawei", 15 | "budget": { 16 | "amount": 120, 17 | "currencyCode": "EUR", 18 | "policy": "dateRange", 19 | "startDate": "20220418" 20 | }, 21 | "inBudget": true, 22 | "state": "enabled" 23 | }, 24 | { 25 | "name": "Sony", 26 | "budget": { 27 | "amount": 120, 28 | "currencyCode": "EUR", 29 | "policy": "dateRange", 30 | "startDate": "20220418" 31 | }, 32 | "inBudget": true, 33 | "state": "enabled" 34 | } 35 | ] -------------------------------------------------------------------------------- /test/localizations/request_keywords_marketplace_id.json: -------------------------------------------------------------------------------- 1 | { 2 | "localizeKeywordRequests": [ 3 | {"localizationKeyword": {"keyword": "máquina"}}, 4 | {"localizationKeyword": {"keyword": "diagnosis"}}, 5 | {"localizationKeyword": {"keyword": "diagnosis multimarca"}}, 6 | {"localizationKeyword": {"keyword": "coche"}}, 7 | {"localizationKeyword": {"keyword": "automóvil"}}, 8 | {"localizationKeyword": {"keyword": "frenos"}}, 9 | {"localizationKeyword": {"keyword": "presión"}}, 10 | {"localizationKeyword": {"keyword": "neumáticos"}} 11 | ], 12 | "sourceDetails": {"marketplaceId": "A1RKKUPIHCS9HS"}, 13 | "targetDetails": { 14 | "marketplaceIds": [ 15 | "A1F83G8C2ARO7P", 16 | "A13V1IB3VIYZZH", 17 | "APJ6JRA9NG5V4", 18 | "A1PA6795UKMFR9", 19 | "A1805IZSGTT6HS", 20 | "A2NODRKZP88ZB9" 21 | ] 22 | }, 23 | } 24 | 25 | -------------------------------------------------------------------------------- /ad_api/api/sd/campaigns_budget_usage.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class CampaignsBudgetUsage(Client): 5 | @sp_endpoint('/sd/campaigns/budget/usage', method='POST') 6 | def list_campaigns_budget_usage(self, version: int = 1, **kwargs) -> ApiResponse: 7 | r""" 8 | Budget usage API for SD campaigns 9 | 10 | Request Body 11 | | BudgetUsageCampaignRequest { 12 | | **campaignIds**\* (array) maxItems: 100 [ 13 | | A list of campaign IDs (string) 14 | | ] 15 | | } 16 | Returns 17 | ApiResponse 18 | """ 19 | schema_version = 'application/vnd.sdcampaignbudgetusage.v' + str(version) + '+json' 20 | headers = {"Accept": schema_version, "Content-Type": schema_version} 21 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 22 | -------------------------------------------------------------------------------- /ad_api/api/sp/campaigns_budget_usage.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class CampaignsBudgetUsage(Client): 5 | @sp_endpoint('/sp/campaigns/budget/usage', method='POST') 6 | def list_campaigns_budget_usage(self, version: int = 1, **kwargs) -> ApiResponse: 7 | r""" 8 | Budget usage API for SP campaigns 9 | 10 | Request Body 11 | | BudgetUsageCampaignRequest { 12 | | **campaignIds**\* (array) maxItems: 100 [ 13 | | A list of campaign IDs (string) 14 | | ] 15 | | } 16 | Returns 17 | ApiResponse 18 | """ 19 | schema_version = 'application/vnd.spcampaignbudgetusage.v' + str(version) + '+json' 20 | headers = {"Accept": schema_version, "Content-Type": schema_version} 21 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 22 | -------------------------------------------------------------------------------- /docs/sb/brands.rst: -------------------------------------------------------------------------------- 1 | Brands 2 | ====== 3 | 4 | .. autoclass:: ad_api.api.sb.Brands 5 | 6 | .. autofunction:: ad_api.api.sb.Brands.list_brands(self, **kwargs) -> ApiResponse: 7 | 8 | ### Example python 9 | 10 | .. code-block:: python 11 | 12 | from ad_api.api.sb.brands import Brands 13 | 14 | result = Brands().list_brands() 15 | 16 | logging.info(result) 17 | 18 | 19 | 20 | ### Payload 21 | 22 | .. code-block:: python 23 | 24 | [{'brandEntityId': 'ENTITY5ON7M22396H', 25 | 'brandId': 'A387T2Q1UNXHJK', 26 | 'brandRegistryName': 'Apple'}, 27 | {'brandEntityId': 'ENTITY218756GCCQ6CF', 28 | 'brandId': 'A36UAF6UNGFFAR', 29 | 'brandRegistryName': 'Huawei'}, 30 | {'brandEntityId': 'ENTITY1PRG7GD8FVXA3', 31 | 'brandId': 'A87RK5OLHEBUU5', 32 | 'brandRegistryName': 'Xiaomi'}] 33 | 34 | 35 | NOT AVAILABLE SANDBOX [NOT_FOUND - Could not find resource for full path] -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /docs/sp/campaigns_consolidated_recommendations.rst: -------------------------------------------------------------------------------- 1 | Campaign Consolidated Recommendations 2 | ===================================== 3 | 4 | .. warning:: 5 | 6 | Sponsored Product v3 is not available for Sandbox endpoint 7 | 8 | .. note:: 9 | 10 | This API is version 3.0 11 | 12 | .. note:: 13 | 14 | This API is not clear which marketplaces are supported 15 | 16 | 17 | .. code-block:: python 18 | 19 | { 20 | "code":400, 21 | "responseHeaders":null, 22 | "responseBody":null, 23 | "message":"Marketplace is not supported" 24 | } 25 | 26 | 27 | .. autoclass:: ad_api.api.sp.CampaignsRecommendations 28 | 29 | Endpoints available 30 | 31 | .. csv-table:: 32 | :widths: 10, 35, 50 33 | :header: "Method", "Endpoint", "Description" 34 | 35 | "GET", "/sp/campaign/recommendations", "Gets the top consolidated recommendations." 36 | 37 | 38 | .. autofunction:: ad_api.api.sp.CampaignsRecommendations.list_campaigns_recommendations(self, **kwargs) -> ApiResponse: 39 | -------------------------------------------------------------------------------- /test/localizations/result_targeting_expression_v1.json: -------------------------------------------------------------------------------- 1 | { 2 | "responses": [ 3 | { 4 | "sourceTargetingExpression": { 5 | "expression": [{"value": "B08SWH2KP4", "type": "asinSameAs"}], 6 | "isForNegativeTargeting": true 7 | }, 8 | "localizedTargetingExpressions": { 9 | "A1RKKUPIHCS9HS": { 10 | "expression": [{"value": "B08SWH2KP4", "type": "asinSameAs"}], 11 | "isForNegativeTargeting": true 12 | } 13 | }, 14 | "localizedTargetingExpressionResults": { 15 | "A1RKKUPIHCS9HS": { 16 | "localizedTargetingExpression": { 17 | "expression": [{"value": "B08SWH2KP4", "type": "asinSameAs"}], 18 | "isForNegativeTargeting": true 19 | }, 20 | "status": "SUCCESS" 21 | } 22 | }, 23 | "status": "SUCCESS" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /docs/sp_v3.rst: -------------------------------------------------------------------------------- 1 | 3.0 2 | === 3 | 4 | Sponsored Products 5 | 6 | Documentation: https://advertising.amazon.com/API/docs/en-us/sponsored-products/3-0/openapi/prod#/ 7 | 8 | This specification is available for download from the `Advertising API developer portal `_. 9 | 10 | 11 | .. toctree:: 12 | :caption: Table of Contents 13 | :maxdepth: 1 14 | 15 | sp/campaigns_v3 16 | sp/ad_groups_v3 17 | sp/bid_recommendations_v3 18 | sp/product_ads_v3 19 | sp/budget_rules 20 | sp/campaign_optimization_rules 21 | sp/campaigns_consolidated_recommendations 22 | sp/ranked_keywords_recommendations 23 | sp/keywords_v3 24 | sp/negative_keywords_v3 25 | sp/negative_product_targeting_v3 26 | sp/product_targeting_v3 27 | sp/budget_recommendations 28 | sp/campaign_negative_targets 29 | sp/budget_rules_recommendations 30 | sp/product_recommendations 31 | sp/campaign_budget_usage 32 | sp/campaign_negative_keywords_v3 -------------------------------------------------------------------------------- /ad_api/api/sd/__init__.py: -------------------------------------------------------------------------------- 1 | from .campaigns import Campaigns 2 | from .ad_groups import AdGroups 3 | from .product_ads import ProductAds 4 | from .reports import Reports 5 | from .snapshots import Snapshots 6 | from .creatives import Creatives 7 | from .product_targeting import Targets 8 | from .negative_product_targeting import NegativeTargets 9 | from .targeting_recommendations import TargetsRecommendations 10 | from .bid_recommendations import BidRecommendations 11 | from .brand_safety import BrandSafety 12 | from .forecast import Forecast 13 | from .recommendations import Recommendations 14 | from .campaigns_budget_usage import CampaignsBudgetUsage 15 | from .budget_rules import BudgetRules 16 | 17 | __all__ = [ 18 | "Campaigns", 19 | "AdGroups", 20 | "Reports", 21 | "ProductAds", 22 | "Targets", 23 | "NegativeTargets", 24 | "TargetsRecommendations", 25 | "BidRecommendations", 26 | "Creatives", 27 | "BrandSafety", 28 | "Forecast", 29 | "Recommendations", 30 | "CampaignsBudgetUsage", 31 | "BudgetRules", 32 | ] 33 | -------------------------------------------------------------------------------- /ad_api/api/sp/budget_recommendations.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class BudgetRecommendations(Client): 5 | @sp_endpoint('/sp/campaigns/budgetRecommendations', method='POST') 6 | def list_campaigns_budget_recommendations(self, version: int = 3, **kwargs) -> ApiResponse: 7 | r""" 8 | Get recommended daily budget and estimated missed opportunities for campaigns. 9 | 10 | Request Body (required) 11 | | BudgetRecommendationRequest { 12 | | **campaignIds**\* (array) The campaign identifier. minItems: 1. maxItems: 100 [ 13 | | List of campaigns. (string) 14 | | ] 15 | | } 16 | Returns 17 | ApiResponse 18 | """ 19 | contentType = 'application/vnd.budgetrecommendation.v' + str(version) + '+json' 20 | headers = {'Content-Type': contentType} 21 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 22 | -------------------------------------------------------------------------------- /ad_api/api/sp/budget_rules_recommendations.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class BudgetRulesRecommendations(Client): 5 | @sp_endpoint('/sp/campaigns/budgetRules/recommendations', method='POST') 6 | def list_campaigns_budget_rules_recommendations(self, version: int = 3, **kwargs) -> ApiResponse: 7 | r""" 8 | Gets a list of special events with suggested date range and suggested budget increase for a campaign specified by identifier. 9 | 10 | Request Body (required) 11 | | SPBudgetRulesRecommendationEventRequest { 12 | | **campaignId**\* (string) The campaign identifier. 13 | | } 14 | Returns 15 | ApiResponse 16 | """ 17 | contentType = 'application/vnd.spbudgetrulesrecommendation.v' + str(version) + '+json' 18 | headers = {'Content-Type': contentType, "Accept": contentType} 19 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 20 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = python-amazon-ad-api 3 | version = 0.6.7 4 | author = Daniel Alvaro 5 | author_email = denisneuf@hotmail.com 6 | description = Python wrapper for the Amazon Advertising API 7 | long_description = file: README.md 8 | long_description_content_type = text/markdown 9 | url = https://github.com/denisneuf/python-amazon-ad-api 10 | project_urls = 11 | Bug Tracker = https://github.com/denisneuf/python-amazon-ad-api/issues 12 | classifiers = 13 | Programming Language :: Python :: 3 14 | License :: OSI Approved :: MIT License 15 | Operating System :: OS Independent 16 | 17 | [flake8] 18 | max-line-length = 200 19 | exclude =.git,__pycache__,docs/source/conf.py,build,dist,tests 20 | ignore = I101,D100,D101,D102,D103,D104,D105,D107,D401,E203,I900,N802,N806,N812,W503,S311,S605,S607,ISC003,ISC001,T101,T000,F541,PL123 21 | per-file-ignores = __init__.py:F401,F403 22 | 23 | [mypy] 24 | ignore_missing_imports = True 25 | disallow_untyped_defs = True 26 | check_untyped_defs = True 27 | warn_redundant_casts = True 28 | no_implicit_optional = True 29 | strict_optional = True 30 | -------------------------------------------------------------------------------- /ad_api/api/sb/landing_page_asins.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class PageAsins(Client): 5 | """ 6 | Use the Amazon Advertising API for Sponsored Brands for campaign, ad group, keyword, negative keyword, drafts, Stores, landing pages, and Brands management operations. For more information about Sponsored Brands, see the Sponsored Brands Support Center. For onboarding information, see the account setup topic. 7 | """ 8 | 9 | @sp_endpoint('/pageAsins', method='GET') 10 | def get_page_asins(self, **kwargs) -> ApiResponse: 11 | """ 12 | 13 | Gets ASIN information for a specified address. 14 | 15 | Keyword Args 16 | | query **pageUrl**:*string* | Required. For sellers, the address of a Store page. Vendors may also specify the address of a custom landing page. For more information, see the [Stores section](https://advertising.amazon.com/help#GPRM3ZHEXEY5RBFZ) of the Amazon Advertising support center. 17 | 18 | Returns 19 | asinList 20 | """ 21 | return self._request(kwargs.pop('path'), params=kwargs) 22 | -------------------------------------------------------------------------------- /ad_api/base/__init__.py: -------------------------------------------------------------------------------- 1 | from .base_client import BaseClient 2 | from .client import Client 3 | from .helpers import fill_query_params, sp_endpoint 4 | from .marketplaces import Marketplaces, MarketplacesIds, Currencies, CurrencySymbols, Locales 5 | from .exceptions import AdvertisingApiException 6 | from .exceptions import AdvertisingApiBadRequestException 7 | from .exceptions import AdvertisingApiForbiddenException 8 | from .exceptions import AdvertisingTypeException 9 | from .credential_provider import CredentialProvider, MissingCredentials 10 | from .api_response import ApiResponse 11 | from .utils import Utils 12 | 13 | __all__ = [ 14 | 'AccessTokenClient', 15 | 'ApiResponse', 16 | 'Client', 17 | 'BaseClient', 18 | 'Marketplaces', 19 | 'MarketplacesIds', 20 | 'Currencies', 21 | 'CurrencySymbols', 22 | 'Locales', 23 | 'fill_query_params', 24 | 'sp_endpoint', 25 | 'AdvertisingApiException', 26 | 'AdvertisingApiBadRequestException', 27 | 'AdvertisingApiForbiddenException', 28 | 'AdvertisingTypeException', 29 | 'CredentialProvider', 30 | 'MissingCredentials', 31 | 'Utils', 32 | ] 33 | -------------------------------------------------------------------------------- /test/localizations/result_keywords_marketplace_id.json: -------------------------------------------------------------------------------- 1 | { 2 | "localizedKeywordResponses": [ 3 | { 4 | "sourceKeyword": {"keyword": "rondelle d'essuie-glace"}, 5 | "localizedKeywords": { 6 | "APJ6JRA9NG5V4": {"keyword": "tergicristallo"}, 7 | "A1RKKUPIHCS9HS": {"keyword": "limpiaparabrisas"}, 8 | "A1F83G8C2ARO7P": {"keyword": "windscreen wiper washer"} 9 | }, 10 | "localizedKeywordResults": { 11 | "APJ6JRA9NG5V4": { 12 | "localizedKeyword": {"keyword": "tergicristallo"}, 13 | "status": "SUCCESS" 14 | }, 15 | "A1RKKUPIHCS9HS": { 16 | "localizedKeyword": {"keyword": "limpiaparabrisas"}, 17 | "status": "SUCCESS" 18 | }, 19 | "A1F83G8C2ARO7P": { 20 | "localizedKeyword": {"keyword": "windscreen wiper washer"}, 21 | "status": "SUCCESS" 22 | }, 23 | }, 24 | "status": "SUCCESS" 25 | } 26 | ] 27 | } 28 | 29 | -------------------------------------------------------------------------------- /test/localizations/result_targeting_expression_v2.json: -------------------------------------------------------------------------------- 1 | { 2 | "responses": [ 3 | { 4 | "sourceTargetingExpression": { 5 | "expression": [{"value": "1730635031", "type": "asinCategorySameAs"}], 6 | "isForNegativeTargeting": false 7 | }, 8 | "localizedTargetingExpressions": { 9 | "ES": { 10 | "expression": [ 11 | {"value": "1909320031", "type": "asinCategorySameAs"} 12 | ], 13 | "isForNegativeTargeting": false 14 | } 15 | }, 16 | "localizedTargetingExpressionResults": { 17 | "ES": { 18 | "localizedTargetingExpression": { 19 | "expression": [ 20 | {"value": "1909320031", "type": "asinCategorySameAs"} 21 | ], 22 | "isForNegativeTargeting": false 23 | }, 24 | "status": "SUCCESS" 25 | } 26 | }, 27 | "status": "SUCCESS" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Michael Primke 4 | Copyright (c) 2021 Daniel Álvaro 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /test/campaigns/sb-sx-create-campaign-keywords.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "SB.Campaign.Keyword.Api.001", 4 | "budget": 100, 5 | "budgetType": "lifetime", 6 | "startDate": "20240131", 7 | "endDate": "20250131", 8 | "adFormat": "productCollection", 9 | "brandEntityId": "ENTITY218756GCCQ6CF", 10 | "bidOptimization": true, 11 | "creative": { 12 | "brandName": "Apple", 13 | "brandLogoAssetID": "AW0_z87632Ghstax", 14 | "headline": "Apple Iphone XS", 15 | "asins": [ 16 | "B08N5WRTN2", "B081G9YQ73", "B008ATNJNS" 17 | ], 18 | "shouldOptimizeAsins": false 19 | }, 20 | "landingPage": { 21 | "asins": [ 22 | "B08N5WRTN2", "B081G9YQ73", "B008ATNJNS" 23 | ] 24 | }, 25 | "keywords": [ 26 | { 27 | "keywordText": "limpieza professional", 28 | "nativeLanguageKeyword": "cleaning profesional", 29 | "nativeLanguageLocale": "en_GB", 30 | "matchType": "broad", 31 | "bid": 0.1 32 | } 33 | ], 34 | "negativeKeywords": [ 35 | { 36 | "keywordText": "Huawei", 37 | "matchType": "negativeExact" 38 | } 39 | ] 40 | } 41 | ] -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Upload Python Package 10 | 11 | on: 12 | release: 13 | types: [published] 14 | 15 | jobs: 16 | deploy: 17 | 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Set up Python 23 | uses: actions/setup-python@v2 24 | with: 25 | python-version: '3.9' 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install build 30 | - name: Build package 31 | run: python -m build 32 | - name: Publish package 33 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 34 | with: 35 | user: __token__ 36 | password: ${{ secrets.PYPI_API_TOKEN }} 37 | -------------------------------------------------------------------------------- /docs/disclaimer.rst: -------------------------------------------------------------------------------- 1 | Disclaimer 2 | ========== 3 | 4 | .. code-block:: bash 5 | 6 | MIT License 7 | 8 | Copyright (c) 2021-2023 denisneuf 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | -------------------------------------------------------------------------------- /docs/sd/creatives.rst: -------------------------------------------------------------------------------- 1 | Creatives 2 | ========= 3 | 4 | .. warning:: 5 | 6 | Sponsored Display is not available for Sandbox endpoint 7 | 8 | .. autoclass:: ad_api.api.sd.Creatives 9 | 10 | Endpoints available 11 | 12 | .. csv-table:: 13 | :widths: 25, 25, 50 14 | :header: "Method", "Endpoint", "Description" 15 | 16 | "GET", "/sd/creatives", "Gets a list of creatives." 17 | "PUT", "/sd/creatives", "Updates one or more creatives." 18 | "POST", "/sd/creatives", "A POST request of one or more creatives." 19 | "POST", "/sd/creatives/preview", "Gets creative preview HTML." 20 | "GET", "/sd/moderation/creatives", "Gets a list of creative moderations" 21 | 22 | .. autofunction:: ad_api.api.sd.Creatives.list_creatives(self, **kwargs) -> ApiResponse: 23 | 24 | .. autofunction:: ad_api.api.sd.Creatives.edit_creatives(self, **kwargs) -> ApiResponse: 25 | 26 | .. autofunction:: ad_api.api.sd.Creatives.create_creatives(self, **kwargs) -> ApiResponse: 27 | 28 | .. autofunction:: ad_api.api.sd.Creatives.list_moderation_creatives(self, **kwargs) -> ApiResponse: 29 | 30 | .. autofunction:: ad_api.api.sd.Creatives.show_creative_preview(self, **kwargs) -> ApiResponse: 31 | -------------------------------------------------------------------------------- /ad_api/api/sb/brands.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse 2 | 3 | 4 | class Brands(Client): 5 | """ 6 | Use the Amazon Advertising API for Sponsored Brands for campaign, ad group, keyword, negative keyword, drafts, Stores, landing pages, and Brands management operations. For more information about Sponsored Brands, see the Sponsored Brands Support Center. For onboarding information, see the account setup topic. 7 | """ 8 | 9 | @sp_endpoint('/brands', method='GET') 10 | def list_brands(self, **kwargs) -> ApiResponse: 11 | """ 12 | Gets an array of Brand data objects for the Brand associated with the profile ID passed in the header. For more information about Brands, see [Brand Services](https://brandservices.amazon.com/). 13 | 14 | Keyword Args 15 | | None 16 | 17 | Returns: 18 | ApiResponse payload: 19 | 20 | | '**brandId**': *string*, {'description': 'The Brand identifier.'} 21 | | '**brandEntityId**': *string*, {'description': 'The Brand entity identifier.'} 22 | | '**brandRegistryName**': *string*, {'description': 'The Brand name.'} 23 | 24 | """ 25 | return self._request(kwargs.pop('path'), params=kwargs) 26 | -------------------------------------------------------------------------------- /docs/code.rst: -------------------------------------------------------------------------------- 1 | From Code 2 | ========= 3 | 4 | You can override/set credentials from code by passing a ``dict`` to the client. 5 | 6 | If you pass a value in credentials, other credentials from env variables or from a config file will be ignored. 7 | 8 | Required fields: 9 | 10 | .. code-block:: python 11 | 12 | 13 | credentials = dict( 14 | refresh_token='your-refresh_token', 15 | client_id='your-client_id', 16 | client_secret='your-client_secret', 17 | profile_id='your-profile_id', 18 | ) 19 | 20 | ***** 21 | Usage 22 | ***** 23 | 24 | .. code-block:: python 25 | 26 | import logging 27 | from ad_api.base import AdvertisingApiException 28 | from ad_api.api import sponsored_products 29 | 30 | credentials = dict( 31 | refresh_token='your-refresh_token', 32 | client_id='your-client_id', 33 | client_secret='your-client_secret', 34 | profile_id='your-profile_id', 35 | ) 36 | 37 | try: 38 | 39 | status = 'enabled' 40 | 41 | result=sponsored_products.Campaigns(credentials=credentials, debug=True).list_campaigns( 42 | stateFilter=status 43 | ) 44 | 45 | payload = result.payload 46 | 47 | logging.info(payload) 48 | 49 | except AdvertisingApiException as error: 50 | 51 | logging.info(error) 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /docs/sp_v2.rst: -------------------------------------------------------------------------------- 1 | 2.0 2 | === 3 | 4 | .. deprecated:: 4.0.2 5 | 6 | .. warning:: 7 | 8 | There is a new version 3 of Sponsored Product API, please check the `migration guide`_. 9 | 10 | Amazon Advertising API - Sponsored Products 11 | 12 | Documentation: https://advertising.amazon.com/API/docs/en-us/sponsored-products/2-0/openapi#/ 13 | Use the Amazon Advertising API for Sponsored Products for campaign, ad group, keyword, negative keyword, and product ad management operations. For more information about Sponsored Products, see the Sponsored Products Support Center. For onboarding information, see the account setup topic. 14 | 15 | This specification is available for download from the `Advertising API developer portal `_. 16 | 17 | 18 | .. toctree:: 19 | :maxdepth: 1 20 | 21 | sp/campaigns 22 | sp/ad_groups 23 | sp/bid_recommendations 24 | sp/keywords 25 | sp/negative_keywords 26 | sp/campaign_negative_keywords 27 | sp/suggested_keywords 28 | sp/product_ads 29 | sp/product_targeting 30 | sp/negative_product_targeting 31 | sp/reports 32 | sp/snapshots 33 | 34 | ********** 35 | References 36 | ********** 37 | 38 | .. target-notes:: 39 | 40 | .. _`migration guide`: https://advertising.amazon.com/API/docs/en-us/sponsored-products/v3-migration-guide -------------------------------------------------------------------------------- /docs/sp/campaign_negative_keywords_v3.rst: -------------------------------------------------------------------------------- 1 | Campaign Negative Keywords 2 | ========================== 3 | 4 | .. warning:: 5 | 6 | Sponsored Product v3 is not available for Sandbox endpoint 7 | 8 | .. note:: 9 | 10 | This API is version 3.0 11 | 12 | .. autoclass:: ad_api.api.sp.CampaignNegativeKeywordsV3 13 | 14 | Endpoints available 15 | 16 | .. csv-table:: 17 | :widths: 10, 35, 50 18 | :header: "Method", "Endpoint", "Description" 19 | 20 | "POST", "/sp/campaignNegativeKeywords/delete", "Delete campaigns negative keywords" 21 | "POST", "/sp/campaignNegativeKeywords/list", "List campaigns negative keywords" 22 | "POST", "/sp/campaignNegativeKeywords", "Create campaigns negative keywords" 23 | "PUT", "/sp/campaignNegativeKeywords", "Update campaigns negative keywords" 24 | 25 | 26 | .. autofunction:: ad_api.api.sp.CampaignNegativeKeywordsV3.delete_campaign_negative_keyword(self, version: int = 3, **kwargs) -> ApiResponse: 27 | .. autofunction:: ad_api.api.sp.CampaignNegativeKeywordsV3.list_campaign_negative_keywords(self, version: int = 3, **kwargs) -> ApiResponse: 28 | .. autofunction:: ad_api.api.sp.CampaignNegativeKeywordsV3.create_campaign_negative_keywords(self, version: int = 3, prefer: bool = False, **kwargs) -> ApiResponse: 29 | .. autofunction:: ad_api.api.sp.CampaignNegativeKeywordsV3.edit_campaign_negative_keywords(self, version: int = 3, prefer: bool = False, **kwargs) -> ApiResponse: 30 | -------------------------------------------------------------------------------- /docs/sp/campaign_negative_targets.rst: -------------------------------------------------------------------------------- 1 | Campaign Negative Targeting Clauses 2 | =================================== 3 | 4 | .. warning:: 5 | 6 | Sponsored Product v3 is not available for Sandbox endpoint 7 | 8 | .. note:: 9 | 10 | This API is version 3.0 11 | 12 | 13 | .. autoclass:: ad_api.api.sp.CampaignNegativeTargets 14 | 15 | Endpoints available 16 | 17 | .. csv-table:: 18 | :widths: 10, 35, 50 19 | :header: "Method", "Endpoint", "Description" 20 | 21 | "DELETE", "/sp/campaignNegativeTargets/delete", "Deletes Campaign Negative Targeting Clauses" 22 | "POST", "/sp/campaignNegativeTargets", "Create Campaign Negative Targeting Clauses" 23 | "PUT", "/sp/campaignNegativeTargets", "Updates Campaign Negative Targeting Clauses" 24 | "POST", "/sp/campaignNegativeTargets/list", "List Campaign Negative Targeting Clauses" 25 | 26 | 27 | .. autofunction:: ad_api.api.sp.CampaignNegativeTargets.delete_campaign_negative_targets(self, version: int = 3, **kwargs) -> ApiResponse: 28 | .. autofunction:: ad_api.api.sp.CampaignNegativeTargets.create_campaign_negative_targets(self, version: int = 3, prefer: bool = False, **kwargs) -> ApiResponse: 29 | .. autofunction:: ad_api.api.sp.CampaignNegativeTargets.edit_negative_product_targets(self, version: int = 3, prefer: bool = False, **kwargs) -> ApiResponse: 30 | .. autofunction:: ad_api.api.sp.CampaignNegativeTargets.list_campaign_negative_targets(self, version: int = 3, **kwargs) -> ApiResponse: 31 | -------------------------------------------------------------------------------- /ad_api/api/sd/recommendations.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class Recommendations(Client): 5 | @sp_endpoint('/sd/recommendations/creative/headline', method='POST') 6 | def list_headline_recommendations(self, version: float = 4.0, **kwargs) -> ApiResponse: 7 | r""" 8 | You can use this Sponsored Display API to retrieve creative headline recommendations from an array of ASINs. 9 | 10 | Request Body 11 | | SDHeadlineRecommendationRequest { 12 | | Request structure of SD headline recommendation API. 13 | | **asins** (array) minItems: 0 maxItems: 100 [ 14 | | (string) An array of ASINs associated with the creative. 15 | | ] 16 | | **maxNumRecommendations** (array) Maximum number of recommendations that API should return. Response will [0, maxNumRecommendations] recommendations (recommendations are not guaranteed as there can be instances where the ML model can not generate policy compliant headlines for the given set of asins). maximum: 10. minimum: 1 . 17 | | **adFormat** (string): Enum: [ SPONSORED_DISPLAY ] 18 | | } 19 | Returns 20 | ApiResponse 21 | """ 22 | schema = 'application/vnd.sdheadlinerecommendationrequest.v' + str(version) + "+json" 23 | headers = {'Content-Type': schema} 24 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 25 | -------------------------------------------------------------------------------- /docs/sb/landing_page_asins.rst: -------------------------------------------------------------------------------- 1 | Landing Page Asins 2 | ================== 3 | 4 | .. autoclass:: ad_api.api.sb.PageAsins 5 | 6 | .. autofunction:: ad_api.api.sb.PageAsins.get_page_asins(self, **kwargs) -> ApiResponse: 7 | 8 | ### Example python 9 | 10 | .. code-block:: python 11 | 12 | from ad_api.api.sb.landing_page_asins import PageAsins 13 | 14 | page_url = 'https://www.amazon.es/stores/page/49D4CB50-9C2F-46D5-8E50-5505529C790D' 15 | 16 | result = PageAsins().get_page_asins( 17 | pageUrl=page_url 18 | ) 19 | 20 | logging.info(result) 21 | 22 | 23 | ### Payload 24 | 25 | .. code-block:: python 26 | 27 | {'asinList': ['B08N5WRTN2', 28 | 'B081G9YQ73', 29 | 'B008ATNJNS', 30 | 'B08N5VXMK6', 31 | 'B016UPAVDE', 32 | 'B08N5S5HH5', 33 | 'B016MUBL4U', 34 | 'B08N5WM84C', 35 | 'B08N5TLVQ2', 36 | 'B0863B2L69', 37 | 'B08N5TLB5J', 38 | 'B081GBLPTB', 39 | 'B081G4SK26', 40 | 'B08N5VT5SV', 41 | 'B0863G2M7F', 42 | 'B07BRLMY93', 43 | 'B08N5V4CKB', 44 | 'B086395QZM', 45 | 'B081GC15CY', 46 | 'B086395QZP'], 47 | 'code': 'SUCCESS'} 48 | 49 | NOT AVAILABLE SANDBOX [NOT_FOUND - Could not find resource for full path] -------------------------------------------------------------------------------- /ad_api/api/sb/__init__.py: -------------------------------------------------------------------------------- 1 | from .campaigns import Campaigns 2 | from .campaigns_v4 import CampaignsV4 3 | from .brands import Brands 4 | from .landing_page_asins import PageAsins 5 | from .ad_groups import AdGroups 6 | from .ad_groups_v4 import AdGroupsV4 7 | from .ad_creatives_v4 import AdCreativesV4 8 | from .ads_v4 import AdsV4 9 | from .moderation import Moderation 10 | from .keywords import Keywords 11 | from .recommendations import Recommendations 12 | from .keywords_recommendations import KeywordsRecommendations 13 | from .forecast import Forecast 14 | from .negative_keywords import NegativeKeywords 15 | from .product_targeting import Targets 16 | from .negative_product_targeting import NegativeTargets 17 | from .targeting_recommendations import TargetsRecommendations 18 | from .bid_recommendations import BidRecommendations 19 | from .stores import Stores 20 | from .media import Media 21 | from .reports import Reports 22 | from .snapshots import Snapshots 23 | from .benchmarks import Benchmarks 24 | from .themes import Themes 25 | 26 | __all__ = [ 27 | "Brands", 28 | "PageAsins", 29 | "Campaigns", 30 | "CampaignsV4", 31 | "AdCreativesV4", 32 | "AdGroups", 33 | "AdGroupsV4", 34 | "AdsV4", 35 | "Moderation", 36 | "Keywords", 37 | "Recommendations", 38 | "KeywordsRecommendations", 39 | "Forecast", 40 | "NegativeKeywords", 41 | "Targets", 42 | "NegativeTargets", 43 | "TargetsRecommendations", 44 | "BidRecommendations", 45 | "Stores", 46 | "Media", 47 | "Reports", 48 | "Snapshots", 49 | "Benchmarks", 50 | "Themes" 51 | ] 52 | -------------------------------------------------------------------------------- /docs/api/advertising_test_account.rst: -------------------------------------------------------------------------------- 1 | Advertising Test Account 2 | ======================== 3 | 4 | .. autoclass:: ad_api.api.AdvertisingTestAccount 5 | 6 | .. autofunction:: ad_api.api.AdvertisingTestAccount.create_test_account 7 | 8 | ### Example python 9 | 10 | .. code-block:: python 11 | 12 | import json 13 | from ad_api.api import AdvertisingTestAccount 14 | 15 | data = \ 16 | { 17 | "countryCode": "ES", 18 | "accountMetaData": 19 | { 20 | "vendorCode": "ABCDE" 21 | }, 22 | "accountType": "VENDOR" 23 | } 24 | 25 | result = AdvertisingTestAccount(account=store, marketplace=marketplace, debug=True).create_test_account( 26 | body=json.dumps(data) 27 | ) 28 | 29 | .. code-block:: python 30 | 31 | { 32 | 'requestId': 'A7BCDGCEVXQ1CJJ4301V' 33 | } 34 | 35 | .. autofunction:: ad_api.api.AdvertisingTestAccount.get_test_account 36 | 37 | ### Example python 38 | 39 | .. code-block:: python 40 | 41 | from ad_api.api import AdvertisingTestAccount 42 | 43 | request_id = "A7BCDGCEVXQ1CJJ4301V" 44 | 45 | result = AdvertisingTestAccount(account=store, marketplace=marketplace, debug=True).get_test_account( 46 | requestId=request_id 47 | ) 48 | 49 | 50 | .. code-block:: python 51 | 52 | { 53 | "accountType": "VENDOR", 54 | "asins": [], 55 | "countryCode": "ES", 56 | "id": "ENTITY1MBW4T9T7Z5PC", 57 | "status": "COMPLETED" 58 | } -------------------------------------------------------------------------------- /docs/variables.rst: -------------------------------------------------------------------------------- 1 | Environment Variables 2 | ===================== 3 | 4 | ****************************** 5 | Use with environment variables 6 | ****************************** 7 | 8 | ===================== ========================================================================================================= 9 | ENVIRONMENT VARIABLE DESCRIPTION 10 | ===================== ========================================================================================================= 11 | AD_API_REFRESH_TOKEN The refresh token used obtained via authorization 12 | AD_API_CLIENT_ID Your login with amazon app id 13 | AD_API_CLIENT_SECRET Your login with amazon client secret 14 | AD_API_PROFILE_ID Your profile id as Amazon Ad 15 | ===================== ========================================================================================================= 16 | 17 | To set environment variables in your python script, use 18 | 19 | .. code-block:: python 20 | 21 | import os 22 | 23 | os.environ.setdefault('AD_API_REFRESH_TOKEN', 'Your-Token-Here') 24 | os.environ.setdefault('AD_API_CLIENT_ID', 'Your-Client_Id-Here') 25 | os.environ.setdefault('AD_API_CLIENT_SECRET', 'Your-Client_Secret-Here') 26 | os.environ.setdefault('AD_API_PROFILE_ID', 'Your-Profile_Id-Here') 27 | 28 | 29 | ************************** 30 | Note 31 | ************************** 32 | 33 | You still need create the .env file or by default the mode configuration will be sandbox for testing 34 | 35 | .. code-block:: bash 36 | 37 | # environment variables defined inside a .env file 38 | # AWS_ENV=SANDBOX 39 | AWS_ENV=PRODUCTION -------------------------------------------------------------------------------- /docs/api/validation_configurations.rst: -------------------------------------------------------------------------------- 1 | Validation Configuration 2 | ======================== 3 | 4 | .. autoclass:: ad_api.api.ValidationConfigurations 5 | 6 | .. autofunction:: ad_api.api.ValidationConfigurations.retrieve_validation_campaigns(self, **kwargs) -> ApiResponse: 7 | 8 | ### Example python 9 | 10 | .. code-block:: python 11 | 12 | from ad_api.api import ValidationConfigurations 13 | 14 | dictionary = \ 15 | { 16 | "countryCodesList": [ 17 | "ES" 18 | ], 19 | "entityTypesList": [ 20 | "SELLER" 21 | ], 22 | "programTypesList": [ 23 | "SB" 24 | ] 25 | } 26 | 27 | result = ValidationConfigurations().retrieve_validation_campaigns( 28 | body=dictionary 29 | ) 30 | payload = result.payload 31 | 32 | 33 | .. autofunction:: ad_api.api.ValidationConfigurations.retrieve_validation_targeting_clauses(self, **kwargs) -> ApiResponse: 34 | 35 | ### Example python 36 | 37 | .. code-block:: python 38 | 39 | from ad_api.api import ValidationConfigurations 40 | 41 | dictionary = \ 42 | { 43 | "countryCodesList": [ 44 | "ES" 45 | ], 46 | "entityTypesList": [ 47 | "VENDOR" 48 | ], 49 | "programTypesList": [ 50 | "SP" 51 | ] 52 | } 53 | 54 | result = ValidationConfigurations().retrieve_validation_targeting_clauses( 55 | body=dictionary 56 | ) 57 | payload = result.payload 58 | 59 | 60 | -------------------------------------------------------------------------------- /test/localizations/result_keywords_locale.txt: -------------------------------------------------------------------------------- 1 | { 2 | "localizedKeywordResponses": [ 3 | { 4 | "sourceKeyword": {"keyword": "máquina"}, 5 | "localizedKeywords": { 6 | "it_IT": {"keyword": "macchina"}, 7 | "en_GB": {"keyword": "machine"}, 8 | "sv_SE": {"keyword": "maskin"}, 9 | "fr_FR": {"keyword": "machine"}, 10 | "de_DE": {"keyword": "Maschine"}, 11 | "nl_NL": {"keyword": "machine"} 12 | }, 13 | "localizedKeywordResults": { 14 | "it_IT": { 15 | "localizedKeyword": {"keyword": "macchina"}, 16 | "status": "SUCCESS" 17 | }, 18 | "en_GB": { 19 | "localizedKeyword": {"keyword": "machine"}, 20 | "status": "SUCCESS" 21 | }, 22 | "sv_SE": { 23 | "localizedKeyword": {"keyword": "maskin"}, 24 | "status": "SUCCESS" 25 | }, 26 | "fr_FR": { 27 | "localizedKeyword": {"keyword": "machine"}, 28 | "status": "SUCCESS" 29 | }, 30 | "de_DE": { 31 | "localizedKeyword": {"keyword": "Maschine"}, 32 | "status": "SUCCESS" 33 | }, 34 | "nl_NL": { 35 | "localizedKeyword": {"keyword": "machine"}, 36 | "status": "SUCCESS" 37 | }, 38 | }, 39 | "status": "SUCCESS" 40 | }, 41 | {...} 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /docs/sd/brand_safety.rst: -------------------------------------------------------------------------------- 1 | Brand Safety List 2 | ================= 3 | 4 | .. warning:: 5 | 6 | Sponsored Display is not available for Sandbox endpoint 7 | 8 | .. autoclass:: ad_api.api.sd.BrandSafety 9 | 10 | Endpoints available 11 | 12 | .. csv-table:: 13 | :widths: 25, 25, 50 14 | :header: "Method", "Endpoint", "Description" 15 | 16 | "GET", "/sd/brandSafety/deny", "Gets a list of websites/apps that are on the advertiser's Brand Safety Deny List." 17 | "POST", "/sd/brandSafety/deny", "Creates one or more domains to add to a Brand Safety Deny Lis" 18 | "DELETE", "/sd/brandSafety/deny", "Archives all of the domains in the Brand Safety Deny List." 19 | "GET", "/sd/brandSafety/{requestId}/results", "Gets the results for the given request" 20 | "GET", "/sd/brandSafety/{requestId}/status", "Gets the status for the given request" 21 | "GET", "/sd/brandSafety/status", "List status of all Brand Safety List requests." 22 | 23 | .. autofunction:: ad_api.api.sd.BrandSafety.list_brand_safety(self, **kwargs) -> ApiResponse: 24 | 25 | .. autofunction:: ad_api.api.sd.BrandSafety.post_brand_safety(self, **kwargs) -> ApiResponse: 26 | 27 | .. autofunction:: ad_api.api.sd.BrandSafety.delete_brand_safety(self, **kwargs) -> ApiResponse: 28 | 29 | .. autofunction:: ad_api.api.sd.BrandSafety.get_result_brand_safety_request(self, requestId, **kwargs) -> ApiResponse: 30 | 31 | .. autofunction:: ad_api.api.sd.BrandSafety.get_status_brand_safety_request(self, requestId, **kwargs) -> ApiResponse: 32 | 33 | .. autofunction:: ad_api.api.sd.BrandSafety.list_brand_safety_requests_history(self, **kwargs) -> ApiResponse: -------------------------------------------------------------------------------- /ad_api/api/advertising_test_account.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class AdvertisingTestAccount(Client): 5 | """ 6 | Create test advertising account for 3P API integrators 7 | """ 8 | 9 | @sp_endpoint('/testAccounts', method='POST') 10 | def create_test_account(self, **kwargs) -> ApiResponse: 11 | r""" 12 | create_test_account(body: dict or str) -> ApiResponse: 13 | API to create test accounts 14 | 15 | Submit a account creation request. You can create up to 1 test account type per marketplace. 16 | 17 | Request body 18 | | **countryCode** (string): [required] Country code of the test account. [ US, CA, MX, BR, UK, DE, FR, ES, IT, CN, JP, AU, AE, SA, NL ] 19 | | **accountMetaData** (string): 20 | | vendorCode [optional] Vendor code that needs to be associated with the vendor account. example: ABCDE 21 | | **accountType** (string): [required] Type of test account. [ VENDOR, AUTHOR ] 22 | 23 | Returns: 24 | ApiResponse 25 | 26 | """ 27 | body = Utils.convert_body(kwargs.pop('body'), wrap=False) 28 | return self._request(kwargs.pop('path'), data=body, params=kwargs) 29 | 30 | @sp_endpoint('/testAccounts', method='GET') 31 | def get_test_account(self, **kwargs) -> ApiResponse: 32 | r""" 33 | get_test_account(requestId: str) -> ApiResponse 34 | 35 | Keyword Args 36 | | query **requestId** (string): [required] request id. 37 | 38 | Returns: 39 | ApiResponse 40 | """ 41 | return self._request(kwargs.pop('path'), params=kwargs) 42 | -------------------------------------------------------------------------------- /ad_api/api/__init__.py: -------------------------------------------------------------------------------- 1 | from .account import Account 2 | from .profiles import Profiles 3 | from .manager_accounts import ManagerAccounts 4 | from .invoices import Invoices 5 | from .billing import Billing 6 | from .eligibility import Eligibility 7 | from .metadata import Metadata 8 | from .history import History 9 | from .creative_assets import CreativeAssets 10 | from .localization import Localization 11 | from .audiences import Audiences 12 | from .portfolios import Portfolios 13 | from .portfolios_v3 import PortfoliosV3 14 | from .insights import Insights 15 | from . import sp as sponsored_products 16 | from . import sb as sponsored_brands 17 | from . import sd as sponsored_display 18 | from .attribution import Attribution 19 | from .brand_metrics import BrandMetrics 20 | from .advertising_test_account import AdvertisingTestAccount 21 | from .reports import Reports 22 | from .validation_configurations import ValidationConfigurations 23 | from .recommendations import Recommendations 24 | from .stream import Stream 25 | from .exports import Exports 26 | from .stores import Stores 27 | 28 | __all__ = [ 29 | "sp", 30 | "sb", 31 | "sd", 32 | "sponsored_products", 33 | "sponsored_brands", 34 | "sponsored_display", 35 | "Profiles", 36 | "ManagerAccounts", 37 | "Invoices", 38 | "Billing", 39 | "Eligibility", 40 | "Metadata", 41 | "History", 42 | "CreativeAssets", 43 | "Localization", 44 | "Audiences", 45 | "Portfolios", 46 | "Insights", 47 | "Attribution", 48 | "BrandMetrics", 49 | "AdvertisingTestAccount", 50 | "Reports", 51 | "ValidationConfigurations", 52 | "Recommendations", 53 | "Stream", 54 | "Exports", 55 | "Stores", 56 | "Account" 57 | ] 58 | -------------------------------------------------------------------------------- /ad_api/api/invoices.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, fill_query_params, ApiResponse 2 | 3 | 4 | class Invoices(Client): 5 | @sp_endpoint('/invoices', method='GET') 6 | def list_invoices(self, **kwargs) -> ApiResponse: 7 | r""" 8 | 9 | list_invoices(**kwargs) -> ApiResponse 10 | 11 | Get invoices for advertiser. Requires one of these permissions: ["nemo_transactions_view","nemo_transactions_edit"] 12 | 13 | query **invoiceStatuses**:*string* | Optional. Available values : ISSUED, PAID_IN_PART, PAID_IN_FULL, WRITTEN_OFF. (Not documented: ACCUMULATING) 14 | 15 | query **count**:*string* | Optional. Number of records to include in the paged response. Defaults to 100. Cannot be combined with the cursor parameter. 16 | 17 | query **cursor**:*string* | Optional. A cursor representing how far into a result set this query should begin. In the absence of a cursor the request will default to start index of 0 and page size of 100. 18 | 19 | 20 | Returns: 21 | 22 | ApiResponse 23 | 24 | """ 25 | return self._request(kwargs.pop('path'), params=kwargs) 26 | 27 | @sp_endpoint('/invoices/{}', method='GET') 28 | def get_invoice(self, invoiceId, **kwargs) -> ApiResponse: 29 | r""" 30 | 31 | get_invoice(invoiceId: str) -> ApiResponse 32 | 33 | Get invoice data by invoice ID. Requires one of these permissions: ["nemo_transactions_view","nemo_transactions_edit"] 34 | 35 | path **invoiceId**:*string* | required. ID of invoice to fetch 36 | 37 | Returns: 38 | 39 | ApiResponse 40 | 41 | """ 42 | return self._request(fill_query_params(kwargs.pop('path'), invoiceId), params=kwargs) 43 | -------------------------------------------------------------------------------- /test/localizations/result_keywords_locales.json: -------------------------------------------------------------------------------- 1 | { 2 | "localizedKeywordResponses": [ 3 | { 4 | "sourceKeyword": {"keyword": "anti-block system"}, 5 | "localizedKeywords": { 6 | "DE": {"keyword": "Antiblockiersystem"}, 7 | "SE": {"keyword": "antiblocksystem"}, 8 | "IT": {"keyword": "sistema anti-blocco"}, 9 | "FR": {"keyword": "système anti-blocage"}, 10 | "ES": {"keyword": "sistema antibloqueo"}, 11 | "NL": {"keyword": "anti-blokkeersysteem"} 12 | }, 13 | "localizedKeywordResults": { 14 | "DE": { 15 | "localizedKeyword": {"keyword": "Antiblockiersystem"}, 16 | "status": "SUCCESS" 17 | }, 18 | "SE": { 19 | "localizedKeyword": {"keyword": "antiblocksystem"}, 20 | "status": "SUCCESS" 21 | }, 22 | "IT": { 23 | "localizedKeyword": {"keyword": "sistema anti-blocco"}, 24 | "status": "SUCCESS" 25 | }, 26 | "FR": { 27 | "localizedKeyword": {"keyword": "système anti-blocage"}, 28 | "status": "SUCCESS" 29 | }, 30 | "ES": { 31 | "localizedKeyword": {"keyword": "sistema antibloqueo"}, 32 | "status": "SUCCESS" 33 | }, 34 | "NL": { 35 | "localizedKeyword": {"keyword": "anti-blokkeersysteem"}, 36 | "status": "SUCCESS" 37 | } 38 | }, 39 | "status": "SUCCESS" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /ad_api/api/sb/media.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class Media(Client): 5 | """ 6 | Use this interface to request and retrieve store information. This can be used for Sponsored Brands campaign creation, to pull the store URL information, and for asset registration for Stores. 7 | """ 8 | 9 | @sp_endpoint('/media/upload', method='POST') 10 | def create_media(self, **kwargs) -> ApiResponse: 11 | """ 12 | Creates an ephemeral resource (upload location) to upload Media for an Ad Program (SponsoredBrands). 13 | 14 | The upload location is short lived and expires in 15 minutes. Once the upload is complete, /media/complete API should be used to notify that the upload is complete. 15 | 16 | The upload location only supports PUT HTTP Method to upload the media content. If the upload location expires, API user will get 403 Forbidden response. 17 | 18 | Request body 19 | | **programType** (string): [required] [ SponsoredBrands ]. 20 | | **creativeType** (string): [required] [ Video ]. 21 | 22 | Returns: 23 | | ApiResponse 24 | 25 | """ 26 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs) 27 | 28 | @sp_endpoint('/media/complete', method='PUT') 29 | def complete_media(self, **kwargs) -> ApiResponse: 30 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs) 31 | 32 | @sp_endpoint('/media/describe', method='GET') 33 | def describe_media(self, **kwargs) -> ApiResponse: 34 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs) 35 | -------------------------------------------------------------------------------- /test/campaigns/sp-sx-create-campaigns.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "portfolioId": 214026257044134, 4 | "name": "Campaign manual bid true", 5 | "tags": { 6 | "PONumber": "203382", 7 | "accountManager": "Manager001" 8 | }, 9 | "campaignType": "sponsoredProducts", 10 | "targetingType": "manual", 11 | "state": "enabled", 12 | "dailyBudget": 22.0, 13 | "startDate": "20220430", 14 | "endDate": "20420430", 15 | "premiumBidAdjustment": true 16 | }, 17 | { 18 | "portfolioId": 214026257044134, 19 | "name": "Campaign manual adjustments true", 20 | "tags": { 21 | "PONumber": "203382", 22 | "accountManager": "Manager001" 23 | }, 24 | "campaignType": "sponsoredProducts", 25 | "targetingType": "manual", 26 | "state": "enabled", 27 | "dailyBudget": 22.0, 28 | "startDate": "20220430", 29 | "endDate": "20420430", 30 | "bidding": { 31 | "strategy": "legacyForSales", 32 | "adjustments": 33 | [ 34 | { 35 | "predicate": "placementTop", 36 | "percentage": 15 37 | } 38 | ] 39 | } 40 | }, 41 | { 42 | "portfolioId": 214026257044134, 43 | "name": "Campaign auto test from dict as 3 option", 44 | "tags": { 45 | "PONumber": "203382", 46 | "accountManager": "Manager001" 47 | }, 48 | "campaignType": "sponsoredProducts", 49 | "targetingType": "auto", 50 | "state": "enabled", 51 | "dailyBudget": 12.5, 52 | "startDate": "20220430", 53 | "endDate": "20420430" 54 | } 55 | ] -------------------------------------------------------------------------------- /docs/sd/product_targeting.rst: -------------------------------------------------------------------------------- 1 | Product Targeting 2 | ================= 3 | 4 | .. warning:: 5 | 6 | Sponsored Display is not available for Sandbox endpoint 7 | 8 | .. autoclass:: ad_api.api.sd.Targets 9 | 10 | Endpoints available 11 | 12 | .. csv-table:: 13 | :widths: 25, 25, 50 14 | :header: "Method", "Endpoint", "Description" 15 | 16 | "GET", "/sd/targets", "Gets a list of targeting clauses." 17 | "PUT", "/sd/targets", "Updates one or more targeting clauses." 18 | "POST", "/sd/targets", "Creates one or more targeting clauses." 19 | "GET", "/sd/targets/{targetId}", "Gets a targeting clause specified by identifier." 20 | "DELETE", "/sd/targets/{targetId}", "Sets the `state` of a targeting clause to `archived`." 21 | "GET", "/sd/targets/extended", "Gets a list of targeting clause objects with extended fields." 22 | "GET", "/sd/targets/extended/{targetId}", "Gets extended information for a targeting clause." 23 | 24 | 25 | .. autofunction:: ad_api.api.sd.Targets.list_products_targets(self, **kwargs) -> ApiResponse: 26 | 27 | .. autofunction:: ad_api.api.sd.Targets.edit_products_targets(self, **kwargs) -> ApiResponse: 28 | 29 | .. autofunction:: ad_api.api.sd.Targets.create_products_targets(self, **kwargs) -> ApiResponse: 30 | 31 | .. autofunction:: ad_api.api.sd.Targets.get_products_target(self, targetId, **kwargs) -> ApiResponse: 32 | 33 | .. autofunction:: ad_api.api.sd.Targets.delete_products_target(self, targetId, **kwargs) -> ApiResponse: 34 | 35 | .. autofunction:: ad_api.api.sd.Targets.list_products_targets_extended(self, **kwargs) -> ApiResponse: 36 | 37 | .. autofunction:: ad_api.api.sd.Targets.get_products_target_extended(self, targetId, **kwargs) -> ApiResponse: 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /docs/config.rst: -------------------------------------------------------------------------------- 1 | Config File 2 | =========== 3 | 4 | An example config file is provided in this repository, it supports multiple accounts. 5 | The program looks for a file called `credentials.yml`_ 6 | 7 | The config is parsed by `confused`_, see their docs for more in depth information. 8 | Search paths are: 9 | 10 | .. code-block:: bash 11 | 12 | macOS: ~/.config/python-sp-api 13 | Other Unix: ~/.config/python-sp-api 14 | Windows: %APPDATA%\python-sp-api where the APPDATA environment variable falls back to %HOME%\AppData\Roaming if undefined 15 | 16 | If you're only using one account, place it under default. You can pass the account's name to the client to use any other account used in the `credentials.yml`_ file. 17 | 18 | .. code-block:: yaml 19 | 20 | version: '1.0' 21 | 22 | default: 23 | refresh_token: '' 24 | client_id: '' 25 | client_secret: '' 26 | profile_id: '' 27 | 28 | another_account: 29 | refresh_token: '' 30 | client_id: '' 31 | client_secret: '' 32 | profile_id: '' 33 | 34 | 35 | ************************** 36 | Usage with default account 37 | ************************** 38 | 39 | .. code-block:: python 40 | 41 | Campaigns().list_campaigns() 42 | 43 | 44 | ************************** 45 | Usage with another_account 46 | ************************** 47 | 48 | You can use every account's name from the config file for account 49 | 50 | .. code-block:: python 51 | 52 | Campaigns(account="another_account", marketplace=Marketplaces.ES).list_campaigns() 53 | 54 | 55 | ********** 56 | References 57 | ********** 58 | 59 | .. target-notes:: 60 | 61 | .. _`credentials.yml`: https://github.com/denisneuf/python-amazon-ad-api/#credentials 62 | .. _`confused`: https://confuse.readthedocs.io/en/latest/usage.html#search-paths 63 | 64 | 65 | -------------------------------------------------------------------------------- /ad_api/api/history.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class History(Client): 5 | """ """ 6 | 7 | @sp_endpoint('/history', method='POST') 8 | def get_history(self, **kwargs) -> ApiResponse: 9 | r""" 10 | 11 | get_history(body: (dict, str, file)) -> ApiResponse 12 | 13 | Returns history of changes for provided event sources that match the filters and time ranges specified. Only events that belong to the authenticated Advertiser can be queried. All times will be in UTC Epoch format. This API accepts identifiers in either the alphamumeric format (default), or the numeric format. If numeric IDs are supplied, then numeric IDs will be returned otherwise, alphanumeric IDs are returned. 14 | 15 | body: | REQUIRED {'description': 'A HistoryQuery}' 16 | 17 | | **from_date** | **int** | Max 90 days of history. 18 | | **to_date** | **int** | 19 | | **event_types** 20 | | HistoryEventType 21 | | **next_token** | **str** | token from previous response to get next set of data. | [optional] 22 | | **page_offset** | **int** | Mutually exclusive with 'nextToken'. Max results with pageOffset is 10000. Use nextToken instead for more results. | [optional] 23 | | **count** | **int** | Requested number of results. Default 100. Minimum 50. Maximum 200. | [optional] 24 | | **sort** 25 | | HistorySortParameter 26 | | **any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] 27 | 28 | """ 29 | body = Utils.convert_body(kwargs.pop('body'), wrap=False) 30 | return self._request(kwargs.pop('path'), data=body, params=kwargs) 31 | -------------------------------------------------------------------------------- /test/localizations/result_currency_extended_v1.json: -------------------------------------------------------------------------------- 1 | { 2 | "localizedCurrencyResponses": [ 3 | { 4 | "localizedCurrencies": { 5 | "A1F83G8C2ARO7P": { 6 | "amount": 8.29, 7 | "currency": "GBP", 8 | "country_code": "UK" 9 | }, 10 | "ATVPDKIKX0DER": { 11 | "amount": 10.83, 12 | "currency": "USD", 13 | "country_code": "US" 14 | }, 15 | "A1VC38T7YXB528": { 16 | "amount": 1363.0, 17 | "currency": "JPY", 18 | "country_code": "JP" 19 | }, 20 | }, 21 | "status": "SUCCESS", 22 | "sourceCurrency": { 23 | "amount": 10, 24 | "country_code": "DE", 25 | "currency": "EUR", 26 | "source_marketplace_id": "A1PA6795UKMFR9" 27 | }, 28 | }, 29 | { 30 | "localizedCurrencies": { 31 | "A1F83G8C2ARO7P": { 32 | "amount": 12.43, 33 | "currency": "GBP", 34 | "country_code": "UK" 35 | }, 36 | "ATVPDKIKX0DER": { 37 | "amount": 16.25, 38 | "currency": "USD", 39 | "country_code": "US" 40 | }, 41 | "A1VC38T7YXB528": { 42 | "amount": 2045.0, 43 | "currency": "JPY", 44 | "country_code": "JP" 45 | }, 46 | }, 47 | "status": "SUCCESS", 48 | "sourceCurrency": { 49 | "amount": 15, 50 | "country_code": "DE", 51 | "currency": "EUR", 52 | "source_marketplace_id": "A1PA6795UKMFR9" 53 | } 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /docs/api/attribution.rst: -------------------------------------------------------------------------------- 1 | Amazon Attribution API open beta 2 | ================================ 3 | 4 | `https://dtrnk0o2zy01c.cloudfront.net/openapi/en-us/dest/AmazonAttribution_prod_3p.json`_ 5 | 6 | .. _https://dtrnk0o2zy01c.cloudfront.net/openapi/en-us/dest/AmazonAttribution_prod_3p.json: https://dtrnk0o2zy01c.cloudfront.net/openapi/en-us/dest/AmazonAttribution_prod_3p.json 7 | 8 | **Amazon Attribution** 9 | 10 | Amazon Attribution is an advertising measurement product that enables advertisers to understand the impact that their non-Amazon ads (i.e. Google Ads, Facebook, Microsoft Ads) have in driving shopping activity on Amazon. Measuring ads using Amazon Attribution is done through implementing Attribution tags on non-Amazon ads. Amazon Attribution is currently available in beta for US, CA, UK, DE, FR, IT, and ES vendors and professional sellers enrolled in Brand Registry. 11 | 12 | **Amazon Attribution API** 13 | 14 | The Amazon Attribution API enables agencies and integrators to easily retrieve their advertiser client's non-Amazon publisher attribution tags to automate tag implementation on their non-Amazon ads that link to an Amazon product or Stores page. The API also enables agencies and integrators to create and retrieve reporting on behalf of their advertiser clients to better understand Amazon conversion performance on their campaigns. 15 | 16 | Note that you must pass a header named Amazon-Advertising-Api-Scope with each call to an Amazon Attribution API URI, including GET /advertisers. The value for this header is the profileId available from the Profiles resource (/v2/profiles). 17 | 18 | .. autoclass:: ad_api.api.Attribution 19 | 20 | .. autofunction:: ad_api.api.Attribution.get_advertisers 21 | 22 | .. autofunction:: ad_api.api.Attribution.get_publishers 23 | 24 | .. autofunction:: ad_api.api.Attribution.post_report 25 | 26 | .. autofunction:: ad_api.api.Attribution.get_macro_tag 27 | 28 | .. autofunction:: ad_api.api.Attribution.get_non_macro_template_tag -------------------------------------------------------------------------------- /docs/sd/negative_product_targeting.rst: -------------------------------------------------------------------------------- 1 | Negative targeting 2 | ================== 3 | 4 | .. warning:: 5 | 6 | Sponsored Display is not available for Sandbox endpoint 7 | 8 | .. autoclass:: ad_api.api.sd.NegativeTargets 9 | 10 | Endpoints available 11 | 12 | .. csv-table:: 13 | :widths: 25, 25, 50 14 | :header: "Method", "Endpoint", "Description" 15 | 16 | "GET", "/sd/negativeTargets", "Gets a list of negative targeting clauses." 17 | "PUT", "/sd/negativeTargets", "Updates one or more negative targeting clauses." 18 | "POST", "/sd/negativeTargets", "Creates one or more negative targeting clauses." 19 | "GET", "/sd/negativeTargets/{negativeTargetId}", "Gets a negative targeting clause specified by identifier." 20 | "DELETE", "/sd/negativeTargets/{negativeTargetId}", "Sets the `state` of a negative targeting clause to `archived`." 21 | "GET", "/sd/negativeTargets/extended", "Gets a list of negative targeting clause objects with extended fields." 22 | "GET", "/sd/negativeTargets/extended/{negativeTargetId}", "Gets extended information for a negative targeting clause." 23 | 24 | 25 | .. autofunction:: ad_api.api.sd.NegativeTargets.list_negative_targets(self, **kwargs) -> ApiResponse: 26 | 27 | .. autofunction:: ad_api.api.sd.NegativeTargets.edit_negative_targets(self, **kwargs) -> ApiResponse: 28 | 29 | .. autofunction:: ad_api.api.sd.NegativeTargets.create_negative_targets(self, **kwargs) -> ApiResponse: 30 | 31 | .. autofunction:: ad_api.api.sd.NegativeTargets.get_negative_target(self, targetId, **kwargs) -> ApiResponse: 32 | 33 | .. autofunction:: ad_api.api.sd.NegativeTargets.delete_negative_targets(self, targetId, **kwargs) -> ApiResponse: 34 | 35 | .. autofunction:: ad_api.api.sd.NegativeTargets.list_negative_targets_extended(self, **kwargs) -> ApiResponse: 36 | 37 | .. autofunction:: ad_api.api.sd.NegativeTargets.get_negative_target_extended(self, targetId, **kwargs) -> ApiResponse: 38 | 39 | -------------------------------------------------------------------------------- /test/audiences/result_query_general.json: -------------------------------------------------------------------------------- 1 | { 2 | "audiences": [ 3 | { 4 | "audienceId": "381539216349606208", 5 | "audienceName": "LS - Vehicle Owners", 6 | "description": "People who own vehicles", 7 | "category": "Lifestyle", 8 | "createDate": "2019-06-27T13:19:12.656Z", 9 | "updateDate": "2021-03-10T06:52:21Z", 10 | "status": "Active", 11 | "forecasts": { 12 | "inventoryForecasts": { 13 | "all": { 14 | "dailyReach": { 15 | "lowerBoundInclusive": 2000000, 16 | "upperBoundExclusive": 2500000, 17 | } 18 | } 19 | } 20 | }, 21 | }, 22 | { 23 | "audienceId": "423091775444044780", 24 | "audienceName": "IM - Coche y moto", 25 | "description": "Personas cuyas actividades de compras\xa0 indican que estan inclinadas a comprar productos de Coche y moto", 26 | "category": "In-market", 27 | "createDate": "2013-04-19T00:00:00Z", 28 | "updateDate": "2017-09-19T23:36:00.964Z", 29 | "status": "Active", 30 | "forecasts": { 31 | "inventoryForecasts": { 32 | "all": { 33 | "dailyReach": { 34 | "lowerBoundInclusive": 1500000, 35 | "upperBoundExclusive": 2000000 36 | } 37 | } 38 | } 39 | }, 40 | }, 41 | ], 42 | "matchCount": 44, 43 | "nextToken": "RlYDS3X-i8zaLNutSzVIJIQ7RJZ2WYnHyi17DVEHBlZ-skWA-ubGpcNeuoUKas4uKXRen2DYddavk_QtvVRgl8swN6v5KSo86n6tuiy95u4R5OJcmKZ5_7ExV19KnFZIf-aEOnSslt9Kp84HJQjfFffTJRTzf3nIAa61kFPdVuGeW-fEuZvAgdZgowSuW5NhA76bR5wPL1jPcxATKqr4b2dZFYb36r9AYVzpRgEOcI2K6PxECaq4sVDjIztIgHf1BxyLO0ppZcuGFgpnzAKf-l_Bv5zNcWHLqo--" 44 | } 45 | 46 | -------------------------------------------------------------------------------- /ad_api/api/sb/bid_recommendations.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class BidRecommendations(Client): 5 | """ 6 | Use the Amazon Advertising API for Sponsored Brands for campaign, ad group, keyword, negative keyword, drafts, Stores, landing pages, and Brands management operations. For more information about Sponsored Brands, see the Sponsored Brands Support Center. For onboarding information, see the account setup topic. 7 | """ 8 | 9 | @sp_endpoint('/sb/recommendations/bids', method='POST') 10 | def get_bid_recommendations(self, **kwargs) -> ApiResponse: 11 | r""" 12 | Get a list of bid recommendation objects for a specified list of keywords or products. 13 | 14 | Request Body 15 | | '**campaignId**': *integer($int64)*, {'description': 'The identifier of the campaign for which bid recommendations are created.'} 16 | | '**targets**': *integer($int64)*, {'SBExpression': 'A name value pair that defines a targeting expression. The type field defines the predicate. The value field defines the value to match for the predicate.'} 17 | 18 | | '**type**': *string*, {'values': '[ asinCategorySameAs, asinBrandSameAs, asinPriceLessThan, asinPriceBetween, asinPriceGreaterThan, asinReviewRatingLessThan, asinReviewRatingBetween, asinReviewRatingGreaterThan, asinSameAs ]'} 19 | 20 | | '**value**': *string*, {'description': 'The text of the targeting expression. The - token defines a range. For example, 2-4 defines a range of 2, 3, and 4.'} 21 | 22 | | '**keywords**': *string*, {'description': 'SBBidRecommendationKeyword'} 23 | 24 | | '**matchType**': *string*, {'values': '[ broad, exact, phrase ]'} 25 | 26 | | '**keywordText**': *string*, {'description': 'The text of the keyword. Maximum of 10 words.'} 27 | 28 | | '**adFormat**': *integer($int64)*, {'values': '[ productCollection, video ]'} 29 | 30 | Returns: 31 | ApiResponse 32 | 33 | """ 34 | 35 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs) 36 | -------------------------------------------------------------------------------- /docs/sb/campaigns.rst: -------------------------------------------------------------------------------- 1 | Campaigns 2 | ========= 3 | 4 | .. autoclass:: ad_api.api.sb.Campaigns 5 | 6 | .. deprecated:: 4.0.2 7 | 8 | .. autofunction:: ad_api.api.sb.Campaigns.list_campaigns(self, **kwargs) -> ApiResponse: 9 | 10 | ### Example python 11 | 12 | .. code-block:: python 13 | 14 | from ad_api.api.sb.campaigns import Campaigns 15 | 16 | res = Campaigns().list_campaigns() 17 | 18 | print(result) 19 | 20 | 21 | 22 | 23 | .. deprecated:: 4.0.2 24 | 25 | .. autofunction:: ad_api.api.sb.Campaigns.create_campaigns(self, **kwargs) -> ApiResponse: 26 | 27 | ### Example python 28 | 29 | .. code-block:: python 30 | 31 | from ad_api.api.sb.campaigns import Campaigns 32 | 33 | file = open("SBCreateCampaignWithKeywords.json") 34 | data = file.read() 35 | file.close() 36 | 37 | result = Campaigns().create_campaigns( 38 | body=data 39 | ) 40 | print(result) 41 | 42 | ### Example json 43 | 44 | .. literalinclude:: ../../test/campaigns/sb-sx-create-campaign-keywords.json 45 | 46 | .. deprecated:: 4.0.2 47 | 48 | .. autofunction:: ad_api.api.sb.Campaigns.edit_campaigns(self, **kwargs) -> ApiResponse: 49 | 50 | ### Example json 51 | 52 | .. literalinclude:: ../../test/campaigns/sb-sx-edit-campaign.json 53 | 54 | .. autofunction:: ad_api.api.sb.Campaigns.get_campaign(self, campaignId, **kwargs) -> ApiResponse: 55 | 56 | ### Example python 57 | 58 | .. code-block:: python 59 | 60 | from ad_api.api.sb.campaigns import Campaigns 61 | 62 | campaign_id = 144329325594765093 63 | 64 | result = Campaigns().get_campaign( 65 | campaignId=campaign_id 66 | ) 67 | print(result) 68 | 69 | 70 | .. deprecated:: 4.0.2 71 | 72 | .. autofunction:: ad_api.api.sb.Campaigns.delete_campaign(self, campaignId, **kwargs) -> ApiResponse: 73 | 74 | .. code-block:: python 75 | 76 | from ad_api.api.sb.campaigns import Campaigns 77 | 78 | campaign_id = 144329325594765093 79 | 80 | result = Campaigns().delete_campaign( 81 | campaignId=campaign_id 82 | ) 83 | print(result) 84 | 85 | 86 | NOT AVAILABLE SANDBOX [INTERNAL_ERROR - HTTP 500 Internal Server Error] 87 | -------------------------------------------------------------------------------- /docs/sp/product_targeting_v3.rst: -------------------------------------------------------------------------------- 1 | Product Targeting 2 | ================= 3 | 4 | .. warning:: 5 | 6 | Sponsored Product v3.0 is not available for Sandbox endpoint 7 | 8 | .. note:: 9 | 10 | This documentation API 3.0 11 | 12 | Endpoints available version 3.0 13 | 14 | .. csv-table:: 15 | :widths: 10, 35, 50 16 | :header: "Method", "Endpoint", "Description" 17 | 18 | 19 | "POST", "/sp/targets/list", "Listing product targets." 20 | "POST", "/sp/targets", "Creating product targets." 21 | "PUT", "/sp/targets", "Creating product targets." 22 | "POST", "/sp/targets/delete", "Deleting product targets." 23 | "POST", "/sp/targets/categories/recommendations", "Returns a list of category recommendations for the input list of ASINs." 24 | "POST", "/sp/targets/products/count", "Get number of targetable asins based on refinements provided by the user." 25 | "GET", "/sp/targets/categories", "Returns all targetable categories." 26 | "GET", "/sp​/targets​/category​/{categoryId}​/refinements", "Returns refinements according to category input." 27 | 28 | 29 | 30 | 31 | 32 | 33 | .. autoclass:: ad_api.api.sp.TargetsV3 34 | 35 | .. autofunction:: ad_api.api.sp.TargetsV3.list_product_targets(self, version: int = 3, **kwargs) -> ApiResponse: 36 | .. autofunction:: ad_api.api.sp.TargetsV3.create_product_targets(self, version: int = 3, prefer: bool = False, **kwargs) -> ApiResponse: 37 | .. autofunction:: ad_api.api.sp.TargetsV3.edit_product_targets(self, version: int = 3, prefer: bool = False, **kwargs) -> ApiResponse: 38 | .. autofunction:: ad_api.api.sp.TargetsV3.delete_product_targets(self, version: int = 3, **kwargs) -> ApiResponse: 39 | 40 | .. autofunction:: ad_api.api.sp.TargetsV3.list_products_targets_categories_recommendations(self, version: int = 3, prefer: bool = False, **kwargs) -> ApiResponse: 41 | .. autofunction:: ad_api.api.sp.TargetsV3.get_products_targets_count(self, version: int = 3, prefer: bool = False, **kwargs) -> ApiResponse: 42 | .. autofunction:: ad_api.api.sp.TargetsV3.list_targets_categories(self, prefer: bool = False, **kwargs) -> ApiResponse: 43 | .. autofunction:: ad_api.api.sp.TargetsV3.list_products_targets_category_refinements(self, categoryId, prefer: bool = False, **kwargs) -> ApiResponse: -------------------------------------------------------------------------------- /docs/api/history.rst: -------------------------------------------------------------------------------- 1 | Change History open beta 2 | ======================== 3 | `https://dtrnk0o2zy01c.cloudfront.net/openapi/en-us/dest/Eligibility_prod_3p.json`_ 4 | 5 | .. _https://dtrnk0o2zy01c.cloudfront.net/openapi/en-us/dest/Eligibility_prod_3p.json: https://dtrnk0o2zy01c.cloudfront.net/openapi/en-us/dest/Eligibility_prod_3p.json 6 | 7 | Provides information about changes made to campaigns, adgroups, ads, etc 8 | 9 | .. autoclass:: ad_api.api.History 10 | 11 | .. autofunction:: ad_api.api.History.get_history 12 | 13 | ### Example python 14 | 15 | .. code-block:: python 16 | 17 | import logging 18 | from ad_api.api import History 19 | from ad_api.base import AdvertisingApiException 20 | 21 | def get_history(data: (str, dict)): 22 | 23 | try: 24 | 25 | result = History(debug=True).get_history( 26 | body=json.dumps(data) 27 | ) 28 | payload = result.payload 29 | events = payload.get("events") 30 | for event in events: 31 | logging.info(event) 32 | 33 | except AdvertisingApiException as error: 34 | logging.info(error) 35 | 36 | if __name__ == '__main__': 37 | 38 | 39 | # fromDate cannot be more than 90 days ago 40 | from_date = datetime(2022, 2, 1, 0, 0, 0).strftime('%s%f')[:-3] 41 | to_date = datetime.now().strftime('%s%f')[:-3] 42 | 43 | request = \ 44 | { 45 | "fromDate": int(from_date), 46 | "toDate": int(to_date), 47 | "eventTypes": { 48 | "CAMPAIGN": { 49 | "filters": [ 50 | "BUDGET_AMOUNT", 51 | "STATUS" 52 | ], 53 | "eventTypeIds": [ 54 | "45662011530311" 55 | ] 56 | } 57 | } 58 | } 59 | 60 | get_history(request) 61 | 62 | ### Example query.json 63 | 64 | Download :download:`json <../../test/history/query.json>` the file to use: 65 | 66 | .. literalinclude:: ../../test/history/query.json -------------------------------------------------------------------------------- /docs/sd/budget_rules.rst: -------------------------------------------------------------------------------- 1 | Budget Rules 2 | ============ 3 | 4 | .. autoclass:: ad_api.api.sd.BudgetRules 5 | 6 | Endpoints available 7 | 8 | .. csv-table:: 9 | :widths: 10, 35, 50 10 | :header: "Method", "Endpoint", "Description" 11 | 12 | "GET", "/sd/campaigns/{campaignId}/budgetRules/budgetHistory", "Gets the budget history for a campaign specified by identifier." 13 | "POST", "/sd/budgetRules", "Creates one or more budget rules." 14 | "GET", "/sd/budgetRules", "Get all budget rules created by an advertiser" 15 | "PUT", "/sd/budgetRules", "Updates one or more budget rules." 16 | "GET", "/sd/budgetRules/{budgetRuleId}", "Gets a budget rule specified by identifier." 17 | "GET", "/sd/budgetRules/{budgetRuleId}/campaigns", "Gets all the campaigns associated with a budget rule" 18 | "POST", "/sd/campaigns/{campaignId}/budgetRules", "Associates one or more budget rules to a campaign specified by identifer." 19 | "GET", "/sd/campaigns/{campaignId}/budgetRules", "Gets a list of budget rules associated to a campaign specified by identifier." 20 | "DELETE", "/sd/campaigns/{campaignId}/budgetRules/{budgetRuleId}", "Disassociates a budget rule specified by identifier from a campaign specified by identifier." 21 | 22 | .. autofunction:: ad_api.api.sd.BudgetRules.get_budget_history(self, campaignId, **kwargs) -> ApiResponse: 23 | .. autofunction:: ad_api.api.sd.BudgetRules.create_budget_rules(self, **kwargs) -> ApiResponse: 24 | .. autofunction:: ad_api.api.sd.BudgetRules.list_budget_rules(self, **kwargs) -> ApiResponse: 25 | .. autofunction:: ad_api.api.sd.BudgetRules.edit_budget_rules(self, **kwargs) -> ApiResponse: 26 | .. autofunction:: ad_api.api.sd.BudgetRules.get_budget_rule(self, budgetRuleId, **kwargs) -> ApiResponse: 27 | .. autofunction:: ad_api.api.sd.BudgetRules.get_campaigns_budget_rule(self, budgetRuleId, **kwargs) -> ApiResponse: 28 | .. autofunction:: ad_api.api.sd.BudgetRules.create_campaign_budget_rules(self, campaignId, **kwargs) -> ApiResponse: 29 | .. autofunction:: ad_api.api.sd.BudgetRules.get_budget_rules_campaign(self, campaignId, **kwargs) -> ApiResponse: 30 | .. autofunction:: ad_api.api.sd.BudgetRules.delete_budget_rule_campaign(self, campaignId, budgetRuleId, **kwargs) -> ApiResponse: 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /test/localizations/result_currency_v2.json: -------------------------------------------------------------------------------- 1 | { 2 | "localizedCurrencyResponses": [ 3 | { 4 | "sourceCurrency": {"amount": 10.0}, 5 | "localizedCurrencies": { 6 | "A1F83G8C2ARO7P": {"amount": 8.29}, 7 | "ATVPDKIKX0DER": {"amount": 10.83}, 8 | "A1VC38T7YXB528": {"amount": 1363.0} 9 | }, 10 | "localizedCurrency": { 11 | "A1F83G8C2ARO7P": {"amount": 8.29}, 12 | "ATVPDKIKX0DER": {"amount": 10.83}, 13 | "A1VC38T7YXB528": {"amount": 1363.0} 14 | }, 15 | "localizedCurrencyResults": { 16 | "A1F83G8C2ARO7P": { 17 | "localizedCurrency": {"amount": 8.29}, 18 | "status": "SUCCESS" 19 | }, 20 | "ATVPDKIKX0DER": { 21 | "localizedCurrency": {"amount": 10.83}, 22 | "status": "SUCCESS" 23 | }, 24 | "A1VC38T7YXB528": { 25 | "localizedCurrency": {"amount": 1363.0}, 26 | "status": "SUCCESS" 27 | }, 28 | }, 29 | "status": "SUCCESS" 30 | }, 31 | { 32 | "sourceCurrency": {"amount": 15.0}, 33 | "localizedCurrencies": { 34 | "A1F83G8C2ARO7P": {"amount": 12.43}, 35 | "ATVPDKIKX0DER": {"amount": 16.25}, 36 | "A1VC38T7YXB528": {"amount": 2045.0} 37 | }, 38 | "localizedCurrency": { 39 | "A1F83G8C2ARO7P": {"amount": 12.43}, 40 | "ATVPDKIKX0DER": {"amount": 16.25}, 41 | "A1VC38T7YXB528": {"amount": 2045.0} 42 | }, 43 | "localizedCurrencyResults": { 44 | "A1F83G8C2ARO7P": { 45 | "localizedCurrency": {"amount": 12.43}, 46 | "status": "SUCCESS" 47 | }, 48 | "ATVPDKIKX0DER": { 49 | "localizedCurrency": {"amount": 16.25}, 50 | "status": "SUCCESS" 51 | }, 52 | "A1VC38T7YXB528": { 53 | "localizedCurrency": {"amount": 2045.0}, 54 | "status": "SUCCESS" 55 | } 56 | }, 57 | "status": "SUCCESS" 58 | } 59 | ] 60 | } 61 | 62 | -------------------------------------------------------------------------------- /docs/api/metadata.rst: -------------------------------------------------------------------------------- 1 | Product Selector 2 | ================= 3 | `https://dtrnk0o2zy01c.cloudfront.net/openapi/en-us/dest/ProductSelector_prod_3p.json`_ 4 | 5 | .. _https://dtrnk0o2zy01c.cloudfront.net/openapi/en-us/dest/ProductSelector_prod_3p.json: https://dtrnk0o2zy01c.cloudfront.net/openapi/en-us/dest/ProductSelector_prod_3p.json 6 | 7 | The Amazon Product Selector API allows integrators to receive product metadata such as inventory status, price, eligibility status and product details for SKUS or ASINs in their Product Catalog in order to launch, manage or optimize Sponsored Product, Sponsored Brands or Sponsored Display advertising campaigns. The Product Selector API is available to Sellers, Vendors, and Authors. 8 | 9 | .. autoclass:: ad_api.api.Metadata 10 | 11 | .. autofunction:: ad_api.api.Metadata.get_products_metadata 12 | 13 | ### Example getting the metadata of a search string 14 | 15 | .. code-block:: python 16 | 17 | import logging 18 | from ad_api.api import Metadata 19 | from ad_api.base import AdvertisingApiException 20 | 21 | 22 | logging.basicConfig( 23 | level=logging.INFO, 24 | format="%(asctime)s:%(levelname)s:%(message)s" 25 | ) 26 | 27 | 28 | def get_products_metadata(data: (str, dict)): 29 | 30 | try: 31 | 32 | result = Metadata(debug=True).get_products_metadata( 33 | body=data 34 | ) 35 | 36 | logging.info(result) 37 | 38 | product_metadata_list = result.payload.get("ProductMetadataList") 39 | 40 | logging.info(len(product_metadata_list)) 41 | 42 | for product_metadata in product_metadata_list: 43 | logging.info(product_metadata) 44 | 45 | except AdvertisingApiException as error: 46 | logging.info(error) 47 | 48 | 49 | if __name__ == '__main__': 50 | 51 | search_dict = \ 52 | { 53 | 'checkItemDetails': True, 54 | 'adType': 'SP', 55 | 'checkEligibility': True, 56 | 'searchStr': 'obd2', 57 | 'pageIndex': 1, 58 | 'pageSize': 20, 59 | 'sortBy': 'CREATED_DATE', 60 | 'locale': 'es_ES' 61 | } 62 | 63 | get_products_metadata(search_dict) 64 | 65 | -------------------------------------------------------------------------------- /ad_api/api/sd/snapshots.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, fill_query_params, ApiResponse, Utils 2 | 3 | 4 | class Snapshots(Client): 5 | """ 6 | Use the Amazon Advertising API for Sponsored Products for campaign, ad group, keyword, negative keyword, and product ad management operations. For more information about Sponsored Products, see the Sponsored Products Support Center. For onboarding information, see the account setup topic. 7 | """ 8 | 9 | @sp_endpoint('/sd/{}/snapshot', method='POST') 10 | def post_snapshot(self, recordType, **kwargs) -> ApiResponse: 11 | """ 12 | 13 | Returns: 14 | ApiResponse 15 | """ 16 | return self._request(fill_query_params(kwargs.pop('path'), recordType), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs) 17 | 18 | @sp_endpoint('/sd/snapshots/{}', method='GET') 19 | def get_snapshot(self, snapshotId, **kwargs) -> ApiResponse: 20 | r""" 21 | Gets the status of a requested snapshot. 22 | 23 | Returns: 24 | ApiResponse 25 | """ 26 | return self._request(fill_query_params(kwargs.pop('path'), snapshotId), params=kwargs) 27 | 28 | def download_snapshot(self, **kwargs) -> ApiResponse: 29 | r""" 30 | Downloads the snapshot previously get report specified by location (this is not part of the official Amazon Advertising API, is a helper method to download the snapshot). Take in mind that a direct download of location returned in get_snapshot will return 401 - Unauthorized. 31 | 32 | kwarg parameter **file** if not provided will take the default amazon name from path download (add a path with slash / if you want a specific folder, do not add extension as the return will provide the right extension based on format choosed if needed) 33 | 34 | kwarg parameter **format** if not provided a format will return a url to download the snapshot (this url has a expiration time) 35 | 36 | Keyword Args 37 | | **url** (string): The location obatined from get_snapshot [required] 38 | | **file** (string): The path to save the file if mode is download json, zip or gzip. [optional] 39 | | **format** (string): The mode to download the snapshot: data (list), raw, url, json, zip, gzip. Default (url) [optional] 40 | 41 | Returns: 42 | ApiResponse 43 | """ 44 | return self._download(self, params=kwargs) 45 | -------------------------------------------------------------------------------- /docs/sp/budget_rules.rst: -------------------------------------------------------------------------------- 1 | Budget Rules 2 | ============ 3 | 4 | .. warning:: 5 | 6 | Sponsored Product v3 is not available for Sandbox endpoint 7 | 8 | .. note:: 9 | 10 | This API is version 3.0 11 | 12 | .. autoclass:: ad_api.api.sp.BudgetRules 13 | 14 | Endpoints available 15 | 16 | .. csv-table:: 17 | :widths: 10, 35, 50 18 | :header: "Method", "Endpoint", "Description" 19 | 20 | "GET", "/sp/campaigns/{campaignId}/budgetRules/budgetHistory", "Gets the budget history for a campaign specified by identifier." 21 | "POST", "/sp/budgetRules", "Creates one or more budget rules." 22 | "GET", "/sp/budgetRules", "Get all budget rules created by an advertiser" 23 | "PUT", "/sp/budgetRules", "Updates one or more budget rules." 24 | "GET", "/sp/budgetRules/{budgetRuleId}", "Gets a budget rule specified by identifier." 25 | "GET", "/sp/budgetRules/{budgetRuleId}/campaigns", "Gets all the campaigns associated with a budget rule" 26 | "POST", "/sp/campaigns/{campaignId}/budgetRules", "Associates one or more budget rules to a campaign specified by identifer." 27 | "GET", "/sp/campaigns/{campaignId}/budgetRules", "Gets a list of budget rules associated to a campaign specified by identifier." 28 | "DELETE", "/sp/campaigns/{campaignId}/budgetRules/{budgetRuleId}", "Disassociates a budget rule specified by identifier from a campaign specified by identifier." 29 | 30 | .. autofunction:: ad_api.api.sp.BudgetRules.get_budget_history(self, campaignId, **kwargs) -> ApiResponse: 31 | .. autofunction:: ad_api.api.sp.BudgetRules.create_budget_rules(self, **kwargs) -> ApiResponse: 32 | .. autofunction:: ad_api.api.sp.BudgetRules.list_budget_rules(self, **kwargs) -> ApiResponse: 33 | .. autofunction:: ad_api.api.sp.BudgetRules.edit_budget_rules(self, **kwargs) -> ApiResponse: 34 | .. autofunction:: ad_api.api.sp.BudgetRules.get_budget_rule(self, budgetRuleId, **kwargs) -> ApiResponse: 35 | .. autofunction:: ad_api.api.sp.BudgetRules.get_campaigns_budget_rule(self, budgetRuleId, **kwargs) -> ApiResponse: 36 | .. autofunction:: ad_api.api.sp.BudgetRules.create_campaign_budget_rules(self, campaignId, **kwargs) -> ApiResponse: 37 | .. autofunction:: ad_api.api.sp.BudgetRules.get_budget_rules_campaign(self, campaignId, **kwargs) -> ApiResponse: 38 | .. autofunction:: ad_api.api.sp.BudgetRules.delete_budget_rule_campaign(self, campaignId, budgetRuleId, **kwargs) -> ApiResponse: 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /docs/sb/keywords.rst: -------------------------------------------------------------------------------- 1 | Keywords 2 | ======== 3 | 4 | .. autoclass:: ad_api.api.sb.Keywords 5 | 6 | .. autofunction:: ad_api.api.sb.Keywords.list_keywords(self, **kwargs) -> ApiResponse: 7 | 8 | ### Example python 9 | 10 | .. code-block:: python 11 | 12 | from ad_api.api.sb.keywords import Keywords 13 | 14 | result = Keywords().list_keywords() 15 | print(result) 16 | 17 | .. autofunction:: ad_api.api.sb.Keywords.edit_keywords(self, **kwargs) -> ApiResponse: 18 | 19 | ### Example python 20 | 21 | .. code-block:: python 22 | 23 | from ad_api.api.sb.keywords import Keywords 24 | 25 | file = open("edit.json") 26 | data = file.read() 27 | file.close() 28 | result = Keywords().edit_keywords( 29 | body=data 30 | ) 31 | print(result) 32 | 33 | 34 | ### Example json 35 | 36 | .. literalinclude:: ../../test/keywords/sb-sx-edit-keywords.json 37 | 38 | .. autofunction:: ad_api.api.sb.Keywords.create_keywords(self, **kwargs) -> ApiResponse: 39 | 40 | ### Example python 41 | 42 | .. code-block:: python 43 | 44 | from ad_api.api.sb.keywords import Keywords 45 | 46 | file = open("create.json") 47 | data = file.read() 48 | file.close() 49 | result = Keywords().create_keywords( 50 | body=data 51 | ) 52 | logging.info(result) 53 | 54 | ### Example json 55 | 56 | .. literalinclude:: ../../test/keywords/sb-sx-create-keywords.json 57 | 58 | .. autofunction:: ad_api.api.sb.Keywords.get_keyword(self, keywordId, **kwargs) -> ApiResponse: 59 | 60 | ### Example python 61 | 62 | .. code-block:: python 63 | 64 | from ad_api.api.sb.keywords import Keywords 65 | 66 | keyword_id = 144148613757234151 67 | result = Keywords().get_keyword( 68 | keywordId=keyword_id 69 | ) 70 | print(result) 71 | 72 | .. autofunction:: ad_api.api.sb.Keywords.delete_keyword(self, keywordId, **kwargs) -> ApiResponse: 73 | 74 | ### Example python 75 | 76 | .. code-block:: python 77 | 78 | from ad_api.api.sb.keywords import Keywords 79 | 80 | keyword_id = 144148613757234151 81 | result = Keywords().delete_keyword( 82 | keywordId=keyword_id 83 | ) 84 | print(result) 85 | -------------------------------------------------------------------------------- /ad_api/api/sb/snapshots.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, fill_query_params, ApiResponse, Utils 2 | 3 | 4 | class Snapshots(Client): 5 | """ 6 | Use the Amazon Advertising API for Sponsored Products for campaign, ad group, keyword, negative keyword, and product ad management operations. For more information about Sponsored Products, see the Sponsored Products Support Center. For onboarding information, see the account setup topic. 7 | """ 8 | 9 | @sp_endpoint('/v2/hsa/{}/snapshot', method='POST') 10 | @Utils.deprecated 11 | def post_snapshot(self, recordType, **kwargs) -> ApiResponse: 12 | """ 13 | 14 | Returns: 15 | ApiResponse 16 | """ 17 | return self._request(fill_query_params(kwargs.pop('path'), recordType), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs) 18 | 19 | @sp_endpoint('/v2/hsa/snapshots/{}', method='GET') 20 | @Utils.deprecated 21 | def get_snapshot(self, snapshotId, **kwargs) -> ApiResponse: 22 | r""" 23 | Gets the status of a requested snapshot. 24 | 25 | Returns: 26 | ApiResponse 27 | """ 28 | return self._request(fill_query_params(kwargs.pop('path'), snapshotId), params=kwargs) 29 | 30 | @Utils.deprecated 31 | def download_snapshot(self, **kwargs) -> ApiResponse: 32 | r""" 33 | Downloads the snapshot previously get report specified by location (this is not part of the official Amazon Advertising API, is a helper method to download the snapshot). Take in mind that a direct download of location returned in get_snapshot will return 401 - Unauthorized. 34 | 35 | kwarg parameter **file** if not provided will take the default amazon name from path download (add a path with slash / if you want a specific folder, do not add extension as the return will provide the right extension based on format choosed if needed) 36 | 37 | kwarg parameter **format** if not provided a format will return a url to download the snapshot (this url has a expiration time) 38 | 39 | Keyword Args 40 | | **url** (string): The location obatined from get_snapshot [required] 41 | | **file** (string): The path to save the file if mode is download json, zip or gzip. [optional] 42 | | **format** (string): The mode to download the snapshot: data (list), raw, url, json, zip, gzip. Default (url) [optional] 43 | 44 | Returns: 45 | ApiResponse 46 | """ 47 | return self._download(self, params=kwargs) 48 | -------------------------------------------------------------------------------- /ad_api/api/stores.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, fill_query_params, ApiResponse, Utils 2 | 3 | 4 | class Stores(Client): 5 | """ 6 | Brand Metrics provides a new measurement solution that quantifies opportunities for your brand at each stage of the customer journey on Amazon, and helps brands understand the value of different shopping engagements that impact stages of that journey. You can now access Awareness and Consideration indices that compare your performance to peers using models predictive of consideration and sales. Brand Metrics quantifies the number of customers in the awareness and consideration marketing funnel stages and is built at scale to measure all shopping engagements with your brand on Amazon, not just ad-attributed engagements. Additionally, BM breaks out key shopping engagements at each stage of the shopping journey, along with the Return on Engagement, so you can measure the historical sales following a consideration event or purchase. 7 | """ 8 | 9 | @sp_endpoint('/stores/{}/asinMetrics', method='POST') 10 | def get_asin_engagement_for_store(self, brandEntityId, version: int = 1, **kwargs) -> ApiResponse: 11 | r""" 12 | getAsinEngagementForStore 13 | """ 14 | content_type = 'application/vnd.GetAsinEngagementForStoreRequest.v'+ str(version) +'+json' 15 | accept = 'application/vnd.GetAsinEngagementForStoreResponse.v'+ str(version) +'+json' 16 | headers = {'Content-Type': content_type, 'Accept': accept} 17 | return self._request(fill_query_params(kwargs.pop('path'), brandEntityId), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 18 | 19 | @sp_endpoint('/stores/{}/insights', method='GET') 20 | def get_insights_for_store_api(self, brandEntityId, version: int = 1, **kwargs) -> ApiResponse: 21 | r""" 22 | getInsightsForStoreAPI 23 | """ 24 | content_type = 'application/vnd.GetInsightsForStoreRequest.v'+ str(version) +'+json' 25 | # accept = 'application/vnd.GetInsightsForStoreResponse.v'+ str(version) +'+json' 26 | # headers = {'Content-Type': content_type, 'Accept': accept} 27 | return self._request(fill_query_params(kwargs.pop('path'), brandEntityId), 28 | data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs) 29 | # return self._request(fill_query_params(kwargs.pop('path'), brandEntityId), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # mac os 88 | 89 | .DS_Store 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 99 | __pypackages__/ 100 | 101 | # Celery stuff 102 | celerybeat-schedule 103 | celerybeat.pid 104 | 105 | # SageMath parsed files 106 | *.sage.py 107 | 108 | # Environments 109 | .env 110 | .venv 111 | env/ 112 | venv/ 113 | ENV/ 114 | env.bak/ 115 | venv.bak/ 116 | 117 | # Spyder project settings 118 | .spyderproject 119 | .spyproject 120 | 121 | # Rope project settings 122 | .ropeproject 123 | 124 | # mkdocs documentation 125 | /site 126 | 127 | # mypy 128 | .mypy_cache/ 129 | .dmypy.json 130 | dmypy.json 131 | 132 | # Pyre type checker 133 | .pyre/ 134 | -------------------------------------------------------------------------------- /ad_api/api/metadata.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class Metadata(Client): 5 | r""" """ 6 | 7 | @sp_endpoint('/product/metadata', method='POST') 8 | def get_products_metadata(self, **kwargs) -> ApiResponse: 9 | r""" 10 | 11 | get_products_metadata(body: (dict, str)) -> ApiResponse 12 | 13 | Returns product metadata for the advertiser. 14 | 15 | body: | REQUIRED 16 | 17 | '**asins**': *list>string*, {'description': 'Specific asins to search for in the advertiser's inventory. Cannot use together with skus or searchStr input types.'} 18 | 19 | '**checkItemDetails**': *boolean*, {'description': 'Whether item details such as name, image, and price is required. default: false'} 20 | 21 | '**cursorToken**': *string*, {'description': 'Pagination token used for the suggested sort type'} 22 | 23 | '**adType**': *string*, {'description': 'Program type. Required if checks advertising eligibility. Enum: [ SP, SB, SD ]'} 24 | 25 | '**skus**': *list>string*, {'description': 'Specific skus to search for in the advertiser's inventory. Currently only support SP program type for sellers. Cannot use together with asins or searchStr input types'} 26 | 27 | '**checkEligibility**': *boolean*, {'description': 'Whether advertising eligibility info is required. default: false'} 28 | 29 | '**searchStr**': *string*, {'description': 'Specific string in the item title to search for in the advertiser's inventory. Case insensitive. Cannot use together with asins or skus input types'} 30 | 31 | '**pageIndex**': *integer($int32)*, {'description*': 'Index of the page to be returned'} 32 | 33 | '**sortOrder**': *string*, {'description': 'Sort order (has to be DESC for the suggested sort type). default: DESC. Enum [ ASC, DESC ]'} 34 | 35 | '**pageSize**': *integer($int32)*, {'description*': 'Number of items to be returned on this page index (max 100 for author)'} 36 | 37 | '**sortBy**': *string*, {'description': 'Sort option for the result. Currently only support SP program type for sellers. Enum [ SUGGESTED, CREATED_DATE ]'} 38 | 39 | 40 | 41 | Returns: 42 | 43 | ApiResponse 44 | 45 | """ 46 | contentType = 'application/vnd.productmetadatarequest.v1+json' 47 | headers = {'Content-Type': contentType} 48 | body = Utils.convert_body(kwargs.pop('body'), wrap=False) 49 | return self._request(kwargs.pop('path'), data=body, params=kwargs, headers=headers) 50 | -------------------------------------------------------------------------------- /ad_api/base/helpers.py: -------------------------------------------------------------------------------- 1 | from io import BytesIO 2 | from typing import TypeVar 3 | 4 | from Crypto.Util.Padding import pad 5 | import hashlib 6 | 7 | import base64 8 | from Crypto.Cipher import AES 9 | 10 | 11 | def fill_query_params(query, *args): 12 | return query.format(*args) 13 | 14 | 15 | T = TypeVar("T") 16 | 17 | 18 | def sp_endpoint(path, method="GET"): 19 | def decorator(function: T) -> T: 20 | def wrapper(*args, **kwargs): 21 | kwargs.update({"path": path, "method": method}) 22 | return function(*args, **kwargs) 23 | 24 | wrapper.__doc__ = function.__doc__ 25 | return wrapper 26 | 27 | return decorator 28 | 29 | 30 | def encrypt_aes(file_or_bytes_io, key, iv): 31 | key = base64.b64decode(key) 32 | iv = base64.b64decode(iv) 33 | aes = AES.new(key, AES.MODE_CBC, iv) 34 | try: 35 | if isinstance(file_or_bytes_io, BytesIO): 36 | return aes.encrypt(pad(file_or_bytes_io.read(), 16)) 37 | return aes.encrypt(pad(bytes(file_or_bytes_io.read(), encoding="iso-8859-1"), 16)) 38 | except UnicodeEncodeError: 39 | file_or_bytes_io.seek(0) 40 | return aes.encrypt(pad(bytes(file_or_bytes_io.read(), encoding="utf-8"), 16)) 41 | except TypeError: 42 | file_or_bytes_io.seek(0) 43 | return aes.encrypt(pad(file_or_bytes_io.read(), 16)) 44 | 45 | 46 | def decrypt_aes(content, key, iv): 47 | key = base64.b64decode(key) 48 | iv = base64.b64decode(iv) 49 | decrypter = AES.new(key, AES.MODE_CBC, iv) 50 | decrypted = decrypter.decrypt(content) 51 | padding_bytes = decrypted[-1] 52 | return decrypted[:-padding_bytes] 53 | 54 | 55 | def create_md5(file): 56 | hash_md5 = hashlib.md5() 57 | if isinstance(file, BytesIO): 58 | for chunk in iter(lambda: file.read(4096), b""): 59 | hash_md5.update(chunk) 60 | file.seek(0) 61 | return hash_md5.hexdigest() 62 | if isinstance(file, str): 63 | with open(file, "rb") as f: 64 | for chunk in iter(lambda: f.read(4096), b""): 65 | hash_md5.update(chunk) 66 | return hash_md5.hexdigest() 67 | for chunk in iter(lambda: file.read(4096), b""): 68 | hash_md5.update(chunk) 69 | return hash_md5.hexdigest() 70 | 71 | 72 | def nest_dict(flat: dict()): 73 | result = {} 74 | for k, v in flat.items(): 75 | _nest_dict_rec(k, v, result) 76 | return result 77 | 78 | 79 | def _nest_dict_rec(k, v, out): 80 | k, *rest = k.split(".", 1) 81 | if rest: 82 | _nest_dict_rec(rest[0], v, out.setdefault(k, {})) 83 | else: 84 | out[k] = v 85 | -------------------------------------------------------------------------------- /ad_api/api/sp/schedule_based_bid_optimization.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Any 3 | 4 | from ad_api.base import ApiResponse, Client, Utils, fill_query_params, sp_endpoint 5 | 6 | 7 | class ScheduleBasedBidOptimizationRules(Client): 8 | """ 9 | A class representing the Amazon Advertising API endpoint for Schedule Based Bid Optimization Rules. 10 | https://advertising.amazon.com/API/docs/en-us/sponsored-products/3-0/openapi/prod#tag/Optimization-Rules 11 | 12 | Methods 13 | ------- 14 | create_optimization_rules(**kwargs) 15 | Creates a new optimization rule. 16 | update_optimization_rules(**kwargs) 17 | Updates an existing optimization rule. 18 | associate_optimization_rules_with_campaign(campaignId, **kwargs) 19 | Associates an optimization rule with a campaign. 20 | searches_optimization_rules(**kwargs) 21 | Searches for optimization rules that match the specified criteria. 22 | """ 23 | 24 | HEADERS = { 25 | "Content-Type": "application/vnd.spoptimizationrules.v1+json", 26 | "Accept": "application/vnd.spoptimizationrules.v1+json", 27 | } 28 | 29 | @sp_endpoint('/sp/rules/optimization', method='POST') 30 | def create_optimization_rules(self, **kwargs) -> ApiResponse: 31 | return self._request( 32 | str(kwargs.pop('path')), 33 | data=Utils.convert_body(kwargs.pop('body'), False), 34 | params=kwargs, 35 | headers=self.HEADERS, 36 | ) 37 | 38 | @sp_endpoint('/sp/rules/optimization', method='PUT') 39 | def update_optimization_rules(self, **kwargs) -> ApiResponse: 40 | return self._request( 41 | str(kwargs.pop('path')), 42 | data=Utils.convert_body(kwargs.pop('body'), False), 43 | params=kwargs, 44 | headers=self.HEADERS, 45 | ) 46 | 47 | @sp_endpoint('/sp/campaigns/{}/optimizationRules', method='POST') 48 | def associate_optimization_rules_with_campaign(self, campaignId: str, **kwargs) -> ApiResponse: 49 | return self._request( 50 | fill_query_params(kwargs.pop('path'), campaignId), 51 | data=Utils.convert_body(kwargs.pop('body'), False), 52 | params=kwargs, 53 | headers=self.HEADERS, 54 | ) 55 | 56 | @sp_endpoint('/sp/rules/optimization/search', method='POST') 57 | def searches_optimization_rules(self, **kwargs) -> ApiResponse: 58 | return self._request( 59 | str(kwargs.pop('path')), 60 | data=Utils.convert_body(kwargs.pop('body'), False), 61 | params=kwargs, 62 | headers=self.HEADERS, 63 | ) 64 | -------------------------------------------------------------------------------- /ad_api/api/sp/__init__.py: -------------------------------------------------------------------------------- 1 | from .campaigns import Campaigns 2 | from .campaigns_v3 import CampaignsV3 3 | from .ad_groups import AdGroups 4 | from .ad_groups_v3 import AdGroupsV3 5 | from .keywords_v3 import KeywordsV3 6 | from .negative_keywords_v3 import NegativeKeywordsV3 7 | from .negative_product_targeting_v3 import NegativeTargetsV3 8 | from .product_ads import ProductAds 9 | from .product_ads_v3 import ProductAdsV3 10 | from .bid_recommendations import BidRecommendations 11 | from .bid_recommendations_v3 import BidRecommendationsV3 12 | from .keywords import Keywords 13 | from .negative_keywords import NegativeKeywords 14 | from .campaign_negative_keywords import CampaignNegativeKeywords 15 | from .campaign_negative_keywords_v3 import CampaignNegativeKeywordsV3 16 | from .campaign_negative_targets import CampaignNegativeTargets 17 | from .product_targeting_v3 import TargetsV3 18 | from .suggested_keywords import SuggestedKeywords 19 | from .product_targeting import Targets 20 | from .negative_product_targeting import NegativeTargets 21 | from .reports import Reports 22 | from .snapshots import Snapshots 23 | from .budget_rules import BudgetRules 24 | from .budget_initial_recommendation import InitialBudgetRecommendation 25 | from .campaings_optimization import CampaignOptimization 26 | from .campaign_consolidated_recommendations import CampaignsRecommendations 27 | from .ranked_keywords_recommendations import RankedKeywordsRecommendations 28 | from .budget_recommendations import BudgetRecommendations 29 | from .budget_rules_recommendations import BudgetRulesRecommendations 30 | from .product_recommendations import ProductRecommendations 31 | from .campaigns_budget_usage import CampaignsBudgetUsage 32 | from .schedule_based_bid_optimization import ScheduleBasedBidOptimizationRules 33 | 34 | __all__ = [ 35 | "Campaigns", 36 | "CampaignsV3", 37 | "AdGroups", 38 | "AdGroupsV3", 39 | "ProductAds", 40 | "ProductAdsV3", 41 | "BidRecommendations", 42 | "Keywords", 43 | "KeywordsV3", 44 | "NegativeKeywords", 45 | "NegativeKeywordsV3", 46 | "CampaignNegativeKeywords", 47 | "CampaignNegativeKeywordsV3", 48 | "CampaignNegativeTargets", 49 | "SuggestedKeywords", 50 | "Targets", 51 | "TargetsV3", 52 | "NegativeTargets", 53 | "NegativeTargetsV3", 54 | "Reports", 55 | "Snapshots", 56 | "BudgetRules", 57 | "InitialBudgetRecommendation", 58 | "CampaignOptimization", 59 | "CampaignsRecommendations", 60 | "RankedKeywordsRecommendations", 61 | "BudgetRecommendations", 62 | "BudgetRulesRecommendations", 63 | "ProductRecommendations", 64 | "CampaignsBudgetUsage", 65 | "ScheduleBasedBidOptimizationRules", 66 | "BidRecommendationsV3", 67 | ] 68 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '43 13 * * 0' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'python' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /ad_api/api/sb/stores.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, fill_query_params, ApiResponse, Utils 2 | 3 | 4 | class Stores(Client): 5 | """ 6 | Use the Amazon Advertising API for Sponsored Brands for campaign, ad group, keyword, negative keyword, drafts, Stores, landing pages, and Brands management operations. For more information about Sponsored Brands, see the Sponsored Brands Support Center. For onboarding information, see the account setup topic. 7 | """ 8 | 9 | @sp_endpoint('/stores/assets', method='GET') 10 | def list_assets(self, **kwargs) -> ApiResponse: 11 | """ 12 | For sellers or vendors, gets an array of assets associated with the specified brand entity identifier. Vendors are not required to specify a brand entity identifier, and in this case all assets associated with the vendor are returned. 13 | 14 | Keyword Args 15 | | path **brandEntityId** (string): For sellers, this field is required. It is the Brand entity identifier of the Brand for which assets are returned. This identifier is retrieved using the getBrands operation. For vendors, this field is optional. If a vendor does not specify this field, all assets associated with the vendor are returned. For more information about the difference between a seller and a vendor, see the Amazon Advertising FAQ. [required] 16 | 17 | | query **mediaType** (string): Specifies the media types used to filter the returned array. Currently, only the brandLogo type is supported. If not specified, all media types are returned. Available values : brandLogo, image [optional] 18 | 19 | Returns: 20 | | ApiResponse 21 | """ 22 | return self._request(fill_query_params(kwargs.pop('path')), params=kwargs) 23 | 24 | @sp_endpoint('/v2/stores', method='GET') 25 | def list_stores(self, **kwargs) -> ApiResponse: 26 | """ 27 | List store information for all registered stores under an advertiser. 28 | 29 | Keyword Args 30 | None 31 | 32 | """ 33 | return self._request(fill_query_params(kwargs.pop('path')), params=kwargs) 34 | 35 | # It is not working the endpoint 36 | @sp_endpoint('/v2/stores/{}', method='GET') 37 | def get_store(self, brandEntityId, **kwargs) -> ApiResponse: 38 | return self._request(fill_query_params(kwargs.pop('path'), brandEntityId), params=kwargs) 39 | 40 | # It is not working Cannot consume content type 41 | @sp_endpoint('/stores/assets', method='POST') 42 | def create_asset(self, contentDisposition, contentType, **kwargs) -> ApiResponse: 43 | headers = {'Content-Disposition': contentDisposition, 'Content-Type': contentType} 44 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 45 | -------------------------------------------------------------------------------- /docs/sd/reports.rst: -------------------------------------------------------------------------------- 1 | Reports 2 | ======= 3 | 4 | .. autoclass:: ad_api.api.sd.Reports 5 | 6 | .. autofunction:: ad_api.api.sd.Reports.post_report(self, recordType, **kwargs) -> ApiResponse: 7 | 8 | ### Example python 9 | 10 | .. code-block:: python 11 | 12 | from ad_api.api.sd.reports import Reports 13 | 14 | with open("asins.json", "r", encoding="utf-8") as f: 15 | data = f.read() 16 | 17 | # Available values : campaigns, adGroups, productAds, targets, asins 18 | record_type = 'asins' 19 | 20 | result = Reports().post_report( 21 | recordType=record_type, 22 | body=data 23 | ) 24 | 25 | payload = result.payload 26 | report_id = payload.get('reportId') 27 | 28 | ### Example json 29 | 30 | Open this :download:`json <../../test/reports/sd-sx-asins-report.json>` file to see the result: 31 | 32 | .. literalinclude:: ../../test/reports/sd-sx-asins-report.json 33 | 34 | 35 | 36 | 37 | .. autofunction:: ad_api.api.sd.Reports.get_report(self, reportId, **kwargs) -> ApiResponse: 38 | 39 | ### Example python 40 | 41 | .. code-block:: python 42 | 43 | from ad_api.api.sd.reports import Reports 44 | 45 | # this report_id is obtained from post_report method 46 | report_id = 'amzn1.clicksAPI.v1.p44551.61549C5E.e4599469-7392-4624-a858-fc1fecdb165c' 47 | 48 | result = Reports().get_report( 49 | reportId=report_id 50 | ) 51 | 52 | ### Result json 53 | 54 | .. code-block:: python 55 | 56 | {'expiration': 1640736000000, 57 | 'fileSize': 6546, 58 | 'location': 'https://advertising-api-eu.amazon.com/v1/reports/amzn1.clicksAPI.v1.p44551.61549C5E.e4599469-7392-4624-a858-fc1fecdb165c/download', 59 | 'reportId': 'amzn1.clicksAPI.v1.p44551.61549C5E.e4599469-7392-4624-a858-fc1fecdb165c', 60 | 'status': 'SUCCESS', 61 | 'statusDetails': 'Report has been successfully generated.'}} 62 | 63 | .. autofunction:: ad_api.api.sd.Reports.download_report(self, **kwargs) -> ApiResponse: 64 | 65 | ### Example python 66 | 67 | .. code-block:: python 68 | 69 | from ad_api.api.sd.reports import Reports 70 | 71 | # the url=location is obtained from get_report method need to in stay 'status': 'SUCCESS' if is 'IN_PROGRESS' the report cannot be downloaded 72 | location = 'https://advertising-api-eu.amazon.com/v1/reports/amzn1.clicksAPI.v1.p44551.61549C5E.e4599469-7392-4624-a858-fc1fecdb165c/download' 73 | 74 | # path = '/Users/your-profile/Downloads/report_name' 75 | # mode = "data" # "data (list), raw, url, json, zip, gzip default is url" 76 | 77 | result = Reports().download_report( 78 | url=location, 79 | # file=path, 80 | # format=mode 81 | ) 82 | -------------------------------------------------------------------------------- /docs/sb/reports.rst: -------------------------------------------------------------------------------- 1 | Reports 2 | ======= 3 | 4 | .. autoclass:: ad_api.api.sb.Reports 5 | 6 | .. autofunction:: ad_api.api.sb.Reports.post_report(self, recordType, **kwargs) -> ApiResponse: 7 | 8 | ### Example python 9 | 10 | .. code-block:: python 11 | 12 | from ad_api.api.sb.reports import Reports 13 | 14 | with open("campaigns.json", "r", encoding="utf-8") as f: 15 | data = f.read() 16 | 17 | # Available values : campaigns, adGroups, targets and keywords 18 | record_type = 'campaigns' 19 | 20 | result = Reports().post_report( 21 | recordType=record_type, 22 | body=data 23 | ) 24 | 25 | payload = result.payload 26 | report_id = payload.get('reportId') 27 | 28 | ### Example json 29 | 30 | Open this :download:`json <../../test/reports/sb-sx-campaigns-report.json>` file to see the result: 31 | 32 | .. literalinclude:: ../../test/reports/sb-sx-campaigns-report.json 33 | 34 | 35 | 36 | 37 | .. autofunction:: ad_api.api.sb.Reports.get_report(self, reportId, **kwargs) -> ApiResponse: 38 | 39 | ### Example python 40 | 41 | .. code-block:: python 42 | 43 | from ad_api.api.sb.reports import Reports 44 | 45 | # this report_id is obtained from post_report method 46 | report_id = 'amzn1.clicksAPI.v1.p44551.61549C5E.e4599469-7392-4624-a858-fc1fecdb165c' 47 | 48 | result = Reports().get_report( 49 | reportId=report_id 50 | ) 51 | 52 | ### Result json 53 | 54 | .. code-block:: python 55 | 56 | {'expiration': 1640736000000, 57 | 'fileSize': 6546, 58 | 'location': 'https://advertising-api-eu.amazon.com/v1/reports/amzn1.clicksAPI.v1.p44551.61549C5E.e4599469-7392-4624-a858-fc1fecdb165c/download', 59 | 'reportId': 'amzn1.clicksAPI.v1.p44551.61549C5E.e4599469-7392-4624-a858-fc1fecdb165c', 60 | 'status': 'SUCCESS', 61 | 'statusDetails': 'Report has been successfully generated.'}} 62 | 63 | .. autofunction:: ad_api.api.sb.Reports.download_report(self, **kwargs) -> ApiResponse: 64 | 65 | ### Example python 66 | 67 | .. code-block:: python 68 | 69 | from ad_api.api.sb.reports import Reports 70 | 71 | # the url=location is obtained from get_report method need to in stay 'status': 'SUCCESS' if is 'IN_PROGRESS' the report cannot be downloaded 72 | location = 'https://advertising-api-eu.amazon.com/v1/reports/amzn1.clicksAPI.v1.p44551.61549C5E.e4599469-7392-4624-a858-fc1fecdb165c/download' 73 | 74 | # path = '/Users/your-profile/Downloads/report_name' 75 | # mode = "data" # "data (list), raw, url, json, zip, gzip default is url" 76 | 77 | result = Reports().download_report( 78 | url=location, 79 | # file=path, 80 | # format=mode 81 | ) 82 | -------------------------------------------------------------------------------- /ad_api/api/portfolios_v3.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class PortfoliosV3(Client): 5 | """ """ 6 | 7 | @sp_endpoint('/portfolios/list', method='POST') 8 | def list_portfolios(self, version: int = 3, prefer: bool = False, **kwargs) -> ApiResponse: 9 | r""" 10 | 11 | list_portfolios(body: (str, dict)) -> ApiResponse 12 | 13 | """ 14 | 15 | schema_version = 'application/vnd.spPortfolio.v' + str(version) + '+json' 16 | headers = {"Accept": schema_version, "Content-Type": schema_version} 17 | prefer_value = 'return=representation' 18 | if prefer: 19 | headers.update({"Prefer": prefer_value}) 20 | 21 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 22 | 23 | 24 | 25 | @sp_endpoint('/portfolios', method='POST') 26 | def create_portfolios(self, version: int = 3, prefer: bool = False, **kwargs) -> ApiResponse: 27 | r""" 28 | 29 | create_portfolios(body: (str, dict)) -> ApiResponse 30 | 31 | 32 | """ 33 | schema_version = 'application/vnd.spPortfolio.v' + str(version) + '+json' 34 | headers = {"Accept": schema_version, "Content-Type": schema_version} 35 | prefer_value = 'return=representation' 36 | if prefer: 37 | headers.update({"Prefer": prefer_value}) 38 | 39 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 40 | 41 | @sp_endpoint('/portfolios', method='PUT') 42 | def edit_portfolios(self, version: int = 3, prefer: bool = False, **kwargs) -> ApiResponse: 43 | r""" 44 | 45 | edit_portfolios(body: (str, dict)) -> ApiResponse 46 | 47 | 48 | """ 49 | schema_version = 'application/vnd.spPortfolio.v' + str(version) + '+json' 50 | headers = {"Accept": schema_version, "Content-Type": schema_version} 51 | prefer_value = 'return=representation' 52 | if prefer: 53 | headers.update({"Prefer": prefer_value}) 54 | 55 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 56 | 57 | 58 | @sp_endpoint('/portfolios/budget/usage', method='POST') 59 | def get_budget_usage_for_portfolios(self, version: int = 1, **kwargs) -> ApiResponse: 60 | r""" 61 | 62 | get_budget_usage_for_portfolios(body: (str, dict)) -> ApiResponse 63 | 64 | 65 | """ 66 | schema_version = 'application/vnd.portfoliobudgetusage.v' + str(version) + '+json' 67 | headers = {"Accept": schema_version, "Content-Type": schema_version} 68 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) -------------------------------------------------------------------------------- /ad_api/api/exports.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, fill_query_params, ApiResponse, Utils 2 | 3 | 4 | class Exports(Client): 5 | """Amazon Ads API Exports Version 3 6 | """ 7 | @sp_endpoint('/ads/export', method='POST') 8 | def ads_export(self, version: int = 1, **kwargs) -> ApiResponse: 9 | r""" 10 | application/vnd.adsexport.v1+json 11 | """ 12 | content_type = 'application/vnd.adsexport.v'+ str(version) +'+json' 13 | accept = 'application/vnd.adsexport.v'+ str(version) +'+json' 14 | headers = {'Content-Type': content_type, 'Accept': accept} 15 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 16 | 17 | @sp_endpoint('/campaigns/export', method='POST') 18 | def campaigns_export(self, version: int = 1, **kwargs) -> ApiResponse: 19 | r""" 20 | application/vnd.campaignsexport.v1+json 21 | """ 22 | content_type = 'application/vnd.campaignsexport.v'+ str(version) +'+json' 23 | accept = 'application/vnd.campaignsexport.v'+ str(version) +'+json' 24 | headers = {'Content-Type': content_type, 'Accept': accept} 25 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 26 | 27 | 28 | @sp_endpoint('/adGroups/export', method='POST') 29 | def adgroups_export(self, version: int = 1, **kwargs) -> ApiResponse: 30 | r""" 31 | application/vnd.adgroupsexport.v1+json 32 | """ 33 | content_type = 'application/vnd.adgroupsexport.v'+ str(version) +'+json' 34 | accept = 'application/vnd.adgroupsexport.v'+ str(version) +'+json' 35 | headers = {'Content-Type': content_type, 'Accept': accept} 36 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 37 | 38 | 39 | @sp_endpoint('/targets/export', method='POST') 40 | def targets_export(self, version: int = 1, **kwargs) -> ApiResponse: 41 | r""" 42 | application/vnd.targetsexport.v1+json 43 | """ 44 | content_type = 'application/vnd.targetsexport.v'+ str(version) +'+json' 45 | accept = 'application/vnd.targetsexport.v'+ str(version) +'+json' 46 | headers = {'Content-Type': content_type, 'Accept': accept} 47 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs, headers=headers) 48 | 49 | 50 | @sp_endpoint('/exports/{}', method='GET') 51 | def get_export(self, exportId, typeExport, version: int = 1, **kwargs) -> ApiResponse: 52 | r""" 53 | """ 54 | content_type = 'application/vnd.'+typeExport+'export.v' + str(version) + '+json' 55 | headers = {'Content-Type': content_type, 'Accept': content_type} 56 | return self._request(fill_query_params(kwargs.pop('path'), exportId), params=kwargs, headers=headers) 57 | -------------------------------------------------------------------------------- /ad_api/api/sb/ad_groups.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, fill_query_params, ApiResponse, Utils 2 | 3 | 4 | class AdGroups(Client): 5 | """ 6 | Use the Amazon Advertising API for Sponsored Brands for campaign, ad group, keyword, negative keyword, drafts, Stores, landing pages, and Brands management operations. For more information about Sponsored Brands, see the Sponsored Brands Support Center. For onboarding information, see the account setup topic. 7 | """ 8 | 9 | @sp_endpoint('/sb/adGroups', method='GET') 10 | @Utils.deprecated 11 | def list_ad_groups(self, **kwargs) -> ApiResponse: 12 | r""" 13 | 14 | Gets an array of ad groups associated with the client identifier passed in the authorization header, filtered by specified criteria. 15 | 16 | Keyword Args 17 | | query **startIndex**:*integer* | Optional. Sets a cursor into the requested set of campaigns. Use in conjunction with the count parameter to control pagination of the returned array. 0-indexed record offset for the result set, defaults to 0. 18 | 19 | | query **count**:*integer* | Optional. Sets the number of AdGroup objects in the returned array. Use in conjunction with the startIndex parameter to control pagination. For example, to return the first ten ad groups set startIndex=0 and count=10. To return the next ten ad groups, set startIndex=10 and count=10, and so on. Defaults to max page size. 20 | 21 | | query **name**:*string* | Optional. The returned array includes only ad groups with the specified name. 22 | 23 | | query **adGroupIdFilter**:*string* | Optional. The returned array is filtered to include only ad groups with an identifier specified in the comma-delimited list. 24 | 25 | | query **campaignIdFilter**:*string* | Optional. The returned array is filtered to include only ad groups associated with the campaign identifiers in the specified comma-delimited list. 26 | 27 | | query **creativeType**:*string* | Optional. Filter by the type of creative the campaign is associated with. To get ad groups associated with non-video campaigns specify 'productCollection'. To get ad groups associated with video campaigns, this must be set to 'video'. Returns all ad groups if not specified. Available values : productCollection, video 28 | 29 | Returns: 30 | | ApiResponse 31 | 32 | """ 33 | return self._request(kwargs.pop('path'), params=kwargs) 34 | 35 | @sp_endpoint('/sb/adGroups/{}', method='GET') 36 | def get_ad_group(self, adGroupId, **kwargs) -> ApiResponse: 37 | r""" 38 | 39 | get_ad_group(self, adGroupId, \*\*kwargs) -> ApiResponse 40 | 41 | Gets an ad group specified by identifier. 42 | 43 | Keyword Args 44 | | path **adGroupId**:*number* | Required. The identifier of an existing ad group. 45 | 46 | Returns: 47 | | ApiResponse 48 | 49 | """ 50 | return self._request(fill_query_params(kwargs.pop('path'), adGroupId), params=kwargs) 51 | -------------------------------------------------------------------------------- /ad_api/api/insights.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, fill_query_params, ApiResponse 2 | 3 | 4 | class Insights(Client): 5 | """ """ 6 | 7 | @sp_endpoint('/insights/audiences/{}/overlappingAudiences', method='GET') 8 | def get_insights(self, audienceId: str, version: int = 1, **kwargs) -> ApiResponse: 9 | r""" 10 | get_insights(audienceId: str, version: int = 1, **kwargs) -> ApiResponse 11 | 12 | Retrieves the top audiences that overlap with the provided audience. 13 | 14 | Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] 15 | 16 | path **audienceId**:string | Required. The identifier of an audience. 17 | 18 | internal **version**:int | Optional. The version of the overlapping audiences accept 'application/vnd.insightsaudiencesoverlap.v'+str(version)+'+json'. Available values : 1, 2 Default: 1 19 | 20 | query **adType**:string | Required. The advertising program. Available values : DSP, SD 21 | 22 | query **advertiserId**:string | Optional. The identifier of the advertiser you'd like to retrieve overlapping audiences for. This parameter is required for the DSP adType, but is optional for the SD adType. 23 | 24 | query **minimumAudienceSize**:number | Optional. If specified, the sizes of all returned overlapping audiences will be at least the provided size. This parameter is supported only for request to return application/vnd.insightsaudiencesoverlap.v1+json. 25 | 26 | query **maximumAudienceSize**:number | Optional. If specified, the sizes of all returned overlapping audiences will be at most the provided size. This parameter is supported only for request to return application/vnd.insightsaudiencesoverlap.v1+json. 27 | 28 | query **minimumOverlapAffinity**:number | Optional. If specified, the affinities of all returned overlapping audiences will be at least the provided affinity. 29 | 30 | query **maximumOverlapAffinity**:number | Optional. If specified, the affinities of all returned overlapping audiences will be at most the provided affinity. 31 | 32 | query **audienceCategory**:array[string] | Optional. f specified, the categories of all returned overlapping audiences will be one of the provided categories. 33 | 34 | query **maxResults**:integer | Optional. Sets the maximum number of overlapping audiences in the response. This parameter is supported only for request to return application/vnd.insightsaudiencesoverlap.v2+json. Default value : 30 35 | 36 | query **nextToken**:string | Optional. TToken to be used to request additional overlapping audiences. If not provided, the top 30 overlapping audiences are returned. Note: subsequent calls must be made using the same parameters as used in previous requests. 37 | 38 | """ 39 | 40 | contentType = 'application/vnd.insightsaudiencesoverlap.v' + str(version) + '+json' 41 | headers = {'Accept': contentType} 42 | return self._request(fill_query_params(kwargs.pop('path'), audienceId), params=kwargs, headers=headers) 43 | -------------------------------------------------------------------------------- /docs/api/brand_metrics.rst: -------------------------------------------------------------------------------- 1 | Brand Metrics API open beta 2 | =========================== 3 | 4 | .. autoclass:: ad_api.api.BrandMetrics 5 | 6 | .. autofunction:: ad_api.api.BrandMetrics.post_report 7 | 8 | ### Example python 9 | 10 | .. code-block:: python 11 | 12 | from ad_api.api import BrandMetrics 13 | from ad_api.base import Marketplaces, AdvertisingApiException 14 | import json 15 | 16 | dictionary = { 17 | "categoryNodeTreeName": "es-automotive", 18 | "brandName": "BMW", 19 | "reportStartDate": "2022-03-01", 20 | "lookBackPeriod": "1w", 21 | "format": "CSV", 22 | "metrics": [ 23 | "engagedShopperRateLowerBound", 24 | "customerConversionRate", 25 | "newToBrandCustomerRate" 26 | ], 27 | "reportEndDate": "2022-03-05" 28 | } 29 | 30 | try: 31 | 32 | result = BrandMetrics(account=store, marketplace=marketplace, debug=True).post_report( 33 | body=json.dumps(data) 34 | ) 35 | 36 | print(result) 37 | 38 | except AdvertisingApiException as error: 39 | 40 | print(error) 41 | 42 | ### Example results 43 | 44 | .. literalinclude:: ../../test/brand_metrics/result.json 45 | 46 | .. autofunction:: ad_api.api.BrandMetrics.get_report 47 | 48 | ### Example python 49 | 50 | .. code-block:: python 51 | 52 | from ad_api.api import BrandMetrics 53 | from ad_api.base import Marketplaces, AdvertisingApiException 54 | 55 | report_id = "820882fa-ff22-4772-99e1-6889e3ae97f8" 56 | 57 | try: 58 | 59 | result = BrandMetrics(account=store, marketplace=marketplace, debug=True).get_report( 60 | reportId=report_id 61 | ) 62 | 63 | print(result) 64 | 65 | except AdvertisingApiException as error: 66 | 67 | print(error) 68 | 69 | ### Result to get the location 70 | 71 | .. literalinclude:: ../../test/brand_metrics/location.json 72 | 73 | .. autofunction:: ad_api.api.BrandMetrics.download_report 74 | 75 | 76 | ### Example python 77 | 78 | .. code-block:: python 79 | 80 | from ad_api.api import BrandMetrics 81 | from ad_api.base import Marketplaces, AdvertisingApiException 82 | 83 | location = "https://infrastack-prod-eu-eu-we-generatedreportsbucket49-96329mbigajd.s3.eu-west-1.amazonaws.com/[...]" 84 | 85 | 86 | try: 87 | 88 | result = BrandMetrics(account=store, marketplace=marketplace, debug=True).download_report( 89 | url=location, 90 | format="csv" 91 | ) 92 | 93 | print(result) 94 | 95 | except AdvertisingApiException as error: 96 | 97 | print(error) 98 | 99 | ### Result of the file downloaded 100 | 101 | .. literalinclude:: ../../test/brand_metrics/download.json -------------------------------------------------------------------------------- /ad_api/api/sb/targeting_recommendations.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, ApiResponse, Utils 2 | 3 | 4 | class TargetsRecommendations(Client): 5 | """ 6 | Use the Amazon Advertising API for Sponsored Brands for campaign, ad group, keyword, negative keyword, drafts, Stores, landing pages, and Brands management operations. For more information about Sponsored Brands, see the Sponsored Brands Support Center. For onboarding information, see the account setup topic. 7 | """ 8 | 9 | @sp_endpoint('/sb/recommendations/targets/product/list', method='POST') 10 | def list_products_targets(self, **kwargs) -> ApiResponse: 11 | r""" 12 | Gets a list of recommended categories for targeting. 13 | 14 | Recommendations are based on the ASINs that are passed in the request. 15 | 16 | Request Body 17 | | '**nextToken**': *string*, {'description': 'Operations that return paginated results include a pagination token in this field. To retrieve the next page of results, call the same operation and specify this token in the request. If the NextToken field is empty, there are no further results.'} 18 | | '**maxResults**': *integer*, {'description': 'Sets a limit on the number of results returned by an operation. minimum: 1, maximum: 100'} 19 | | '**filters**': *SBExpression*, {'filterType': '[ ASINS ]', 'values': 'A list of ASINs / An ASIN.'} 20 | 21 | Returns: 22 | ApiResponse 23 | """ 24 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs) 25 | 26 | @sp_endpoint('/sb/recommendations/targets/category', method='POST') 27 | def list_category_targets(self, **kwargs) -> ApiResponse: 28 | r""" 29 | Gets a list of recommended categories for targeting. 30 | 31 | Recommendations are based on the ASINs that are passed in the request. 32 | 33 | Request Body 34 | | '**asins**': *string*, {'description': 'A list of ASINs.'} 35 | 36 | Returns: 37 | ApiResponse 38 | """ 39 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs) 40 | 41 | @sp_endpoint('/sb/recommendations/targets/brand', method='POST') 42 | def list_brand_targets(self, **kwargs) -> ApiResponse: 43 | r""" 44 | Gets a list of brand suggestions. 45 | 46 | The Brand suggestions are based on a list of either category identifiers or keywords passed in the request. It is not valid to specify both category identifiers and keywords in the request. 47 | 48 | Request Body (oneOf ->) 49 | | '**categoryId**': *integer($int64)*, {'description': 'The category identifier for which to get recommendations.'} 50 | | '**keyword**': *string*, {'description': 'The keyword for which to get recommendations.} 51 | 52 | Returns: 53 | ApiResponse 54 | """ 55 | return self._request(kwargs.pop('path'), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs) 56 | -------------------------------------------------------------------------------- /ad_api/api/account.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, fill_query_params, ApiResponse, Utils 2 | 3 | 4 | class Account(Client): 5 | @sp_endpoint('/adsAccounts/list', method='POST') 6 | def list_accounts(self, **kwargs) -> ApiResponse: 7 | r""" 8 | list_accounts(body: (dict, str)) -> ApiResponse 9 | 10 | body: | Optional 11 | | **max_results** (int) Number of records to include in the paginated response. Defaults to max page size for given API. Minimum 10 and a Maximum of 100 [optional] 12 | | **next_token** (string) Token value allowing to navigate to the next response page. [optional] 13 | 14 | Returns: List all advertising accounts for the user associated with the access token. 15 | """ 16 | schema_version = 'application/vnd.listaccountsresource.v1+json' 17 | headers = {'Accept': schema_version, 'Content-Type': schema_version} 18 | 19 | body = Utils.convert_body(kwargs.pop('body'), wrap=False) 20 | return self._request(kwargs.pop('path'), data=body, params=kwargs, headers=headers) 21 | 22 | @sp_endpoint('/adsAccounts/{}', method='GET') 23 | def get_account(self, advertisingAccountId: str, **kwargs) -> ApiResponse: 24 | r""" 25 | get_account(advertisingAccountId: str) -> ApiResponse 26 | 27 | Request attributes of a given advertising account. 28 | 29 | path **advertisingAccountId**:string | Required. This is the global advertising account Id from the client. 30 | """ 31 | schema_version = 'application/vnd.listaccountsresource.v1+json' 32 | headers = {'Accept': schema_version, 'Content-Type': schema_version} 33 | 34 | return self._request(fill_query_params(kwargs.pop('path'), advertisingAccountId), params=kwargs, headers=headers) 35 | 36 | @sp_endpoint('/adsAccounts', method='POST') 37 | def create_account(self, **kwargs) -> ApiResponse: 38 | r""" 39 | create_account(body: (dict, str)) -> ApiResponse 40 | 41 | Create a new advertising account tied to a specific Amazon vendor, seller or author, or to a business who does not sell on Amazon. 42 | 43 | body: | REQUIRED 44 | | **associations** (list[dict]) Associations you would like to link to this advertising account, could be Amazon Vendor, Seller, or just a regular business 45 | | **countryCodes** (list[string]) The countries that you want this account to operate in. 46 | | **accountName** (string) Account names are typically the name of the company or brand being advertised. 47 | | **termsToken** (string) An obfuscated identifier of the termsToken, which is activated when an advertisers accepts the Amazon Ads Agreement in relation to the ads account being register. 48 | """ 49 | schema_version = 'application/vnd.registeradsaccountresource.v1+json' 50 | headers = {'Accept': schema_version, 'Content-Type': schema_version} 51 | 52 | body = Utils.convert_body(kwargs.pop('body'), wrap=False) 53 | return self._request(kwargs.pop('path'), data=body, params=kwargs, headers=headers) 54 | -------------------------------------------------------------------------------- /ad_api/api/sp/snapshots.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, fill_query_params, ApiResponse, Utils 2 | 3 | 4 | class Snapshots(Client): 5 | """ 6 | Use the Amazon Advertising API for Sponsored Products for campaign, ad group, keyword, negative keyword, and product ad management operations. For more information about Sponsored Products, see the Sponsored Products Support Center. For onboarding information, see the account setup topic. 7 | """ 8 | 9 | @sp_endpoint('/v2/sp/{}/snapshot', method='POST') 10 | def post_snapshot(self, recordType, **kwargs) -> ApiResponse: 11 | """ 12 | Request a file-based snapshot of all entities of the specified type in the account satisfying the filtering criteria. 13 | 14 | Keyword Args 15 | | path **recordType** (integer): The type of entity for which the snapshot is generated. Available values : campaigns, adGroups, keywords, negativeKeywords, campaignNegativeKeywords, productAds, targets, negativeTargets [required] 16 | 17 | Request body 18 | | **stateFilter** (string): [required] [ enabled, paused, archived, enabled, paused, enabled, archived, paused, archived, enabled, paused, archived ]. 19 | 20 | Returns: 21 | ApiResponse 22 | """ 23 | return self._request(fill_query_params(kwargs.pop('path'), recordType), data=Utils.convert_body(kwargs.pop('body'), False), params=kwargs) 24 | 25 | @sp_endpoint('/v2/snapshots/{}', method='GET') 26 | def get_snapshot(self, snapshotId, **kwargs) -> ApiResponse: 27 | r""" 28 | Gets the status of a requested snapshot. 29 | 30 | Keyword Args 31 | | path **snapshotId** (number): The snapshot identifier. [required] 32 | 33 | Returns: 34 | ApiResponse 35 | """ 36 | return self._request(fill_query_params(kwargs.pop('path'), snapshotId), params=kwargs) 37 | 38 | def download_snapshot(self, **kwargs) -> ApiResponse: 39 | r""" 40 | Downloads the snapshot previously get report specified by location (this is not part of the official Amazon Advertising API, is a helper method to download the snapshot). Take in mind that a direct download of location returned in get_snapshot will return 401 - Unauthorized. 41 | 42 | kwarg parameter **file** if not provided will take the default amazon name from path download (add a path with slash / if you want a specific folder, do not add extension as the return will provide the right extension based on format choosed if needed) 43 | 44 | kwarg parameter **format** if not provided a format will return a url to download the snapshot (this url has a expiration time) 45 | 46 | Keyword Args 47 | | **url** (string): The location obatined from get_snapshot [required] 48 | | **file** (string): The path to save the file if mode is download json, zip or gzip. [optional] 49 | | **format** (string): The mode to download the snapshot: data (list), raw, url, json, zip, gzip. Default (url) [optional] 50 | 51 | Returns: 52 | ApiResponse 53 | """ 54 | return self._download(self, params=kwargs) 55 | -------------------------------------------------------------------------------- /docs/sp/snapshots.rst: -------------------------------------------------------------------------------- 1 | Snapshots 2 | ========= 3 | 4 | .. autoclass:: ad_api.api.sp.Snapshots 5 | 6 | .. autofunction:: ad_api.api.sp.Snapshots.post_snapshot(self, recordType, **kwargs) -> ApiResponse: 7 | 8 | ### Example python 9 | 10 | .. code-block:: python 11 | 12 | from ad_api.api.sp.snapshots import Snapshots 13 | 14 | file = open("request.json") 15 | data = file.read() 16 | file.close() 17 | 18 | # Available values : campaigns, adGroups, keywords, negativeKeywords, campaignNegativeKeywords, productAds, targets, negativeTargets 19 | 20 | record_type = 'campaigns' 21 | 22 | result = Snapshots().post_snapshot( 23 | recordType=record_type, 24 | body=data 25 | ) 26 | 27 | ### Example json 28 | 29 | Open this :download:`json <../../test/snapshots/sp-sx-state-filter.json>` file to see the result: 30 | 31 | .. literalinclude:: ../../test/snapshots/sp-sx-state-filter.json 32 | 33 | .. autofunction:: ad_api.api.sp.Snapshots.get_snapshot(self, reportId, **kwargs) -> ApiResponse: 34 | 35 | ### Example python 36 | 37 | .. code-block:: python 38 | 39 | from ad_api.api.sp.snapshots import Snapshots 40 | 41 | # this snapshot_id is obtained from post_snapshot method 42 | snapshot_id = "amzn1.clicksAPI.v1.p44551.614D9309.84477233-ccc8-4591-80f2-1f96b7ea9c7e" 43 | 44 | result = Snapshots().get_snapshot( 45 | snapshotId=snapshot_id 46 | ) 47 | 48 | ### Result json 49 | 50 | .. code-block:: python 51 | 52 | {'expiration': 1640304000000, 53 | 'fileSize': 1241, 54 | 'location': 'https://advertising-api-eu.amazon.com/v1/snapshots/amzn1.clicksAPI.v1.p44551.614D9309.84477233-ccc8-4591-80f2-1f96b7ea9c7e/download', 55 | 'snapshotId': 'amzn1.clicksAPI.v1.p44551.614D9309.84477233-ccc8-4591-80f2-1f96b7ea9c7e', 56 | 'status': 'SUCCESS', 57 | 'statusDetails': 'Snapshot has been successfully generated.'}} 58 | 59 | .. autofunction:: ad_api.api.sp.Snapshots.download_snapshot(self, **kwargs) -> ApiResponse: 60 | 61 | .. warning:: 62 | 63 | This method is not a part of the Amazon Advertising Api. 64 | 65 | ### Example python 66 | 67 | .. code-block:: python 68 | 69 | from ad_api.api.sp.snapshots import Snapshots 70 | 71 | # the url=location is obtained from get_snapshot method need to in stay 'status': 'SUCCESS' if is 'IN_PROGRESS' the snapshot cannot be downloaded 72 | location = 'https://advertising-api-eu.amazon.com/v1/snapshots/amzn1.clicksAPI.v1.p44551.614D9309.84477233-ccc8-4591-80f2-1f96b7ea9c7e/download' 73 | 74 | # path = '/Users/your-profile/Downloads/report_name' 75 | # mode = "data" # "data (list), raw, url, json, zip, gzip default is url" 76 | 77 | result = Reports().download_report( 78 | url=location, 79 | # file=path, 80 | # format=mode 81 | ) 82 | 83 | 84 | -------------------------------------------------------------------------------- /ad_api/api/stream.py: -------------------------------------------------------------------------------- 1 | from ad_api.base import Client, sp_endpoint, fill_query_params, ApiResponse, Utils 2 | 3 | 4 | class Stream(Client): 5 | """Amazon Marketing Stream 6 | 7 | Documentation: https://advertising.amazon.com/API/docs/en-us/amazon-marketing-stream/openapi 8 | """ 9 | 10 | @sp_endpoint('/streams/subscriptions', method='POST') 11 | def create_subscription(self, **kwargs) -> ApiResponse: 12 | r""" 13 | Request the creation a new subscription 14 | 15 | Request body 16 | | **notes** (string): [optional] Additional details associated with the subscription 17 | | **clientRequestToken** (string): [required] Unique value supplied by the caller used to track identical API requests. Should request be re-tried, the caller should supply the same value. 18 | | **dataSetId** (string): [required] Identifier of data set, callers can be subscribed to. Please refer to https://advertising.amazon.com/API/docs/en-us/amazon-marketing-stream/data-guide for the list of all data sets. 19 | | **destinationArn** (string): [required] AWS ARN of the destination endpoint associated with the subscription. Supported destination type - SQS. 20 | 21 | Returns: 22 | ApiResponse 23 | """ 24 | body = Utils.convert_body(kwargs.pop('body'), wrap=False) 25 | return self._request(kwargs.pop('path'), data=body, params=kwargs) 26 | 27 | @sp_endpoint('/streams/subscriptions/{}', method='PUT') 28 | def update_subscription(self, subscription_id: str, **kwargs) -> ApiResponse: 29 | r""" 30 | Update an existing subscription 31 | 32 | Request body 33 | | **notes** (string): [optional] Additional details associated with the subscription 34 | | **status** (string): [optional] Update the status of the entity. Supported value: 'ARCHIVED' 35 | 36 | Returns: 37 | ApiResponse 38 | """ 39 | body = Utils.convert_body(kwargs.pop('body'), wrap=False) 40 | return self._request(fill_query_params(kwargs.pop('path'), subscription_id), data=body, params=kwargs) 41 | 42 | @sp_endpoint('/streams/subscriptions/{}', method='GET') 43 | def get_subscription(self, subscription_id: str, **kwargs) -> ApiResponse: 44 | r""" 45 | Fetch a specific subscription by ID 46 | 47 | Keyword Args 48 | | path **subscriptionId** (string): [required] Unique subscription identifier 49 | 50 | Returns: 51 | ApiResponse 52 | """ 53 | return self._request(fill_query_params(kwargs.pop('path'), subscription_id), params=kwargs) 54 | 55 | @sp_endpoint('/streams/subscriptions', method='GET') 56 | def list_subscriptions(self, **kwargs) -> ApiResponse: 57 | r""" 58 | List subscriptions 59 | 60 | query **maxResults**:*string* | Optional. [1-5000] Desired number of entries in the response, defaults to maximum value 61 | 62 | query **startingToken**:*string* | Optional. Token which can be used to get the next page of results, if more entries exist 63 | 64 | Returns: 65 | ApiResponse 66 | """ 67 | return self._request(kwargs.pop('path'), params=kwargs) 68 | --------------------------------------------------------------------------------