{"id":22096270,"url":"https://github.com/opteo/google-ads-api","last_synced_at":"2025-05-14T17:10:01.936Z","repository":{"id":37450954,"uuid":"144563888","full_name":"Opteo/google-ads-api","owner":"Opteo","description":"Google Ads API client library for Node.js","archived":false,"fork":false,"pushed_at":"2025-04-29T10:51:30.000Z","size":6136,"stargazers_count":292,"open_issues_count":46,"forks_count":99,"subscribers_count":23,"default_branch":"master","last_synced_at":"2025-05-14T17:09:18.633Z","etag":null,"topics":["adwords","adwords-api","google-ads","nodejs","typescript"],"latest_commit_sha":null,"homepage":"https://opteo.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Opteo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-08-13T10:22:19.000Z","updated_at":"2025-05-14T06:37:13.000Z","dependencies_parsed_at":"2024-03-03T22:21:35.316Z","dependency_job_id":"01c57d3e-3820-4eb9-9c14-eccfae05a509","html_url":"https://github.com/Opteo/google-ads-api","commit_stats":{"total_commits":675,"total_committers":21,"mean_commits":"32.142857142857146","dds":0.6577777777777778,"last_synced_commit":"7363dec06387c7a5030eb72e33707c63fa9e34fa"},"previous_names":[],"tags_count":50,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Opteo%2Fgoogle-ads-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Opteo%2Fgoogle-ads-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Opteo%2Fgoogle-ads-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Opteo%2Fgoogle-ads-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Opteo","download_url":"https://codeload.github.com/Opteo/google-ads-api/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254190394,"owners_count":22029632,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["adwords","adwords-api","google-ads","nodejs","typescript"],"created_at":"2024-12-01T04:10:00.323Z","updated_at":"2025-05-14T17:10:01.912Z","avatar_url":"https://github.com/Opteo.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\" style=\"font-size:32px\"\u003e\n    Google Ads API\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  Unofficial Google Ads API client library for Node.js\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://developers.google.com/google-ads/api/docs/release-notes\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/google%20ads-v17.1.0-009688.svg?style=flat-square\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/google-ads-api\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/google-ads-api.svg?style=flat-square\"\u003e\n  \u003c/a\u003e\n  \u003ca\u003e\n    \u003cimg src=\"https://img.shields.io/npm/dm/google-ads-api.svg?style=flat-square\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://opteo.com\"\u003e\n    \u003cimg src=\"https://app.opteo.com/favicon.png\" width=\"90\" height=\"90\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n# Features\n\n- Simple and easy to use API\n- Uses [gRPC](https://grpc.io/) and [Protocol Buffers](https://developers.google.com/protocol-buffers/) internally (recommended by Google)\n- Typescript definitions for all resources, enums, errors and services\n- Provides all API functionality\n\n# Installation\n\n```bash\nnpm install google-ads-api\n```\n\n# Usage\n\n- Setup\n  - [Create a client](#create-a-client)\n  - [Create a customer instance](#create-a-customer-instance)\n  - [List accessible customers](#list-accessible-customers)\n- Reporting\n  - [Retrieve Campaigns with metrics](#retrieve-campaigns-with-metrics)\n  - [Retrieve Campaigns using GAQL](#retrieve-campaigns-using-gaql)\n  - [Retrieve Ad Group metrics by date](#retrieve-ad-group-metrics-by-date)\n  - [Retrieve Keywords with an async iterator](#retrieve-keywords-with-an-async-iterator)\n  - [Retrieve Keywords with a raw stream](#retrieve-keywords-with-a-raw-stream)\n  - [Summary Row](#summary-row)\n  - [Total Results Count](#total-results-count)\n- Mutations\n  - [Create an expanded text ad](#create-an-expanded-text-ad)\n  - [Create a campaign \u0026 budget atomically](#create-a-campaign--budget-atomically)\n- Misc\n  - [Resource Names](#resource-names)\n  - [Hooks](#hooks)\n  - [Error handling](#error-handling)\n\n## Create a client\n\n```ts\nimport { GoogleAdsApi } from \"google-ads-api\";\n\nconst client = new GoogleAdsApi({\n  client_id: \"\u003cCLIENT-ID\u003e\",\n  client_secret: \"\u003cCLIENT-SECRET\u003e\",\n  developer_token: \"\u003cDEVELOPER-TOKEN\u003e\",\n});\n```\n\n---\n\n## Create a customer instance\n\n```ts\nconst customer = client.Customer({\n  customer_id: \"1234567890\",\n  refresh_token: \"\u003cREFRESH-TOKEN\u003e\",\n});\n\n// Also supports login \u0026 linked customer ids\nconst customer = client.Customer({\n  customer_id: \"1234567890\",\n  login_customer_id: \"\u003cLOGIN-CUSTOMER-ID\u003e\",\n  linked_customer_id: \"\u003cLINKED-CUSTOMER-ID\u003e\",\n  refresh_token: \"\u003cREFRESH-TOKEN\u003e\",\n});\n```\n\n---\n\n## List accessible customers\n\nThis is a special client method for listing the accessible customers for a given refresh token, and is equivalent to [CustomerService.listAccessibleCustomers](https://developers.google.com/google-ads/api/reference/rpc/v13/CustomerService#listaccessiblecustomers). It returns the resource names of available customer accounts.\n\n```ts\nconst client = new GoogleAdsApi({\n  client_id: \"\u003cCLIENT-ID\u003e\",\n  client_secret: \"\u003cCLIENT-SECRET\u003e\",\n  developer_token: \"\u003cDEVELOPER-TOKEN\u003e\",\n});\n\nconst refreshToken = \"\u003cREFRESH-TOKEN\u003e\";\n\nconst customers = await client.listAccessibleCustomers(refreshToken);\n```\n\n---\n\n## Retrieve Campaigns with metrics\n\n```ts\nimport { enums } from \"google-ads-api\";\n\nconst campaigns = await customer.report({\n  entity: \"campaign\",\n  attributes: [\n    \"campaign.id\",\n    \"campaign.name\",\n    \"campaign.bidding_strategy_type\",\n    \"campaign_budget.amount_micros\",\n  ],\n  metrics: [\n    \"metrics.cost_micros\",\n    \"metrics.clicks\",\n    \"metrics.impressions\",\n    \"metrics.all_conversions\",\n  ],\n  constraints: {\n    \"campaign.status\": enums.CampaignStatus.ENABLED,\n  },\n  limit: 20,\n});\n```\n\n---\n\n## Retrieve Campaigns using GAQL\n\nIf you prefer to use the [Google Ads Query Language](https://developers.google.com/google-ads/api/docs/query/overview) (GAQL) the `query` method is available. Internally `report` uses this function. [More GAQL examples can be found here](https://developers.google.com/google-ads/api/docs/query/cookbook).\n\n```ts\nconst campaigns = await customer.query(`\n  SELECT \n    campaign.id, \n    campaign.name,\n    campaign.bidding_strategy_type,\n    campaign_budget.amount_micros,\n    metrics.cost_micros,\n    metrics.clicks,\n    metrics.impressions,\n    metrics.all_conversions\n  FROM \n    campaign\n  WHERE\n    campaign.status = \"ENABLED\"\n  LIMIT 20\n`);\n```\n\n---\n\n## Retrieve Ad Group metrics by date\n\n```ts\nimport { enums } from \"google-ads-api\";\n\nconst campaigns = await customer.report({\n  entity: \"ad_group\",\n  metrics: [\n    \"metrics.cost_micros\",\n    \"metrics.clicks\",\n    \"metrics.impressions\",\n    \"metrics.all_conversions\",\n  ],\n  segments: [\"segments.date\"],\n  from_date: \"2021-01-01\",\n  to_date: \"2021-02-01\",\n});\n```\n\n---\n\n## Retrieve Keywords with an async iterator\n\nCalls searchStream internally but returns the rows one by one in an async iterator.\n\n\u003c!-- prettier-ignore-start --\u003e\n```ts\nimport { enums } from \"google-ads-api\";\n\nconst stream = customer.reportStream({\n  entity: \"ad_group_criterion\",\n  attributes: [\n    \"ad_group_criterion.keyword.text\", \n    \"ad_group_criterion.status\",\n  ],\n  constraints: {\n    \"ad_group_criterion.type\": enums.CriterionType.KEYWORD,\n  },\n});\n\n// Rows are streamed in one by one\nfor await (const row of stream) {\n    // Break the loop to stop streaming\n    if (someLogic) {\n        break\n    }\n}\n```\n\u003c!-- prettier-ignore-end --\u003e\n\nOr use a GAQL query.\n\n\u003c!-- prettier-ignore-start --\u003e\n```ts\nconst stream = customer.queryStream(`\n  SELECT\n    ad_group_criterion.keyword.text,\n    ad_group_criterion.status\n  FROM\n    ad_group_criterion\n  WHERE\n    ad_group_criterion.type = \"KEYWORD\"\n`);\n\n// Rows are streamed in one by one\nfor await (const row of stream) {\n    // Break the loop to stop streaming\n    if (someLogic) {\n        break\n    }\n}\n```\n\u003c!-- prettier-ignore-end --\u003e\n\n---\n\n## Retrieve Keywords with a raw stream\n\nReturns the raw stream so that events can be handled manually. For more information on Google's stream methods please [consult their docs](https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#server-streaming).\n\n\u003c!-- prettier-ignore-start --\u003e\n\n```ts\nimport { enums, parse } from \"google-ads-api\";\n\nconst reportOptions = {\n  entity: \"ad_group_criterion\",\n  attributes: [\n    \"ad_group_criterion.keyword.text\", \n    \"ad_group_criterion.status\",\n  ],\n  constraints: {\n    \"ad_group_criterion.type\": enums.CriterionType.KEYWORD,\n  },\n};\n\nconst stream = customer.reportStreamRaw(reportOptions);\n\n// Rows are streamed in 10,000 row chunks\nstream.on(\"data\", (chunk) =\u003e {\n  const parsedResults = parse({\n    results: chunk.results,\n    reportOptions,\n  });\n});\n\nstream.on(\"error\", (error) =\u003e {\n  throw new Error(error);\n});\n\nstream.on(\"end\", () =\u003e {\n  console.log(\"stream has finished\");\n});\n```\n\u003c!-- prettier-ignore-end --\u003e\n\n---\n\n## Create an expanded text ad\n\n```ts\nimport { resources, enums, ResourceNames } from \"google-ads-api\";\n\nconst ad = new resources.Ad({\n  expanded_text_ad: {\n    headline_part1: \"Cruise to Mars\",\n    headline_part2: \"Best Space Cruise Line\",\n    description: \"Buy your tickets now!\",\n    path1: \"cruises\",\n    path2: \"mars\",\n  },\n  final_urls: [\"https://example.com\"],\n  type: enums.AdType.EXPANDED_TEXT_AD,\n});\n\nconst adGroup = ResourceNames.adGroup(cus.credentials.customerId, \"123\");\n\nconst adGroupAd = new resources.AdGroupAd({\n  status: enums.AdGroupAdStatus.PAUSED,\n  ad_group,\n  ad,\n});\n\n// Returns an array of newly created resource names if successful\nconst { results } = await cus.adGroupAds.create([adGroupAd]);\n```\n\n---\n\n## Create a Campaign \u0026 Budget atomically\n\n```ts\nimport {\n  resources,\n  enums,\n  toMicros,\n  ResourceNames,\n  MutateOperation,\n} from \"google-ads-api\";\n\n// Create a resource name with a temporary resource id (-1)\nconst budgetResourceName = ResourceNames.campaignBudget(\n  customer.credentials.customer_id,\n  \"-1\"\n);\n\nconst operations: MutateOperation\u003c\n  resources.ICampaignBudget | resources.ICampaign\n\u003e[] = [\n  {\n    entity: \"campaign_budget\",\n    operation: \"create\",\n    resource: {\n      // Create a budget with the temporary resource id\n      resource_name: budgetResourceName,\n      name: \"Planet Express Budget\",\n      delivery_method: enums.BudgetDeliveryMethod.STANDARD,\n      amount_micros: toMicros(500),\n    },\n  },\n  {\n    entity: \"campaign\",\n    operation: \"create\",\n    resource: {\n      name: \"Planet Express\",\n      advertising_channel_type: enums.AdvertisingChannelType.SEARCH,\n      status: enums.CampaignStatus.PAUSED,\n      manual_cpc: {\n        enhanced_cpc_enabled: false,\n      },\n      // Use the temporary resource id which will be created in the previous operation\n      campaign_budget: budgetResourceName,\n      network_settings: {\n        target_google_search: true,\n        target_search_network: true,\n      },\n    },\n  },\n];\n\nconst result = await customer.mutateResources(operations);\n```\n\n---\n\n## Add Policy Exemption Rules\n\n```ts\nimport {\n  resources,\n  enums,\n  toMicros,\n  ResourceNames,\n  MutateOperation,\n} from \"google-ads-api\";\n\n// Ad Group to which you want to add the keyword\nconst adGroupResourceName = \"customers/123/adGroups/456\";\n\nconst keyword = \"24 hour locksmith harlem\";\n\nconst operations: MutateOperation\u003c\n  resources.IAdGroupCriterion \u0026 {\n    exempt_policy_violation_keys?: google.ads.googleads.v17.common.IPolicyViolationKey[];\n  }\n\u003e[] = [\n  {\n    entity: \"ad_group_criterion\",\n    operation: \"create\",\n    resource: {\n      // Keyword with policy violation exemptions\n      ad_group: adGroupResourceName,\n      keyword: {\n        text: \"24 hour locksmith harlem\",\n        match_type: enums.KeywordMatchType.PHRASE,\n      },\n      status: enums.AdGroupStatus.ENABLED,\n    },\n    exempt_policy_violation_keys: [\n      {\n        policy_name: \"LOCAL_SERVICES\",\n        violating_text: keyword,\n      },\n    ],\n  },\n];\n\nconst result = await customer.mutateResources(operations);\n```\n\n---\n\n## Uploading Click Conversions\n\n```ts\nconst clickConversion = {\n  gclid: \"\u003cGOOGLE-CLICK-ID\u003e\",\n  conversion_action: \"customers/1234567890/conversionActions/111222333\",\n  conversion_date_time: \"2022-01-11 00:00:00\",\n  conversion_value: 123,\n  currency_code: \"GBP\",\n};\n\nconst request = new services.UploadClickConversionsRequest({\n  customer_id: customerId,\n  conversions: [clickConversion],\n});\n\nawait customer.conversionUploads.uploadClickConversions(request);\n```\n\n---\n\n## Summary Row\n\nIf a summary row is requested in the `report` method, it will be included as the **first** row of the results.\n\n```ts\nconst [summaryRow, ...response] = await customer.report({\n  entity: \"campaign\",\n  metrics: [\"metrics.clicks\", \"metrics.all_conversions\"],\n  search_settings: {\n    return_summary_row: true,\n  },\n});\n```\n\nIf a summery row is requested in the `reportStream` method, it will be included as the **final** iterated row of the results.\n\n```ts\nconst stream = customer.reportStream({\n  entity: \"campaign\",\n  metrics: [\"metrics.clicks\", \"metrics.all_conversions\"],\n  search_settings: {\n    return_summary_row: true,\n  },\n});\n\nconst accumulator = [];\nfor await (const row of stream) {\n  accumulator.push(row);\n}\n\nconst summaryRow = accumulator.slice(-1)[0];\n```\n\n---\n\n## Total Results Count\n\nThe `reportCount` method acts like `report` but returns the total number of rows that the query would have returned (ignoring the limit). This replaces the `return_total_results_count` report option. Hooks are **not** called in this function to avoid cacheing conflicts.\n\n```ts\nconst totalRows = await customer.reportCount({\n  entity: \"search_term_view\",\n  attributes: [\"search_term_view.resource_name\"],\n});\n```\n\n---\n\n## Report Results Order\n\nThere are 2 methods of sorting the results of report. The prefered method is to use the `order` key, which should be an array of objects with a `field` key and an optional `sort_order` key. The order of the items in the array will map to the order of the sorting keys in the GAQL query, and hence the priorities of the sorts.\n\n```ts\nconst response = await customer.report({\n  entity: \"campaign\",\n  attributes: [\"campaign.id\"],\n  metrics: [\"metrics.clicks\"],\n  segments: [\"segments.date\"],\n  order: [\n    { field: \"metrics.clicks\", sort_order: \"DESC\" },\n    { field: \"segments.date\", sort_order: \"ASC\" },\n    { field: \"campaign.id\" }, // default sort_order is descending\n  ],\n});\n```\n\nThe other method is to use the `order_by` and `sort_order` keys, however this will be deprecated in a future version of the API.\n\n```ts\nconst response = await customer.report({\n  entity: \"campaign\",\n  attributes: [\"campaign.id\"],\n  metrics: [\"metrics.clicks\"],\n  segments: [\"segments.date\"],\n  order_by: \"metrics.clicks\",\n  sort_order: \"DESC\",\n});\n```\n\n---\n\n## Resource Names\n\nThe library provides a set of helper methods under the `ResourceNames` export. These are used for compiling resource names from ids. Arguments can be of the type `string`, `number`, or a mix of both. If you have a `client.Customer` instance available, you can get the customer id with `customer.credentials.customerId`.\n\n```ts\nimport { ResourceNames } from \"google-ads-api\";\n\nconst customerId = \"1234567890\";\nconst campaignId = \"3218318373\";\n\nResourceNames.campaign(customerId, campaignId);\n// \"customers/1234567890/campaigns/3218318373\"\n\nResourceNames.adGroup(123, 123);\n// \"customers/123/adGroups/123\"\n\nResourceNames.adGroupAd(\"1\", \"2\", \"3\");\n// \"customers/1/adGroupAds/2~3\"\n\nconst amsterdamLocationId = 1010543;\nResourceNames.geoTargetConstant(amsterdamLocationId);\n// \"geoTargetConstants/1010543\"\n\nResourceNames.accountBudget(customer.credentials.customer_id, 123);\n// \"customers/1234567890/accountBudgets/123\"\n```\n\n---\n\n## Hooks\n\nThe library provides hooks that can be executed before, after or on error of a query, stream or a mutation.\n\n### Query/stream hooks:\n\n- `onQueryStart`\n- `onQueryError`\n- `onQueryEnd`\n- `onStreamStart`\n- `onStreamError`\n\nThese hooks have access to the `customerCredentials` argument, containing the `customer_id`, `login_customer_id` and `linked_customer_id`.\n\nThese hooks also have access to the `query` argument, containing the GAQL query as a string.\n\nThese hooks also have access the the `reportOptions` argument. This will be undefined when using the `query` method.\n\n### Mutation hooks:\n\n- `onMutationStart`\n- `onMutationError`\n- `onMutationEnd`\n\nThese hooks have access to the `customerCredentials` argument, containing the `customer_id`, `login_customer_id` and `linked_customer_id`.\n\nThese hooks also have access to the `method` argument, containing the mutation method as a string.\n\n### Service hooks:\n\n- `onServiceStart`\n- `onServiceError`\n- `onServiceEnd`\n\nThese hooks have access to the `customerCredentials` argument, containing the `customer_id`, `login_customer_id` and `linked_customer_id`.\n\nThese hooks also have access to the `method` argument, containing the mutation method as a string.\n\n### Pre-request hooks:\n\n- `onQueryStart` - `query` and `report`\n- `onStreamStart` - `reportStream` and `reportStreamRaw`\n- `onMutationStart`\n- `onServiceStart`\n\nThese hooks are executed **before** a query/stream/mutation/service.\n\nThese hooks have access to the `cancel` method, which can cancel the action before it is done. The query and mutation pre-request hooks allow an optional argument to be passed into the `cancel` method, which will be used as an alternative return value for the query/mutation. A good use case for this method would be to cancel with a cached result.\n\nThese hooks also have access to the `editOptions` method which allows the request options to be changed before the request is sent. Keys included in the object passed to `editOptions` will be changed, and the rest will be maintained. A good use case for this method would be to set `validateOnly` as true when not in production.\n\n```ts\nimport { OnQueryStart } from \"google-ads-api\";\n\nconst onQueryStart: OnQueryStart = async ({ cancel, editOptions }) =\u003e {\n  if (env.mode === \"test\") {\n    cancel([]); // Cancels the request. The supplied argument will become the alternative return value in query and mutation hooks\n  }\n  if (env.mode === \"dev\") {\n    editOptions({ validate_only: true }); // Edits the request options\n  }\n};\n\nconst customer = client.Customer(\n  {\n    clientOptions,\n    customerOptions,\n  },\n  { onQueryStart }\n);\n```\n\n### On error hooks:\n\n- `onQueryError` - `query` and `report`\n- `onStreamError` - `reportStream` (but **not** `reportStreamRaw`)\n- `onMutationError`\n- `onServiceStart`\n\nThese hooks are executed when a query/stream/mutation/service throws an error. If the error is a Google Ads failure then it will be converted to a `GoogleAdsFailure` first. The error can be accessed in these hooks with the `error` argument. Note that the `onStreamError` hook will not work with the `reportStreamRaw` method to avoid blocking the thread.\n\n```ts\nimport { OnQueryError } from \"google-ads-api\";\n\nconst onQueryError: OnQueryError = async ({ error }) =\u003e {\n  console.log(error.message); // An Error or a GoogleAdsFailure\n};\n\nconst customer = client.Customer(\n  {\n    clientOptions,\n    customerOptions,\n  },\n  { onQueryError }\n);\n```\n\n### Post-request hooks:\n\n- `onQueryEnd` - `query` and `report`\n- `onMutationEnd`\n- `onServiceEnd`\n\nThese hooks are executed **after** a query, mutation or service. This library does not contain an `onStreamEnd` hook to avoid accumulating the results of streams, and also so that we don't block the thread by waiting for the end event to be emitted.\n\n```ts\nimport { OnQueryEnd } from \"google-ads-api\";\n\nconst onQueryEnd: OnQueryEnd = async ({ response, resolve }) =\u003e {\n  const [first] = response; // The results of the query/mutation\n  resolve([first]); // The supplied argument will become the alternative return value\n};\n\nconst customer = client.Customer(\n  {\n    clientOptions,\n    customerOptions,\n  },\n  { onQueryEnd }\n);\n```\n\n---\n\n## Error handling\n\nAll errors, apart from GRPC specific cases (such as a connection problem or timeout, [see more here](https://github.com/grpc/grpc/blob/master/doc/statuscodes.md)), are instances of a [GoogleAdsFailure](https://developers.google.com/google-ads/api/reference/rpc/v13/GoogleAdsFailure).\n\nYou can find a list of all error types for a specific version in [the official documentation](https://developers.google.com/google-ads/api/reference/rpc/v13/AccessInvitationErrorEnum.AccessInvitationError), as well as more information about [handling errors here](https://developers.google.com/google-ads/api/docs/best-practices/error-types).\n\n```ts\nimport { errors } from \"google-ads-api\";\n\ntry {\n  await customer.query(`\n      SELECT campaign.bad_field FROM campaign\n    `);\n} catch (err) {\n  if (err instanceof errors.GoogleAdsFailure) {\n    console.log(err.errors); // Array of errors.GoogleAdsError instances\n\n    // Get the first one and explicitly check for a certain error type\n    const [firstError] = err.errors;\n    if (\n      firstError.error_code.query_error ===\n      errors.QueryErrorEnum.QueryError.UNRECOGNIZED_FIELD\n    ) {\n      console.log(\n        `Error: using invalid field \"${firstError.trigger}\" in query`\n      );\n    }\n  }\n}\n```\n\n# Updating this library to the latest Google Ads API version\n\n1. Update google-ads-node \u0026 publish to NPM. Check that package for instructions.\n2. Update google-ads-node \u0026 google-gax in package.json. google-gax must be the same version as google-ads-node,\n   Otherwise you are likely to get a `TypeError: Channel credentials must be a ChannelCredentials object` error.\n3. Update the version in `version.ts`.\n4. Update the versions in `src/protos/index.ts`.\n5. Run `yarn compile` to update the generated files. The `tsc` command might fail -- if so, temporarily add @ts-nocheck to the top of problematic files so that the compile script can continue. After the compile script has run, those @ts-nocheck lines should no longer be required. The compile step depends on some environment variables being set (see scripts/fields.ts), so make sure to set those.\n6. Prettify the generated files so the the diffs are easier to read.\n7. Check that the diffs are expected and that the generated files are correct. New Gads versions will often add new services, fields, and enums.\n8. Test with `yarn test`.\n9. Test by linking to a real project and making some real requests.\n10. Once confident, bump the major version \u0026 publish to NPM.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopteo%2Fgoogle-ads-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopteo%2Fgoogle-ads-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopteo%2Fgoogle-ads-api/lists"}