Skip to content

Change history

POST /history records every change to Sponsored Products and Sponsored Brands entities for 90 days: previous value, new value and timestamp. It does not record who made the change, and it does not cover Sponsored Display.

from datetime import datetime, timedelta, UTC

events = us.history.list(since=datetime.now(UTC) - timedelta(days=3))
for e in events:
    print(e.timestamp, e.entity_type, e.entity_id, e.change_type,
          e.previous_value, "->", e.new_value, e.metadata)

Filters:

us.history.list(
    since=..., until=...,
    entity_types=["KEYWORD", "PRODUCT_TARGETING"],   # also CAMPAIGN, AD_GROUP, AD, NEGATIVE_KEYWORD
    changes=["BID_AMOUNT", "STATUS"],                # also CREATED, BUDGET_AMOUNT, PLACEMENT_GROUP, ...
    entity_ids=["123456789012345"],
    campaign_ids=["987654321098765"],
    newest_first=True,
)

Values are strings ("0.75", "PAUSED"). metadata carries context such as the keyword text, match type, campaign and ad group.

What a value was at a moment

Report rows carry bids as of the pull, not as of the row's date. To know what a bid really was on a past day:

us.history.value_at("KEYWORD", "123456789012345", "BID_AMOUNT",
                    datetime(2026, 9, 20, tzinfo=UTC))   # "0.15", or None if unchanged

None means no change to that field in the window, so the value then is the value now.

Restoring a past state

us.history.rollback_plan(since) builds a change plan that restores bids and states to what they were at since. See restoring from change history.

What the library handles

  • A query with no entity ids and no campaigns returns nothing from Amazon unless it asks for the whole advertiser (useProfileIdAdvertiser). The library adds that.
  • Amazon's per-type change filters did not filter reliably when tested, and other entity types came back too, so filtering happens client-side.
  • Paging through nextToken, and splitting id lists into Amazon's groups of 10.