Posts tagged with google-cloud-platform

I am looking for collect data from Google ADS API into GCP by using Python scripts and it requires to fill these items for authentication in the google-ads.yaml file:

developer_token: client_id: client_secret: refresh_token: login_customer_id: 

I was able to fill these items by asking people in my company or generating it with google python scripts in GitHub but I need to understand the role of each, the docs seems to be disperse with a long learning path.

I'm writing a code to load data from Google Ads api to BigQuery table by using Cloud Functions, the process query a table called ad_group_ad but i'm struggling when trying to validate if there's duplicated rows at my destination.

By reading the docs I was expecting to find some attribute used to identifier a column or a group of columns that represents the table key. May this question seem obviously but i ain't having progress when trying to google this.

Is there a way to identifies if there's is duplicated rows? I'm not using any group by instruction when collecting, just a simple select like the example below:

SELECT     segments.ad_network_type,     campaign.name,     ad_group.name,     ad_group.id,     so     on,     and,     so,     forth FROM ad_group_ad WHERE segments.date = ? 

We have a BigQuery data transfer of GoogleAds data. I came to realization, that I probably do not understand what is in the aggregates.

The discrepancy occurs with "Discovery" campaigns. Here is the query:

SELECT     '1. AdStats' as table,     SUM(Clicks)       AS clicks,     SUM(Cost)/1000000 AS cost,     SUM(Impressions)  AS impressions FROM `project.dataset.p_AdStats_*` WHERE Date = @date AND CampaignId = @campaign     UNION ALL SELECT     '2. AdBasicStats' as table,     SUM(Clicks)       AS clicks,     SUM(Cost)/1000000 AS cost,     SUM(Impressions)  AS impressions FROM `project.dataset.p_AdBasicStats_*` WHERE Date = @date AND CampaignId = @campaign     UNION ALL SELECT     '3. AdGroupStats' as table,     SUM(Clicks)       AS clicks,     SUM(Cost)/1000000 AS cost,     SUM(Impressions)  AS impressions FROM `project.dataset.p_AdGroupStats_*` WHERE Date = @date AND CampaignId = @campaign     UNION ALL SELECT     '4. AdGroupBasicStats' as table,     SUM(Clicks)       AS clicks,     SUM(Cost)/1000000 AS cost,     SUM(Impressions)  AS impressions FROM `project.dataset.p_AdGroupBasicStats_*` WHERE Date = @date AND CampaignId = @campaign ORDER BY 1 

The results come in:

table                clicks cost       impressions 1. AdStats           74     2.906935   15582 2. AdBasicStats      74     345.444868 15582 3. AdGroupStats      78     15.786935  15802 4. AdGroupBasicStats 78     358.324868 15802 

The GoogleAds UI shows 78 clicks, €358.32 and 15802 impressions for campaign report. It matches AdGroupBasicStats.

But I need more.. I need math to hold - AdBasicStats should have the same data, but in more granular detail (on Ad level). But it does not. Also, this is just killing me:

345.444868 + 15.786935 - 2.906935 = 358.324868 ?!?! 

What the actual f...? I've tested multiple days, different campaigns. The math is there. Can someone explain me what does those aggregates have? Point me to any resource describing the data in the tables, please.

Couple of notes:

  1. I suspect that data from AdStats is already in both AdBasicStats and AdGroupStats. Thus the minus in equation. But what does that data stand for? How can I identify such data in other aggregates?
  2. Backfill is not the culprit, I've refreshed data multiple times in historical periods.
  3. Interestingly enough, this behaviour only occurs with "Discovery" campaigns. Other types, e.g. "Display", seems not to have this discrepancy.
  4. I've tried to contact Google Ads support, but they directed me to the GCP support instead, which we don't have available for our account.

I'm trying to access Google Ads campaing reports from Python folowing this tutorial.

I've requested my Developer Token with Basic Access. I think it has enough privileges to execute the script. I Can see my token active when I go to "API Center" in google ads.

I've created a project in google cloud and an Oauth Token.

In google Cloud:

  • Created a new project
  • Activated the Google Ads API.
  • When I go to Manage-> Credentials I see that the Oauth token is compatible with that API.
  • I have successfully created a refresh token.

I'm using this script as proof of concept:

import os import json import sys from google.ads.google_ads.errors import GoogleAdsException # Put an account id to download stats from. Note: not MCC, no dash lines CUSTOMER_ID = "xxxxxxxxxx" def get_account_id(account_id, check_only=False):     """     Converts int to str, checks if str has dashes. Returns 10 chars str or raises error     :check_only - if True, returns None instead of Error     """     if isinstance(account_id, int) and len(str(account_id)) == 10:         return str(account_id)     if isinstance(account_id, str) and len(account_id.replace("-", "")) == 10:         return account_id.replace("-", "")     if check_only:         return None     raise ValueError(f"Couldn't recognize account id from {account_id}") def micros_to_currency(micros):     return micros / 1000000.0 def main(client, customer_id):          ga_service = client.get_service("GoogleAdsService")#     , version="v5")     query = """         SELECT           campaign.id,           campaign.name,           ad_group.id,           ad_group.name,           ad_group_criterion.criterion_id,           ad_group_criterion.keyword.text,           ad_group_criterion.keyword.match_type,           metrics.impressions,           metrics.clicks,           metrics.cost_micros         FROM keyword_view         WHERE           segments.date DURING LAST_7_DAYS           AND campaign.advertising_channel_type = 'SEARCH'           AND ad_group.status = 'ENABLED'           AND ad_group_criterion.status IN ('ENABLED', 'PAUSED')         ORDER BY metrics.impressions DESC         LIMIT 50"""     # Issues a search request using streaming.     response = ga_service.search_stream(customer_id, query) #THIS LINE GENERATES THE ERROR     keyword_match_type_enum = client.get_type(         "KeywordMatchTypeEnum"     ).KeywordMatchType     try:         for batch in response:             for row in batch.results:                 campaign = row.campaign                 ad_group = row.ad_group                 criterion = row.ad_group_criterion                 metrics = row.metrics                 keyword_match_type = keyword_match_type_enum.Name(                     criterion.keyword.match_type                 )                 print(                     f'Keyword text "{criterion.keyword.text}" with '                     f'match type "{keyword_match_type}" '                     f"and ID {criterion.criterion_id} in "                     f'ad group "{ad_group.name}" '                     f'with ID "{ad_group.id}" '                     f'in campaign "{campaign.name}" '                     f"with ID {campaign.id} "                     f"had {metrics.impressions} impression(s), "                     f"{metrics.clicks} click(s), and "                     f"{metrics.cost_micros} cost (in micros) during "                     "the last 7 days."                 )     except GoogleAdsException as ex:         print(             f'Request with ID "{ex.request_id}" failed with status '             f'"{ex.error.code().name}" and includes the following errors:'         )         for error in ex.failure.errors:             print(f'\tError with message "{error.message}".')             if error.location:                 for field_path_element in error.location.field_path_elements:                     print(f"\t\tOn field: {field_path_element.field_name}")         sys.exit(1) if __name__ == "__main__":     # credentials dictonary     creds = {"google_ads": "googleads.yaml"}     if not os.path.isfile(creds["google_ads"]):         raise FileExistsError("File googleads.yaml doesn't exists. ")     resources = {"config": "config.json"}     # This logging allows to see additional information on debugging     import logging     logging.basicConfig(level=logging.INFO, format='[%(asctime)s - %(levelname)s] %(message).5000s')     logging.getLogger('google.ads.google_ads.client').setLevel(logging.DEBUG)     # Initialize the google_ads client     from google.ads.google_ads.client import GoogleAdsClient     gads_client = GoogleAdsClient.load_from_storage(creds["google_ads"])     id_to_load = get_account_id(CUSTOMER_ID)      main(gads_client, id_to_load) 
  • I've changed CUSTOMER_ID to the account number that appears on the upper left corner
  • I've created a googleads.yaml and I've loaded the aforementioned information.

When I execute the script I get this error:

Traceback (most recent call last):  File "download_keywords_from_account.py", line 138, in <module>    main(gads_client, id_to_load)  File "download_keywords_from_account.py", line 70, in main    response = ga_service.search_stream(customer_id, query)  File "google/ads/google_ads/v6/services/google_ads_service_client.py", line 366, in search_stream    return self._inner_api_calls['search_stream'](request, retry=retry, timeout=timeout, metadata=metadata)  File google/api_core/gapic_v1/method.py", line 145, in __call__    return wrapped_func(*args, **kwargs)  File "google/api_core/retry.py", line 281, in retry_wrapped_func    return retry_target(  File "google/api_core/retry.py", line 184, in retry_target    return target()  File "google/api_core/timeout.py", line 214, in func_with_timeout    return func(*args, **kwargs)  File "google/api_core/grpc_helpers.py", line 152, in error_remapped_callable    six.raise_from(exceptions.from_grpc_error(exc), exc)  File "<string>", line 3, in raise_from google.api_core.exceptions.PermissionDenied: 403 Request had insufficient authentication scopes

The googleads.yaml file looks like this:

  #############################################################################   # Required Fields                                                           #   #############################################################################   developer_token: {developer token as seen in google ads -> tools -> api center}   #############################################################################   # Optional Fields                                                           #   #############################################################################   login_customer_id: {Id from the top left corner in google ads, only numbers}   # user_agent: INSERT_USER_AGENT_HERE   # partial_failure: True   validate_only: False   #############################################################################   # OAuth2 Configuration                                                      #   # Below you may provide credentials for either the installed application or #   # service account flows. Remove or comment the lines for the flow you're    #   # not using.                                                                #   #############################################################################   # The following values configure the client for the installed application   # flow.   client_id: {Oauth client id taken from gcloud -> api -> credentials} ends with apps.googleusercontent.com   client_secret: {got it while generating the token}   refresh_token:  1//0hr.... made with generate_refresh_token.py    # The following values configure the client for the service account flow.   path_to_private_key_file: ads.json   # delegated_account: INSERT_DOMAIN_WIDE_DELEGATION_ACCOUNT   #############################################################################   # ReportDownloader Headers                                                  #   # Below you may specify boolean values for optional headers that will be    #   # applied to all requests made by the ReportDownloader utility by default.  #   #############################################################################   # report_downloader_headers:     # skip_report_header: False     # skip_column_header: False     # skip_report_summary: False     # use_raw_enum_values: False 

NOTES:

The file ads.json contains the private key downloaded from the credentials page in gcloud.

I've seen some posts on this issue but none of them are Python + GoogleADs and I couldn't find a solution there either.

I have also tried other Python + GoogleAds examples getting the same error. This makes me think that I must be configuring something wrong in gcloud / google ads. But I don't understand what.

Please help me make the query I'm really stuck.

Thanks a lot!

I'd like to set billing information for a Test Account, but I can't set it using the Google Ads library or even the Google Ads UI.

The message error is:

Your account's access level doesn't include billing information. If you need access to billing, speak with your account admin. 

I tried to search about it and found this google groups conversation, but I couldn't find what I'm looking for.

Any idea how to tackle this?