Change History

https://d1y2lf8k3vrkfu.cloudfront.net/openapi/en-us/dest/Changehistory_prod_3p.json

Provides information about changes made to campaigns, ad groups, ads and the rest of the entity tree.

Specification: https://d1y2lf8k3vrkfu.cloudfront.net/openapi/en-us/dest/Changehistory_prod_3p.json Documentation: https://advertising.amazon.com/API/docs/en-us/change-history/

class ad_api.api.History(account: str = 'default', marketplace: Marketplaces = Marketplaces.EU, credentials: Dict[str, str] | None = None, proxies: Dict[str, str] | None = None, verify: bool = True, timeout: int | None = None, debug: bool = False, access_token: str | None = None, verify_additional_credentials: bool = True)

Note

The contract is version 3.0 and carries no beta marker: no x-beta, no x-amz-*, no deprecation notice. The page is listed as stable because of that.

### Endpoint map

:header-rows: 1

Path

Method

Media type

Answer

POST /history

get_history

vnd.historyresponse.v1.0

200

POST /history with version="1.1"

get_history

vnd.historyresponse.v1.1

200

The second row is the same route. THEME is the only event type that needs version 1.1; at 1.0 the service answers 400 and names it.

### Event types

:header-rows: 1

Type

Version needed

AD

1.0

AD_GROUP

1.0

CAMPAIGN

1.0

KEYWORD

1.0

NEGATIVE_KEYWORD

1.0

PRODUCT_TARGETING

1.0

THEME

1.1

### Request body

:header-rows: 1

Field

Type

Meaning

fromDate

integer

Required. Epoch in milliseconds. At most 90 days back, counted from the moment of the request.

toDate

integer

Required. Epoch in milliseconds.

eventTypes

object

Required. One or more of the types above, each carrying an object, usually {} or {"filters": [...]}.

count

integer

Page size. Default 100, minimum 50, maximum 200.

nextToken

string

Token from a previous answer. Mutually exclusive with pageOffset.

pageOffset

integer

Alternative to nextToken, up to 10000.

sort

object

{"direction": "ASC"|"DESC", "key": "DATE"}.

The names are camelCase. A body written with from_date answers 400 and names fromDate as a required field.

### Answer

:header-rows: 1

Field

Type

Meaning

events

array

The changes found, empty when there were none.

pageSize

integer

The page size the service applied, taken from count.

pageOffset

integer

Where this page starts.

maxPageNumber

integer

The last page number reachable.

totalRecords

integer

How many changes the account has in the window.

An empty events with totalRecords at 0 means the account saw no change in that window. The call worked; there was nothing to report.

### Two things that cost time

The window moves. Ninety days is counted from the service clock at the moment of the request. A window computed once at the top of a script and reused drifts past the edge and answers 400 fromDate is more than 90 days ago. In testing, 717 milliseconds of drift was enough. Compute it per call and leave a day of slack.

The route throttles. Seven calls within twelve seconds answered 429 Too Many Requests. Space them out.

### Example python

from datetime import datetime, timedelta, timezone
from ad_api.api import History

def get_history():
    now = datetime.now(timezone.utc)
    frm = now - timedelta(days=7)   # 90 max, leave slack

    body = {
        "fromDate": int(frm.timestamp() * 1000),
        "toDate": int(now.timestamp() * 1000),
        "eventTypes": {"CAMPAIGN": {"filters": ["STATUS"]}},
        "count": 50,
        "sort": {"direction": "DESC", "key": "DATE"},
    }

    res = History(account='my_account', debug=False).get_history(body=body)
    p = res.payload
    print(f"{p['totalRecords']} cambios")
    for event in p["events"]:
        print(event)

if __name__ == '__main__':
    get_history()

### Example with THEME

THEME needs version 1.1. The method takes it as a string, because the versions are 1.0 and 1.1.

from datetime import datetime, timedelta, timezone
from ad_api.api import History

now = datetime.now(timezone.utc)
frm = now - timedelta(days=7)

body = {
    "fromDate": int(frm.timestamp() * 1000),
    "toDate": int(now.timestamp() * 1000),
    "eventTypes": {"THEME": {"filters": ["STATUS"]}},
}

res = History(account='my_account', debug=False).get_history(
    body=body, version="1.1")

### Example query.json

Download json the file to use:

{
  "fromDate": 1626739199000,
  "toDate": 1632095999000,
  "pageOffset": 10,
  "count": 60,
  "sort": {
    "key": "DATE",
    "direction": "DESC"
  },
  "eventTypes": {
    "CAMPAIGN": {
      "filters": [
        "BUDGET_AMOUNT"
      ],
      "eventTypeIds": [
        "137359064782313"
      ]
    }
  }
}
get_history(version: str = '1.0', **kwargs) → ApiResponse

Returns the changes made to campaigns, ad groups, ads and the rest of the entity tree, within a time range. Only events belonging to the authenticated advertiser come back.

Keyword Args

  • version (string): the Accept version, "1.0" by default. Pass "1.1" to query the THEME event type, which the service refuses at 1.0. The other six types work at either. [optional]

Request Body (required)

  • fromDate (integer): epoch in milliseconds, not seconds.

  • toDate (integer): epoch in milliseconds.

  • eventTypes (object): one or more of AD, AD_GROUP, CAMPAIGN, KEYWORD, NEGATIVE_KEYWORD, PRODUCT_TARGETING and THEME. Each carries an object, usually {} or {"filters": [...]}.

  • count (integer): page size. Default 100, minimum 50, maximum 200.

  • nextToken (string): token from a previous answer. Mutually exclusive with pageOffset.

  • pageOffset (integer): alternative to nextToken, up to 10000.

  • sort (object): {"direction": "ASC"|"DESC", "key": "DATE"}.

The field names are camelCase and the service rejects anything else: a body written with from_date answers 400 naming fromDate as a required field. The dates are counted back from the moment of the request, so a window computed once and reused drifts out of range: the service measures 90 days from its own clock, and a few seconds of drift past the edge answers 400 fromDate is more than 90 days ago.

This route throttles. Seven calls within twelve seconds answered 429.

Returns

ApiResponse with events, pageSize, pageOffset, maxPageNumber and totalRecords. An empty events with totalRecords at 0 means the account saw no change in the window, not that the call failed.