Stay organized with collectionsSave and categorize content based on your preferences.
Change Eventprovides a detailed breakdown of
what changed in your account. When reporting on a create operation, all new
field values are returned; and when reporting on an update operation, both new
and old values are returned, giving you a complete picture of individual
changes.
Additionally, theclient
type(such as API or web
client) that was used to make the change is indicated, as well as which user
made the change, assuming that information is also visible in the Change History
of the web client.
The list of changes must be filtered by date. The date range must be within the
past 30 days.
The query must also include aLIMITclause restricting results to at most
10,000 rows. If you're interested in more than 10,000 results, make a note of
the timestamp of the last entry in the returned result set, and then set your
date range on the following query to start after that time to continue where you
left off.
A change can take up to three minutes to be reflected in the change event
results.
The following example demonstrates how to fetch change events and process them,
including determining who made the change, which client type they used to make
the change, and the old and new values of each field in the change:
defmain(client:GoogleAdsClient,customer_id:str)->None:"""Gets specific details about the most recent changes in the given account.Args:client: The Google Ads client.customer_id: The Google Ads customer ID."""googleads_service:GoogleAdsServiceClient=client.get_service("GoogleAdsService")# Construct a query to find details for recent changes in your account.# The LIMIT clause is required for the change_event resource.# The maximum size is 10000, but a low limit was set here for demonstrative# purposes. For more information see:# https://developers.google.com/google-ads/api/docs/change-event#getting_changes# The WHERE clause on change_date_time is also required. It must specify a# window within the past 30 days.tomorrow:str=(datetime.now()+timedelta(1)).strftime("%Y-%m-%d")two_weeks_ago:str=(datetime.now()+timedelta(-14)).strftime("%Y-%m-%d")query:str=f"""SELECTchange_event.resource_name,change_event.change_date_time,change_event.change_resource_name,change_event.user_email,change_event.client_type,change_event.change_resource_type,change_event.old_resource,change_event.new_resource,change_event.resource_change_operation,change_event.changed_fieldsFROM change_eventWHERE change_event.change_date_time <= '{tomorrow}'AND change_event.change_date_time >= '{two_weeks_ago}'ORDER BY change_event.change_date_time DESCLIMIT 5"""search_request:SearchGoogleAdsRequest=client.get_type("SearchGoogleAdsRequest")search_request.customer_id=customer_idsearch_request.query=queryresults:SearchPagedResponse=googleads_service.search(request=search_request)row:GoogleAdsRowforrowinresults:event:ChangeEvent=row.change_eventresource_type:str=event.change_resource_type.nameold_resource:Anynew_resource:Anyifresource_type=="AD":old_resource=event.old_resource.adnew_resource=event.new_resource.adelifresource_type=="AD_GROUP":old_resource=event.old_resource.ad_groupnew_resource=event.new_resource.ad_groupelifresource_type=="AD_GROUP_AD":old_resource=event.old_resource.ad_group_adnew_resource=event.new_resource.ad_group_adelifresource_type=="AD_GROUP_ASSET":old_resource=event.old_resource.ad_group_assetnew_resource=event.new_resource.ad_group_assetelifresource_type=="AD_GROUP_CRITERION":old_resource=event.old_resource.ad_group_criterionnew_resource=event.new_resource.ad_group_criterionelifresource_type=="AD_GROUP_BID_MODIFIER":old_resource=event.old_resource.ad_group_bid_modifiernew_resource=event.new_resource.ad_group_bid_modifierelifresource_type=="AD_GROUP_FEED":old_resource=event.old_resource.ad_group_feednew_resource=event.new_resource.ad_group_feedelifresource_type=="ASSET":old_resource=event.old_resource.assetnew_resource=event.new_resource.assetelifresource_type=="ASSET_SET":old_resource=event.old_resource.asset_setnew_resource=event.new_resource.asset_setelifresource_type=="ASSET_SET_ASSET":old_resource=event.old_resource.asset_set_assetnew_resource=event.new_resource.asset_set_assetelifresource_type=="CAMPAIGN":old_resource=event.old_resource.campaignnew_resource=event.new_resource.campaignelifresource_type=="CAMPAIGN_ASSET":old_resource=event.old_resource.campaign_assetnew_resource=event.new_resource.campaign_assetelifresource_type=="CAMPAIGN_ASSET_SET":old_resource=event.old_resource.campaign_asset_setnew_resource=event.new_resource.campaign_asset_setelifresource_type=="CAMPAIGN_BUDGET":old_resource=event.old_resource.campaign_budgetnew_resource=event.new_resource.campaign_budgetelifresource_type=="CAMPAIGN_CRITERION":old_resource=event.old_resource.campaign_criterionnew_resource=event.new_resource.campaign_criterionelifresource_type=="CAMPAIGN_FEED":old_resource=event.old_resource.campaign_feednew_resource=event.new_resource.campaign_feedelifresource_type=="CUSTOMER_ASSET":old_resource=event.old_resource.customer_assetnew_resource=event.new_resource.customer_assetelifresource_type=="FEED":old_resource=event.old_resource.feednew_resource=event.new_resource.feedelifresource_type=="FEED_ITEM":old_resource=event.old_resource.feed_itemnew_resource=event.new_resource.feed_itemelse:print("Unknown change_resource_type: '{event.change_resource_type}'")# If the resource type is unrecognized then we continue to# the next row.continueprint(f"On{event.change_date_time}, user{event.user_email}"f"used interface{event.client_type.name}to perform a(n) "f"{event.resource_change_operation.name}operation on a "f"{event.change_resource_type.name}with resource name "f"'{event.change_resource_name}'")operation_type:str=event.resource_change_operation.nameifoperation_typein("UPDATE","CREATE"):forchanged_field_pathinevent.changed_fields.paths:changed_field:str=changed_field_path# Change field name from "type" to "type_" so that it doesn't# raise an exception when accessed on the protobuf object, see:# https://developers.google.com/google-ads/api/docs/client-libs/python/library-version-10#field_names_that_are_reserved_wordsifchanged_field=="type":changed_field="type_"new_value:Any=get_nested_attr(new_resource,changed_field)# If the field value is an Enum get the human readable name# so that it is printed instead of the field ID integer.ifisinstance(type(new_value),ProtoEnumMeta):new_value=new_value.nameifoperation_type=="CREATE":print(f"\t{changed_field}set to{new_value}")else:old_value:Any=get_nested_attr(old_resource,changed_field)# If the field value is an Enum get the human readable name# so that it is printed instead of the field ID integer.ifisinstance(type(old_value),ProtoEnumMeta):old_value=old_value.nameprint(f"\t{changed_field}changed from{old_value}to{new_value}")
defget_change_details(customer_id)# GoogleAdsClient will read a config file from# ENV['HOME']/google_ads_config.rb when called without parametersclient=Google::Ads::GoogleAds::GoogleAdsClient.new# Construct a query to find details for recent changes in your account.# The LIMIT clause is required for the change_event resource.# The maximum size is 10000, but a low limit was set here for demonstrative# purposes.# The WHERE clause on change_date_time is also required. It must specify a# window of at most 30 days within the past 30 days.query=<<~QUERYSELECTchange_event.resource_name,change_event.change_date_time,change_event.change_resource_name,change_event.user_email,change_event.client_type,change_event.change_resource_type,change_event.old_resource,change_event.new_resource,change_event.resource_change_operation,change_event.changed_fieldsFROMchange_eventWHEREchange_event.change_date_time<='#{(Date.today+1).to_s}'ANDchange_event.change_date_time>='#{(Date.today-14).to_s}'ORDERBYchange_event.change_date_timeDESCLIMIT5QUERY# Execute the query to fetch results from the API.response=client.service.google_ads.search(customer_id:customer_id,query:query,)# Process the results and output changes.response.eachdo|row|event=row.change_eventold_resource,new_resource=caseevent.change_resource_typewhen:AD[event.old_resource.ad,event.new_resource.ad]when:AD_GROUP[event.old_resource.ad_group,event.new_resource.ad_group]when:AD_GROUP_AD[event.old_resource.ad_group_ad,event.new_resource.ad_group_ad]when:AD_GROUP_ASSET[event.old_resource.ad_group_asset,event.new_resource.ad_group_asset]when:AD_GROUP_CRITERION[event.old_resource.ad_group_criterion,event.new_resource.ad_group_criterion]when:AD_GROUP_BID_MODIFIER[event.old_resource.ad_group_bid_modifier,event.new_resource.ad_group_bid_modifier]when:ASSET[event.old_resource.asset,event.new_resource.asset]when:ASSET_SET[event.old_resource.asset_set,event.new_resource.asset_set]when:ASSET_SET_ASSET[event.old_resource.asset_set_asset,event.new_resource.asset_set_asset]when:CAMPAIGN[event.old_resource.campaign,event.new_resource.campaign]when:CAMPAIGN_ASSET[event.old_resource.campaign_asset,event.new_resource.campaign_asset]when:CAMPAIGN_ASSET_SET[event.old_resource.campaign_asset_set,event.new_resource.campaign_asset_set]when:CAMPAIGN_BUDGET[event.old_resource.campaign_budget,event.new_resource.campaign_budget]when:CAMPAIGN_CRITERION[event.old_resource.campaign_criterion,event.new_resource.campaign_criterion]when:ASSET[event.old_resource.asset,event.new_resource.asset]when:CUSTOMER_ASSET[event.old_resource.customer_asset,event.new_resource.customer_asset]elseputs"Unknown change_resource_type#{event.change_resource_type}."nextendputs"On#{event.change_date_time}, user#{event.user_email}used interface "\"#{event.client_type}to perform a(n)#{event.resource_change_operation}"\"operation on a#{event.change_resource_type}with resource name "\"#{event.change_resource_name}."if[:UPDATE,:CREATE].include?event.resource_change_operationevent.changed_fields.paths.eachdo|changed_field|new_value=get_value_from_path(changed_field,new_resource)if:CREATE==event.resource_change_operationputs"\t#{changed_field}set to '#{new_value}'."elseold_value=get_value_from_path(changed_field,old_resource)puts"\t#{changed_field}changed from '#{old_value}' to '#{new_value}'."endendendendend# Given the string value of a path from the response, look up the value of the# field located at that path on the given object.defget_value_from_path(path,object)path.split(".").inject(object){|obj,key|obj.send(key)}end
subget_change_details{my($api_client,$customer_id)=@_;# Construct a query to find details for recent changes in your account.# The LIMIT clause is required for the change_event resource.# The maximum size is 10000, but a low limit was set here for demonstrative# purposes.# The WHERE clause on change_date_time is also required. It must specify a# window of at most 30 days within the past 30 days.my$search_query="SELECT change_event.resource_name, change_event.change_date_time, "."change_event.change_resource_name, change_event.user_email, "."change_event.client_type, change_event.change_resource_type, "."change_event.old_resource, change_event.new_resource, "."change_event.resource_change_operation, change_event.changed_fields "."FROM change_event "."WHERE change_event.change_date_time DURING LAST_14_DAYS "."ORDER BY change_event.change_date_time DESC LIMIT 5";# Create a search Google Ads request that will retrieve all change events using# pages of the specified page size.my$search_request=Google::Ads::GoogleAds::V21::Services::GoogleAdsService::SearchGoogleAdsRequest->new({customerId=>$customer_id,query=>$search_query});# Get the GoogleAdsService.my$google_ads_service=$api_client->GoogleAdsService();my$iterator=Google::Ads::GoogleAds::Utils::SearchGoogleAdsIterator->new({service=>$google_ads_service,request=>$search_request});# Iterate over all rows in all pages and print the requested field values for# the change event in each row.while($iterator->has_next){my$google_ads_row=$iterator->next;my$change_event=$google_ads_row->{changeEvent};printf"On %s, user %s used interface %s to perform a(n) %s operation "."on a %s with resource name '%s'.\n",$change_event->{changeDateTime},$change_event->{userEmail},$change_event->{clientType},$change_event->{resourceChangeOperation},$change_event->{changeResourceType},$change_event->{changeResourceName};if(grep/$change_event->{resourceChangeOperation}/,(CREATE,UPDATE)){my($old_resource,$new_resource)=_get_changed_resources_for_resource_type($change_event);foreachmy$changed_field(split/,/,$change_event->{changedFields}){my$new_value=_convert_to_string(get_field_value($new_resource,$changed_field))||"";if($change_event->{resourceChangeOperation}eqCREATE){print"\t$changed_field set to '$new_value'.\n";}else{my$old_value=_convert_to_string(get_field_value($old_resource,$changed_field))||"";print"\t$changed_field changed from '$old_value' to '$new_value'.\n";}}}}return1;}# This method converts the specified value to a string.sub_convert_to_string{my$value=shift;my$string_value="";if(ref($value)eq"ARRAY"){$string_value.="[";foreachmy$item(@$value){if(is_hash_ref($item)){$string_value.=(JSON::XS->new->utf8->encode($item).",");}else{$string_value.=($item.",");}}$string_value.="]";}elsif(is_hash_ref($value)){$string_value.=JSON::XS->new->utf8->encode($value);}else{$string_value=$value;}return$string_value;}# This method returns the old resource and new resource based on the change# resource type of a change event.sub_get_changed_resources_for_resource_type{my$change_event=shift;my$resource_type=$change_event->{changeResourceType};if($resource_typeeqAD){return$change_event->{oldResource}{ad},$change_event->{newResource}{ad};}elsif($resource_typeeqAD_GROUP){return$change_event->{oldResource}{adGroup},$change_event->{newResource}{adGroup};}elsif($resource_typeeqAD_GROUP_AD){return$change_event->{oldResource}{adGroupAd},$change_event->{newResource}{adGroupAd};}elsif($resource_typeeqAD_GROUP_ASSET){return$change_event->{oldResource}{adGroupAsset},$change_event->{newResource}{adGroupAsset};}elsif($resource_typeeqAD_GROUP_CRITERION){return$change_event->{oldResource}{adGroupCriterion},$change_event->{newResource}{adGroupCriterion};}elsif($resource_typeeqAD_GROUP_BID_MODIFIER){return$change_event->{oldResource}{adGroupBidModifier},$change_event->{newResource}{adGroupBidModifier};}elsif($resource_typeeqASSET){return$change_event->{oldResource}{asset},$change_event->{newResource}{asset};}elsif($resource_typeeqASSET_SET){return$change_event->{oldResource}{assetSet},$change_event->{newResource}{assetSet};}elsif($resource_typeeqASSET_SET_ASSET){return$change_event->{oldResource}{assetSetAsset},$change_event->{newResource}{assetSetAsset};}elsif($resource_typeeqCAMPAIGN){return$change_event->{oldResource}{campaign},$change_event->{newResource}{campaign};}elsif($resource_typeeqCAMPAIGN_ASSET){return$change_event->{oldResource}{campaignAsset},$change_event->{newResource}{campaignAsset};}elsif($resource_typeeqCAMPAIGN_ASSET_SET){return$change_event->{oldResource}{campaignAssetSet},$change_event->{newResource}{campaignAssetSet};}elsif($resource_typeeqCAMPAIGN_BUDGET){return$change_event->{oldResource}{campaignBudget},$change_event->{newResource}{campaignBudget};}elsif($resource_typeeqCAMPAIGN_CRITERION){return$change_event->{oldResource}{campaignCriterion},$change_event->{newResource}{campaignCriterion};}elsif($resource_typeeqCUSTOMER_ASSET){return$change_event->{oldResource}{customerAsset},$change_event->{newResource}{customerAsset};}else{print"Unknown change_resource_type $resource_type.\n";}}
A change event may not include every row from the Change History in the web
client. For a similar view to Change History, use thechange
statusresource instead.
The change event resource is intended to drill down into the specifics of an
individual change. You can retrieve data such as specific field values that
have changed, who made the change, and which client type was used to make the
change. However, not all changes to your account will have a corresponding
change event entry.
Some key client types that do record their changes for use with change events
include:
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2025-09-03 UTC."],[[["\u003cp\u003eThe Google Ads API's Change Event resource tracks changes (create, update) to your account, providing details like old/new values, timestamps, and user information.\u003c/p\u003e\n"],["\u003cp\u003eChange Events are queried using GAQL with a 30-day date filter and a 10,000-result limit; larger datasets require batched retrieval using timestamps.\u003c/p\u003e\n"],["\u003cp\u003eProvided code examples (Java, C#, PHP, Python) demonstrate how to query for Change Events and extract key details like resource type, changed fields, and user/client involved.\u003c/p\u003e\n"],["\u003cp\u003eChanges are reflected in Change Event results within 3 minutes and support various resource types listed under \u003ccode\u003eChangeEventResourceType\u003c/code\u003e.\u003c/p\u003e\n"],["\u003cp\u003eWhile Change Events offer detailed change tracking, they might not capture every change visible in the Google Ads web client's Change History, which may have additional business logic applied.\u003c/p\u003e\n"]]],[],null,[]]