Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to get this initially setup and working? #10

Closed
ericscottmarquez opened this issue Feb 25, 2019 · 13 comments
Closed

How to get this initially setup and working? #10

ericscottmarquez opened this issue Feb 25, 2019 · 13 comments
Labels
question ❔ Further information is requested

Comments

@ericscottmarquez
Copy link

ericscottmarquez commented Feb 25, 2019

I already have all my oauth credentials ready, I may be a complete brainlet because I can't figure out how to get a successful request/response through...

This repo only shows basic use case but no real setup and implementation example...

Is there a NodeJS example floating around somewhere that I can look at? Can I "gift" someone fifty bucks for an hour of their time to help me get a request through?

This is a seemingly esoteric subject as there are no guides online, and the official google docs are much less than helpful for a beginner, there's really quite sparse information over there.

@avermeil
Copy link
Member

Do you already have the 6 init/auth variables as described in the Authentication section of our Readme? In particular, do you have a refresh token?

If you don't have a refresh token, you'll need to get one using your OAuth credentials, which at this time is outside the scope of this library.

If you already have everything, then the example on the Readme should just run as-is. What errors are you getting?

@ericscottmarquez
Copy link
Author

I really just don't know how to run the functions, I have all the credentials from google, but in practice, I'm trying to get my data from a back end node server to a front-end react app, which I've already set up. I've been using the:

exports.resubscribe = function(req, res, next) { }

style to receive post requests from my front end. I have no idea how this library works or how to log output or errors from it or anything. Any help would be massively appreciated. Thanks for making this library by the way.

@jafaircl
Copy link
Contributor

jafaircl commented Feb 26, 2019

Assuming you're using an express server and React hooks, something like this should work:

// in createCampaign.ts
const GoogleAdsApi = require('google-ads-api');

// initialize the library
const google_ads_js = new GoogleAdsApi({
  client_id:
    '2345234523-0r123fwfanq6umje125rewc2134n.apps.googleusercontent.com',
  client_secret: '8TLfdsSAJ3rhEK8LbUvSAO',
  developer_token: 'EBI_T2FAGKE3ABCEF'
});

exports.createCampaign = async (req, res, next) => {
  // get data from request
  const { account_id, payload } = JSON.parse(req.body);
  // create your Customer
  const customer = google_ads_js.Customer({
    customer_account_id: account_id,
    manager_cid: '456-456-456',
    refresh_token: '1/fSBD8aGFjQkb0FlCYO5ASNDSJ27-crNoGFdhKQk'
  });
  // create a budget (required for creating a campaign)
  const response = await customer.campaignBudgets.create({
    amount_micros: payload.budget.amount_micros,
    explicitly_shared: false
  });
  // remove payload budget so creating a campaign will work
  delete payload.budget;
  try {
    // create new campaign
    const new_campaign = await customer.campaigns.create({
      budget_id: response.id,
      ...payload
    });
    // send the response. it'll be something like { id: ..., resource_name: ... }
    res.send(new_campaign);
  } catch (e) {
    // send error if there's an error
    res.error(e);
  }
};

/// in your express app
const createCampaign = require('./createCampaign');

app.post('/createCampaign', createCampaign);

// in your React component
import React, { useState, useEffect } from 'react';

const YourComponent = () => {
  const [data, setData] = useState({ campaign: {} });
  const fetchData = async requestPayload => {
    const response = await fetch(`${YOUR_SERVER_URL}/createCampaign`, {
      method: 'post',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(requestPayload)
    });
    setData({ campaign: response.json() });
  };
  useEffect(() => {
    const requestPayload = {
      account_id: '123-45-6789',
      payload: {
        budget: { amount_micros: 1200000 },
        name: 'Test Campaign',
        advertising_channel_type: 'DISPLAY',
        target_spend: {
          cpc_bid_ceiling_micros: 1000000
        }
      }
    };
    fetchData(requestPayload);
  }, []);
  return (
    <pre>
      <code>{JSON.stringify(data)}</code>
    </pre>
  );
};

@ericscottmarquez
Copy link
Author

ericscottmarquez commented Feb 27, 2019

Tremendous response. I wasn't expecting that. Hopefully this thread will help other beginners who are stumbling along this process... It's really helping out.
So far, here's where I am:
I implemented the above from @jafaircl (thanks!) - but now I'm getting this error coming from saying I need to add the credentials, but they are in the right place the code as suggested above... Has anyone else come across this error?

errors

This may be just a shot in the dark but am I using the wrong credentials type?
I know we need:

  • client_id
  • client_secret
  • developer_token (google ads)
  • manager_cid (ads manager account)
  • account_id
  • refresh_token

There are a few different types of credentials available, which is confusing to me. For WEB application type credentials, the OAUTH flow in google's directions describes a process using one of the libraries that you clone on to your system, I used the python library to retrieve it, and could only get a oauth REFRESH token from the OTHER type of oauth token credential in api console, which is used in my error-ed out example. What type of credentials are you guys using with this?

@mateuszjanusz mateuszjanusz added the question ❔ Further information is requested label Feb 27, 2019
@mateuszjanusz
Copy link
Contributor

mateuszjanusz commented Feb 28, 2019

Hi @ericscottmarquez,

This library expects all credentials described in Google Ads API docs in Config parameters section.

You need to pass developer token, client customer ID and client secret as parameters in the Google Ads Api constructor.

In order to make API calls you need to pass Google Ads customer account credentials in the Customer constructor. These are customer account ID, refresh token and customer ID of the manager account (manager CID).

Like in @jafaircl example:

const google_ads_js = new GoogleAdsApi({
  client_id:'2345234523-0r123fwfanq6umje125rewc2134n.apps.googleusercontent.com',
  client_secret: '8TLfdsSAJ3rhEK8LbUvSAO',
  developer_token: 'EBI_T2FAGKE3ABCEF'
})

const customer = google_ads_js.Customer({
  customer_account_id: '123-123-123',
  manager_cid: '456-456-456',
  refresh_token: '1/fSBD8aGFjQkb0FlCYO5ASNDSJ27-crNoGFdhKQk'
})

Hope this helps. If you have any other questions regarding this library, feel free to ask.

@ericscottmarquez
Copy link
Author

ericscottmarquez commented Mar 1, 2019

Thanks for the help guys.
N00b mistake, I got past that last error by installing the package globally (sudo npm install -g (etc...)), now it is actually talking to google through the library.

AND NOW for this evening's entertainment:

Error: It is asking for an OAUTH2 Access Token, which is not part of what is described here.
'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.',
error7

I hope you guys don't think I'm looking for a handout here. I really want to learn this, and I need it for my startup, so I'm going to be posting my progress for others who will be using this in the future. Maybe It will help them get set up and running a little bit faster.

@jafaircl
Copy link
Contributor

jafaircl commented Mar 1, 2019

You mentioned that you were using the web application flow. If you’re using the library on the server you may need the installed application flow instead.

Try folllowing the instructions in the python library here to generate the correct credentials. You’ll need to clone the library and follow those instructions.

@ericscottmarquez
Copy link
Author

Hey guys, here to bug ya again with news on the process (incredible that a neanderthal learned how to code right?)

The Google Api Console is seeing the requests coming through, but there is an error still being returned:

::1 - - [04/Mar/2019:23:25:57 +0000] "OPTIONS /api/google/createcampaign HTTP/1.1" 204 0 "http://localhost:3000/feature" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36"
{ Error: The caller does not have permission Signup not complete.
    at Http.<anonymous> (/home/eric/Desktop/CURRENT/server-(stripe)/node_modules/google-ads-api/lib/Http.js:261:66)
    at Generator.next (<anonymous>)
    at fulfilled (/home/eric/Desktop/CURRENT/server-(stripe)/node_modules/google-ads-api/lib/Http.js:4:58)
  error_name: 'The caller does not have permission Signup not complete.',
  details:
   [ { '@type':
        'type.googleapis.com/google.ads.googleads.v0.errors.GoogleAdsFailure',
       errors: [Array] } ],
  status: 'PERMISSION_DENIED',
  http_code: 403,
  trigger: undefined,
  location: undefined }

Hopefully this might be because I have the account in test mode still with Google Ads, I have not been approved for basic permissions access yet. Correct me if I'm wrong, but I thought Google Ads Api would be able to accept these requests but just with limited bandwidth?? Am I missing something here?

@ericscottmarquez
Copy link
Author

Got the API key approved by google, now permission denied error is gone. Tried the code example above and am getting this:

{ Error: Request contains an invalid argument. Resource was not found.
    at Http.<anonymous> (/home/eric/Desktop/c/server-(stripe)/node_modules/google-ads-api/lib/Http.js:261:66)
    at Generator.next (<anonymous>)
    at fulfilled (/home/eric/Desktop/c/server-(stripe)/node_modules/google-ads-api/lib/Http.js:4:58)
  error_name:
   'Request contains an invalid argument. Resource was not found.',
  details:
   [ { '@type':
        'type.googleapis.com/google.ads.googleads.v0.errors.GoogleAdsFailure',
       errors: [Array] } ],
  status: 'INVALID_ARGUMENT',
  http_code: 400,
  trigger: 'AdGroupId: 123123123',
  location:
   [ { fieldName: 'operations', index: '0' },
     { fieldName: 'create' },
     { fieldName: 'ad_group' } ] }


Have tried every other code snippet from the library and it is returning this error still.
Still, have not made a successful API call.

@jafaircl
Copy link
Contributor

You’re referencing an ad group that doesn’t exist. You need to find the ID of an actual ad group in that account in the UI

@ericscottmarquez
Copy link
Author

ericscottmarquez commented Mar 21, 2019

Okay So I got it working, the main problem was my adwords account not being on basic access, as per the PERMISSION DENIED error.

With the above error where @jafaircl who was pretty much right when he pointed out there was an incorrect ad group, the code had a correct ad group in it, but there just must have been an error.

const GoogleAdsApi = require('google-ads-api');

// initialize the library
  const google_ads_js = new GoogleAdsApi({
    client_id: 
    'blahblahblahblahblahblahblahjbhalbalhablhablaj.apps.googleusercontent.com',
    client_secret: 'YEET',
    developer_token: 'YEET',
  });

  const customer = google_ads_js.Customer({
    customer_account_id: 'skeet',
    manager_cid: 'skeet-skeet',
    refresh_token: '1/fjdksal;jfkdls;ajfklds;jafklds;jafklds;ajffkldsjaklf;s'
  });

exports.create_Campaign = async (req, res, next) => {
  try {
/*  const ad_group_name = req.body.ad_group_name;

  await customer.adgroups.create({
          name: ad_group_name,
          campaign_id: XXXXXXXXXX
  });

  await customer.keywords.create({
    ad_group_id: XXXXXXXXXXX,
    keyword: {
        text: ad_group_name,
        match_type: 'BROAD'
    }
  });
*/

  const ad = await customer.keywords.retrieve('XXXXXXXXXX_YYYYYYYY');
  console.log(ad);

  } catch (e) {
    console.error(JSON.stringify(e));
    res.send({
    error: JSON.stringify(e),
    });
  }

};

Now, what I'm trying to do is pull search amounts and ranking / cost information for keywords. Can I do this with the Google Ads Query Language feature? Is this possible with this library? The response for this code is this:

{ resource_name: 'customers/XXXXXXXXXX/adGroupCriteria/XXXXXXXXXX_YYYYYYYY',
  status: 'ENABLED',
  ad_group: 'customers/XXXXXXXXXX/adGroups/XXXXXXXXXX',
  effective_cpc_bid_micros: '10000',
  effective_cpm_bid_micros: '10000',
  effective_cpc_bid_source: 'ADGROUP',
  effective_cpm_bid_source: 'ADGROUP',
  type: 'KEYWORD',
  criterion_id: '10488771',
  keyword: { text: 'marketing tools', match_type: 'BROAD' },
  negative: false }

--where it does not return the below quality values from the keyword docs (very important) - did they change the api?

{
    quality_info: {
        quality_score: 6,
        creative_quality_score: 'AVERAGE',
        post_click_quality_score: 'AVERAGE',
        search_predicted_ctr: 'AVERAGE' 
    },

@avermeil
Copy link
Member

Try using the report or query methods, which give you the most control over which fields you want to select:

    const keywords_1 = await customer.query(
        `select 
            ad_group_criterion.criterion_id, 
            ad_group_criterion.quality_info.quality_score,
            metrics.cost_micros
        from keyword_view
        where segments.date during LAST_30_DAYS`
    )

    console.log(keywords_1)

// OR

    const keywords_2 = await customer.report({
        entity: 'keyword_view',
        attributes: [
            'ad_group_criterion.criterion_id',
            'ad_group_criterion.quality_info.quality_score',
        ],
        metrics: ['cost_micros'],
        constraints : [],
        date_constant: 'LAST_30_DAYS',
    })

    console.log(keywords_2)

If you only want one keyword, just add it as a constraint.

We're going to be working on some better documentation for everything, so stay tuned!

@avermeil
Copy link
Member

@ericscottmarquez , we've updated the documentation, which should be clearer. I'm going to close this issue for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question ❔ Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants