Amazon 14 min read

Amazon Advertising API: Automate Bid Management and Campaign Reporting

The Amazon Advertising API gives programmatic access to Sponsored Products, Sponsored Brands, and DSP campaigns. This guide covers authentication, key endpoints, automated bid management, and building a reporting pipeline.

A
Aumlytics Team
·

If you manage Amazon Advertising at scale — dozens of campaigns, thousands of keywords, multiple marketplaces — manual bid management in Seller Central or Vendor Central becomes a bottleneck. Hourly bid adjustments, keyword harvesting from Search Term Reports, and daily performance reporting all take time that could be automated.

The Amazon Advertising API provides programmatic access to everything in the Ads console. This guide covers authentication, the key endpoints you’ll actually use, automating bid changes based on performance rules, and building a lightweight reporting pipeline.


The Amazon Advertising API vs SP-API

These are two separate APIs that often get confused:

Amazon Advertising APISP-API
PurposeManage and report on advertising campaignsAccess seller data (orders, inventory, listings)
Cost (2025)Free to use$1,400/year subscription + usage fees
Auth systemLogin with Amazon (LWA) OAuthLogin with Amazon (LWA) OAuth
Use forBids, campaigns, keywords, search terms, DSPOrders, inventory, listings, pricing

The Advertising API is free to use — no subscription fees, no usage billing. This makes it significantly more accessible for sellers building custom tooling.

Official reference: Amazon Advertising API documentation


Setting Up API Access

Step 1: Become an Amazon Advertising API Developer

  1. Go to advertising.amazon.comAPIRequest Access
  2. Fill in the developer registration form — include a description of what you’re building
  3. Wait for approval (typically 1–3 business days for legitimate seller use cases)

Step 2: Create an LWA App

Once approved:

  1. Go to developer.amazon.comLogin with Amazon
  2. Create a New Security Profile
  3. Note your Client ID and Client Secret

Step 3: Authorise Your Account

The Advertising API uses OAuth 2.0. You need to authorise your Amazon Ads account:

  1. Direct the account owner to this URL (replace placeholders):
https://www.amazon.com/ap/oa?client_id=YOUR_CLIENT_ID
&scope=advertising::campaign_management
&response_type=code
&redirect_uri=YOUR_REDIRECT_URI
&state=random_state_string
  1. After authorisation, you receive a code parameter in the redirect URL

  2. Exchange it for tokens:

import requests

token_response = requests.post(
    'https://api.amazon.com/auth/o2/token',
    data={
        'grant_type': 'authorization_code',
        'code': 'YOUR_AUTH_CODE',
        'redirect_uri': 'YOUR_REDIRECT_URI',
        'client_id': 'YOUR_CLIENT_ID',
        'client_secret': 'YOUR_CLIENT_SECRET'
    }
)
tokens = token_response.json()
access_token = tokens['access_token']
refresh_token = tokens['refresh_token']  # Store this securely — it's long-lived

Step 4: Get Your Profile ID

Most Advertising API endpoints require a Profile ID — the identifier for a specific advertiser account in a specific marketplace.

headers = {
    'Authorization': f'Bearer {access_token}',
    'Amazon-Advertising-API-ClientId': 'YOUR_CLIENT_ID',
    'Content-Type': 'application/json'
}

profiles_response = requests.get(
    'https://advertising-api.amazon.com/v2/profiles',
    headers=headers
)
profiles = profiles_response.json()
# Each profile = one marketplace account
for profile in profiles:
    print(f"Profile ID: {profile['profileId']}, Marketplace: {profile['countryCode']}, Type: {profile['accountInfo']['type']}")

Add the chosen profile ID to all subsequent requests:

headers['Amazon-Advertising-API-Scope'] = 'YOUR_PROFILE_ID'

Refreshing Access Tokens

Access tokens expire after 1 hour. Use the refresh token to get a new one:

def refresh_access_token(refresh_token, client_id, client_secret):
    response = requests.post(
        'https://api.amazon.com/auth/o2/token',
        data={
            'grant_type': 'refresh_token',
            'refresh_token': refresh_token,
            'client_id': client_id,
            'client_secret': client_secret
        }
    )
    return response.json()['access_token']

Store the refresh token in a secrets manager (AWS Secrets Manager, HashiCorp Vault, or environment variable) — never in source code.


Key Endpoints for Sponsored Products

The Sponsored Products API (v2 and v3) covers the most common use cases for sellers.

List Campaigns

response = requests.get(
    'https://advertising-api.amazon.com/v2/sp/campaigns',
    headers=headers,
    params={
        'stateFilter': 'enabled',
        'count': 100
    }
)
campaigns = response.json()

List Ad Groups

response = requests.get(
    'https://advertising-api.amazon.com/v2/sp/adGroups',
    headers=headers,
    params={
        'campaignId': campaign_id,
        'stateFilter': 'enabled'
    }
)

Get Keywords with Bids

response = requests.get(
    'https://advertising-api.amazon.com/v2/sp/keywords',
    headers=headers,
    params={
        'adGroupId': ad_group_id,
        'stateFilter': 'enabled,paused',
        'count': 500
    }
)
keywords = response.json()

Update Keyword Bids

update_payload = [
    {
        'keywordId': keyword['keywordId'],
        'bid': new_bid_value,
        'state': 'enabled'
    }
    for keyword in keywords_to_update
]

response = requests.put(
    'https://advertising-api.amazon.com/v2/sp/keywords',
    headers=headers,
    json=update_payload
)

Building an Automated Bid Manager

Here’s a practical bid automation script that adjusts keyword bids based on ACoS (Advertising Cost of Sale) targets:

import requests
import json
from datetime import datetime, timedelta

class AmazonAdsBidManager:
    def __init__(self, access_token, client_id, profile_id):
        self.headers = {
            'Authorization': f'Bearer {access_token}',
            'Amazon-Advertising-API-ClientId': client_id,
            'Amazon-Advertising-API-Scope': profile_id,
            'Content-Type': 'application/json'
        }
        self.base_url = 'https://advertising-api.amazon.com'

    def get_keyword_performance(self, ad_group_id, days=14):
        """Fetch keyword performance report for the past N days."""
        end_date = datetime.now().strftime('%Y%m%d')
        start_date = (datetime.now() - timedelta(days=days)).strftime('%Y%m%d')

        # Request report
        report_request = requests.post(
            f'{self.base_url}/reporting/reports',
            headers=self.headers,
            json={
                "name": f"Keyword Report {end_date}",
                "startDate": start_date,
                "endDate": end_date,
                "configuration": {
                    "adProduct": "SPONSORED_PRODUCTS",
                    "groupBy": ["keyword"],
                    "columns": [
                        "keywordId", "keywordText", "matchType",
                        "impressions", "clicks", "spend",
                        "sales1d", "sales7d", "sales14d", "sales30d",
                        "orders1d", "orders7d"
                    ],
                    "reportTypeId": "spKeywords",
                    "timeUnit": "SUMMARY",
                    "format": "GZIP_JSON"
                }
            }
        )
        return report_request.json().get('reportId')

    def calculate_new_bid(self, current_bid, spend, sales, target_acos=0.25):
        """
        Calculate adjusted bid based on actual vs target ACoS.

        ACoS = Spend / Sales
        If ACoS > target: reduce bid (overspending)
        If ACoS < target: increase bid (room to spend more)
        """
        if sales == 0:
            if spend > 5.0:  # Spending with no sales — reduce significantly
                return max(current_bid * 0.7, 0.02)
            return current_bid  # No data yet — don't change

        actual_acos = spend / sales

        if actual_acos == 0:
            return current_bid

        # Adjustment factor: ratio of target ACoS to actual ACoS
        # If actual ACoS is 0.30 and target is 0.25: factor = 0.25/0.30 = 0.833 → reduce bid
        # If actual ACoS is 0.20 and target is 0.25: factor = 0.25/0.20 = 1.25 → increase bid
        adjustment_factor = target_acos / actual_acos

        # Cap adjustments to prevent drastic changes in one pass
        adjustment_factor = max(0.75, min(1.25, adjustment_factor))

        new_bid = round(current_bid * adjustment_factor, 2)

        # Enforce min/max bid guardrails
        new_bid = max(0.02, min(new_bid, 10.00))

        return new_bid

    def run_bid_optimisation(self, campaign_id, target_acos=0.25, min_clicks=10):
        """
        Full bid optimisation pass for all keywords in a campaign.
        Only adjusts keywords with sufficient click data.
        """
        # Get ad groups in campaign
        ad_groups_resp = requests.get(
            f'{self.base_url}/v2/sp/adGroups',
            headers=self.headers,
            params={'campaignId': campaign_id, 'stateFilter': 'enabled'}
        )
        ad_groups = ad_groups_resp.json()

        updates = []

        for ad_group in ad_groups:
            keywords_resp = requests.get(
                f'{self.base_url}/v2/sp/keywords',
                headers=self.headers,
                params={
                    'adGroupId': ad_group['adGroupId'],
                    'stateFilter': 'enabled',
                    'count': 500
                }
            )
            keywords = keywords_resp.json()

            # Get performance data (simplified — in production, use the Reports API)
            for kw in keywords:
                clicks = kw.get('extendedData', {}).get('clicks', 0)
                spend = kw.get('extendedData', {}).get('cost', 0) / 1_000_000  # Amazon returns microcurrency
                sales = kw.get('extendedData', {}).get('attributedSales14d', 0) / 1_000_000

                if clicks < min_clicks:
                    continue  # Not enough data

                current_bid = kw['bid']
                new_bid = self.calculate_new_bid(current_bid, spend, sales, target_acos)

                if new_bid != current_bid:
                    updates.append({
                        'keywordId': kw['keywordId'],
                        'bid': new_bid,
                        'state': 'enabled'
                    })
                    print(f"  {kw['keywordText']}: £{current_bid:.2f} → £{new_bid:.2f} "
                          f"(ACoS: {spend/sales*100:.1f}% → target {target_acos*100:.0f}%)")

        # Apply updates in batches of 100
        if updates:
            for i in range(0, len(updates), 100):
                batch = updates[i:i+100]
                requests.put(
                    f'{self.base_url}/v2/sp/keywords',
                    headers=self.headers,
                    json=batch
                )
            print(f"Updated {len(updates)} keyword bids")

        return len(updates)

Building a Search Term Harvesting Pipeline

Search Term Reports reveal which actual customer search queries triggered your ads — and which ones are converting. Automating keyword harvesting from these reports is one of the highest-ROI automations for Amazon advertising.

def harvest_converting_search_terms(report_data, min_orders=2, max_acos=0.30):
    """
    Extract high-performing search terms to add as exact match keywords.

    Args:
        report_data: Parsed search term report (list of dicts)
        min_orders: Minimum orders to consider a term proven
        max_acos: Maximum ACoS threshold for harvesting
    """
    candidates = []

    for term in report_data:
        orders = term.get('orders14d', 0)
        spend = term.get('spend', 0)
        sales = term.get('sales14d', 0)

        if orders < min_orders:
            continue

        acos = spend / sales if sales > 0 else 999

        if acos <= max_acos:
            candidates.append({
                'search_term': term['searchTerm'],
                'orders': orders,
                'acos': round(acos * 100, 1),
                'spend': round(spend, 2),
                'sales': round(sales, 2),
                'recommended_bid': round(
                    (sales / orders) * max_acos * 0.8, 2  # Bid at 80% of target ACoS CPA
                )
            })

    # Sort by orders descending
    return sorted(candidates, key=lambda x: x['orders'], reverse=True)

Output from this function gives you a list of search terms, their performance, and a recommended starting bid — ready to add as exact match keywords programmatically.


Scheduling with n8n or Cron

For production use, these scripts should run on a schedule:

Daily at 6 AM: Pull Search Term Report → harvest converting terms → add as new exact match keywords

Daily at 7 AM: Run bid optimisation for all active campaigns using 14-day data

Weekly on Mondays: Pull campaign performance summary → post to Slack

With n8n: use a Schedule Trigger + Execute Command node (if running Python) or the HTTP Request node with direct API calls.

With Linux cron:

# /etc/cron.d/amazon-ads
0 6 * * * /usr/bin/python3 /opt/amazon-ads/harvest_keywords.py >> /var/log/amazon-ads.log 2>&1
0 7 * * * /usr/bin/python3 /opt/amazon-ads/bid_optimiser.py >> /var/log/amazon-ads.log 2>&1
0 9 * * 1 /usr/bin/python3 /opt/amazon-ads/weekly_report.py >> /var/log/amazon-ads.log 2>&1

Automating Amazon Advertising bidding and reporting is one of the clearest ROI cases in e-commerce tooling. The API is free, the data is rich, and the performance improvement from consistent, rule-based bid management typically outperforms manual management as campaign scale increases.

We build Amazon Advertising API integrations — bid automation, keyword harvesting pipelines, performance dashboards, and multi-marketplace reporting. Book a free consultation to discuss your advertising automation requirements.

#amazon#advertising-api#sponsored-products#ppc#automation#bid-management#reporting#python

Want This Implemented Correctly?

Let our team apply these concepts to your specific setup — with QA validation and 30 days of support.