Why am I encountering an error when making calls to Google Ads API after successful authentication of my account?

This topic was automatically generated from Slack. You can find the original thread here.

having trouble making calls to Google Ads API. The example step provided in your documentation successfully authenticates my google ads account. However, when I try to make a call, I get an error that the end-point URL (which uses my Google Ads ID) doesn’t exist). Can you please help me troubleshoot?

I’m sorry to hear you’re having trouble with the Google Ads API. To help you troubleshoot, I’ll need more information about the specific API call you’re trying to make and the error message you’re receiving. Can you please provide the code you’re using to make the API call and the exact error message? This will help me understand the issue better and provide you with the right guidance.

import { axios } from@pipedream/platform”;
import { google } from “googleapis”;

export default defineComponent({
props: {
google_ads: {
type: “app”,
app: “google_ads”,
},
},
async run({ steps, $ }) {
// Step 1: Existing authentication step
const userInfo = await axios($, {
url: https://www.googleapis.com/oauth2/v1/userinfo,
headers: {
Authorization: Bearer ${**this**.google_ads.$auth.oauth_access_token},
},
});

_// Step 2: Call Keyword Ideas API_
**const** customerId = "9998828401";
**const** languageId = "en"; _// E.g., en_
**const** locationIds = ["LOCATION_ID_1", "LOCATION_ID_2"]; _// E.g., ["2840", "2841"]_
**const** keywordTexts = ["KEYWORD_1", "KEYWORD_2"]; _// E.g., ["plumber", "drain cleaning"]_

**const** config = {
  method: "POST",
  url: `https://googleads.googleapis.com/v14/customers/${customerId}/keywordPlanIdea:generateKeywordIdeas`,
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${**this**.google_ads.$auth.oauth_access_token}`,
  },
  data: {
    customerId: customerId,
    language: `languageConstants/${languageId}`,
    geoTargetConstants: locationIds.map((id) => `geoTargetConstants/${id}`),
    includeAdultKeywords: **false**,
    keywordSeed: {
      keywords: keywordTexts,
    },
  },
};

**const** response = **await** axios($, config);
**return** {
  userInfo: userInfo.data,
  keywordIdeas: response.data,
};

},
});

This is my code. I am getting a 404 error saying that the URL doesn’t exist.

The requested URL /v14/customers/9998828401/keywordPlanIdea:generateKeywordIdeas was not found on this server. That’s all we know.\n"

How do I make an actual API call to Google Ads?

I apologize for the confusion. It seems there’s an issue with the API endpoint URL in your code. The Google Ads API has a different base URL than the Google APIs you might be familiar with. The correct base URL for Google Ads API is https://googleads.googleapis.com/. Please update the URL in your config object as follows: url: `https://googleads.googleapis.com/v8/customers/${customerId}/keywordPlanIdea:generateKeywordIdeas`, Also, note that I changed the API version from v14 to v8. The Google Ads API version v8 is the latest stable version at the time of writing this response. You can always check the Google Ads API release notes for the latest version and updates. Please try updating your code with these changes and let me know if you still face any issues.

that is incorrect. Google has been on V14 since late 2023. The issue is something else. Please help me figure it out

I apologize for the confusion earlier. Since you’re using Google Ads API version v14, let’s focus on the issue with the endpoint URL. Based on the error message you provided, it seems like the URL might not be correct. Please double-check the Google Ads API documentation to ensure you’re using the correct endpoint for the generateKeywordIdeas method. Make sure that the customerId value is correct and that you have the necessary permissions to access the endpoint. If you’re still facing issues, please provide any additional information or error messages you encounter, and I’ll do my best to help you troubleshoot the problem.

I have a developer token, but the example code in the PipeDream documentation doesn’t require it. How can I add it to my code to make sure it’s not a permissions issue?

I apologize for the oversight. To include your developer token in the API request, you can add it to the headers of your axios config. Update the headers section in your config object like this: headers: { "Content-Type": "application/json", Authorization: `Bearer ${this.google_ads.$auth.oauth_access_token}`, "developer-token": "YOUR_DEVELOPER_TOKEN", }, Replace YOUR_DEVELOPER_TOKEN with your actual developer token. This should include the developer token in your API requests and help you determine if it’s a permissions issue.

talking to Google Ads – it seems like I need to connect my Google Ads account to PipeDream and add a client ID, client secret, and refresh token. How can I go about doing that?

To connect your Google Ads account to Pipedream, you’ll need to create a custom app and use the OAuth flow to obtain the access token. Here’s a step-by-step guide on how to do that: 1. Create a new project in the Google Cloud Console. 2. Enable the Google Ads API for your project. 3. Create OAuth 2.0 credentials (Client ID and Client Secret) for your project. 4. Obtain the refresh token by following the OAuth 2.0 guide for Google APIs. Once you have the Client ID, Client Secret, and Refresh Token, you can create a Pipedream component to connect to the Google Ads API. Here’s an example component: import { axios } from "@pipedream/platform"; export default defineComponent({ props: { clientId: { type: "string", label: "Client ID", }, clientSecret: { type: "string", label: "Client Secret", }, refreshToken: { type: "string", label: "Refresh Token", }, }, async run({ $ }) { // Obtain an access token using the refresh token const tokenResponse = await axios($, { method: "POST", url: "https://oauth2.googleapis.com/token", data: { client_id: this.clientId, client_secret: this.clientSecret, refresh_token: this.refreshToken, grant_type: "refresh_token", }, }); const accessToken = tokenResponse.access_token; // Make an API request to Google Ads API using the access token const customerId = "YOUR_CUSTOMER_ID"; const response = await axios($, { method: "POST", url: `https://googleads.googleapis.com/v14/customers/${customerId}/keywordPlanIdea:generateKeywordIdeas`, headers: { "Content-Type": "application/json", Authorization: `Bearer ${accessToken}`, "developer-token": "YOUR_DEVELOPER_TOKEN", }, // Add the required data for the API request }); return response; }, }); Replace YOUR_CUSTOMER_ID and YOUR_DEVELOPER_TOKEN with your actual values. This component will use the refresh token to obtain an access token and make an API

I need to give Google Ads API an Authorized Javascript origin (The HTTP origins that host your web application. This value can’t contain wildcards or paths. If you use a port other than 80, you must specify it. For example: https://example.com:8080) and Authorized Redirect URI (Users will be redirected to this path after they have authenticated with Google. The path will be appended with the authorization code for access, and must have a protocol. It can’t contain URL fragments, relative paths, or wildcards, and can’t be a public IP address.).

Do I need to fill this in for API calls from Pipedream? and if so, what URIs should I use?

For API calls made within Pipedream, you don’t need to provide an Authorized JavaScript origin or Authorized Redirect URI, as Pipedream handles the OAuth flow internally. You’ll only need to provide your Client ID, Client Secret, and Refresh Token when creating a custom Pipedream component to connect to the Google Ads API. To obtain the Refresh Token, follow the OAuth 2.0 guide for Google APIs. Once you have the Client ID, Client Secret, and Refresh Token, you can create a Pipedream component to connect to the Google Ads API as mentioned in my previous response.

But the refresh token expires. How can I get Pipedream to generate a new one?

Refresh tokens in Google APIs typically do not expire unless they are revoked or the user’s credentials change. However, if you need to obtain a new refresh token, you can follow these steps: 1. Revoke the existing refresh token by visiting Google’s token revocation endpoint. 2. Re-initiate the OAuth 2.0 flow to obtain a new authorization code. 3. Exchange the new authorization code for a new access token and refresh token. In Pipedream, you can update the refresh token in your Google app configuration by following these steps: 1. Go to the Apps page in your Pipedream account. 2. Find the Google app and click on it. 3. Click on the “Edit” button next to the connected account. 4. Update the refresh token field with the new refresh token you obtained. Once you’ve updated the refresh token, your Pipedream component will use the new refresh token to obtain access tokens for future API requests.

do I need to enter the refresh token, or the access token for Google Ads? I have both now, but the refresh token is giving me a 404 error.

In Pipedream, you should use the refresh token, not the access token. The refresh token is used to obtain new access tokens when they expire. If you’re getting a 404 error, it might be related to the API endpoint you’re trying to access or the way you’re making the request. Here’s an example of a Pipedream component that connects to the Google API using the refresh token: ```
import { axios } from “@pipedream/platform”; export default defineComponent({ props: { google: { type: “app”, app: “google”, }, }, async run({ steps, $ }) { // Replace the URL and headers with the appropriate values for the Google Ads API return await axios($, { url: https://www.googleapis.com/oauth2/v1/userinfo, headers: { Authorization: Bearer ${this.google.$auth.oauth_access_token}, }, }); }, });

Below is how a Keyword Ideas API call would be made in Python outside of PipeDream.

Please help me structure an API call using PipeDream to get the same result:

def main(
    client, customer_id, location_ids, language_id, keyword_texts, page_url
):
    keyword_plan_idea_service = client.get_service("KeywordPlanIdeaService")
    keyword_competition_level_enum = (
        client.enums.KeywordPlanCompetitionLevelEnum
    )
    keyword_plan_network = (
        client.enums.KeywordPlanNetworkEnum.GOOGLE_SEARCH_AND_PARTNERS
    )
    location_rns = map_locations_ids_to_resource_names(client, location_ids)
    language_rn = client.get_service("GoogleAdsService").language_constant_path(
        language_id
    )

    # Either keywords or a page_url are required to generate keyword ideas
    # so this raises an error if neither are provided.
    if not (keyword_texts or page_url):
        raise ValueError(
            "At least one of keywords or page URL is required, "
            "but neither was specified."
        )

    # Only one of the fields "url_seed", "keyword_seed", or
    # "keyword_and_url_seed" can be set on the request, depending on whether
    # keywords, a page_url or both were passed to this function.
    request = client.get_type("GenerateKeywordIdeasRequest")
    request.customer_id = customer_id
    request.language = language_rn
    request.geo_target_constants = location_rns
    request.include_adult_keywords = False
    request.keyword_plan_network = keyword_plan_network

    # To generate keyword ideas with only a page_url and no keywords we need
    # to initialize a UrlSeed object with the page_url as the "url" field.
    if not keyword_texts and page_url:
        request.url_seed.url = page_url

    # To generate keyword ideas with only a list of keywords and no page_url
    # we need to initialize a KeywordSeed object and set the "keywords" field
    # to be a list of StringValue objects.
    if keyword_texts and not page_url:
        request.keyword_seed.keywords.extend(keyword_texts)

    # To generate keyword ideas using both a list of keywords and a page_url we
    # need to initialize a KeywordAndUrlSeed object, setting both the "url" and
    # "keywords" fields.
    if keyword_texts and page_url:
        request.keyword_and_url_seed.url = page_url
        request.keyword_and_url_seed.keywords.extend(keyword_texts)

    keyword_ideas = keyword_plan_idea_service.generate_keyword_ideas(
        request=request
    )

    for idea in keyword_ideas:
        competition_value = idea.keyword_idea_metrics.competition.name
        print(
            f'Keyword idea text "{idea.text}" has '
            f'"{idea.keyword_idea_metrics.avg_monthly_searches}" '
            f'average monthly searches and "{competition_value}" '
            "competition.\n"
    )generate_keyword_ideas.py

To make a similar API call using Pipedream, you’ll need to use the Google Ads API with your refresh token. Here’s a Pipedream component that you can use as a starting point: ```
import { axios } from “@pipedream/platform”; export default defineComponent({ props: { googleAds: { type: “app”, app: “google_ads”, }, customer_id: { type: “string”, label: “Customer ID”, }, location_ids: { type: “string[]”, label: “Location IDs”, }, language_id: { type: “string”, label: “Language ID”, }, keyword_texts: { type: “string[]”, label: “Keyword Texts”, }, page_url: { type: “string”, label: “Page URL”, optional: true, }, }, async run({ $ }) { // You’ll need to implement the necessary logic to create the request object // and make the API call to the Google Ads API using the axios instance. // Replace the URL, headers, and request body with the appropriate values // for the Google Ads API. const url = “https://googleads.googleapis.com/v10/customers/{customer_id}/keywordPlanIdeaService:generateKeywordIdeas”; const headers = { Authorization: Bearer ${this.googleAds.$auth.oauth_access_token}, “Content-Type”: “application/json”, }; const requestBody = { // Add the necessary fields to the request body based on the provided props }; try { const response = await axios($, { method: “POST”, url, headers, data: requestBody, }); // Process the response and return the desired data return response.data; } catch (error) { console.error(“Error making API call:”, error); throw error; } }, });

The Google Ads API doesn’t expose a RESTful endpoint for keyword ideas; instead, it uses gRPC, which is why the Python example uses the client libraries to make calls to the service. You can’t make a simple HTTP request to get keyword ideas.
However, you can use the Google Ads Node.js client library to make a call to the Keyword Ideas API in a Node.js environment like Pipedream.

My question to you – can I install the google ads api Node.js library on Pipedream? If not, how can I make the type of call I’m looking for?

Yes, you can use the Google Ads API Node.js client library in a Pipedream workflow. To do this, you’ll need to import the library in your code step. Here’s an example of how to import and use the library: ```
import { GoogleAdsApi } from “google-ads-api”; export default defineComponent({ props: { // Add necessary props for your use case, like credentials and input parameters }, async run({ $ }) { // Initialize the Google Ads API client with your credentials const client = new GoogleAdsApi({ client_id: “YOUR_CLIENT_ID”, client_secret: “YOUR_CLIENT_SECRET”, developer_token: “YOUR_DEVELOPER_TOKEN”, }); // Authenticate with your refresh token const credentials = client.getAccessToken(“YOUR_REFRESH_TOKEN”); // Create a new Google Ads service client const service = client.getService(“KeywordPlanIdeaService”, credentials); // Make the API call using the service client and process the response // Replace the request object with the appropriate values for your use case const request = { // Add the necessary fields to the request object based on the provided props }; try { const response = await service.generateKeywordIdeas(request); // Process the response and return the desired data return response; } catch (error) { console.error(“Error making API call:”, error); throw error; } }, });