Zernio
Zernio
PlatformsGoogle Ads

Build

Create AdsPerformance MaxDemand Gen

Target

KeywordsCustomer Match

Measure

Insights & GAQLConversionsURL Tracking Tags

Operate

RecommendationsAccounts & LabelsLimits and errors
Dashboard
llms.txtOpenAPI
OverviewPlatformsAPI ReferenceResources
Google Ads

Accounts & Labels

Read the Google Ads manager (MCC) hierarchy behind a connection, invite, accept, decline and end manager links, and organize campaigns, ad groups, ads and keywords with Google Ads labels.


When you finish this page you can see every Google Ads account a connection reaches and how they nest under manager (MCC) accounts, manage the links between managers and clients, and tag campaigns, ad groups, ads and keywords with labels. You need a googleads account.

Manager hierarchy

GET /v1/ads/accounts/hierarchy reads the manager and client tree live from Google. It starts from every customer the Google user behind the connection can access directly and walks each tree to any depth, so every client carries its direct parent, the link id and the link status.

curl "https://zernio.com/api/v1/ads/accounts/hierarchy?accountId=66b2e19d8c3f5a7e9d0b1c2d" \
  -H "Authorization: Bearer $ZERNIO_API_KEY"

Response (200), trimmed to one tree:

{
  "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
  "roots": [
    {
      "customerId": "9876543210",
      "name": "Acme Agency MCC",
      "currency": "USD",
      "timeZone": "America/New_York",
      "manager": true,
      "testAccount": false,
      "status": "ENABLED",
      "managerLinks": [],
      "clients": [
        {
          "customerId": "1234567890",
          "name": "Acme Search",
          "currency": "USD",
          "timeZone": "America/New_York",
          "manager": false,
          "testAccount": false,
          "hidden": false,
          "level": 1,
          "status": "ENABLED",
          "parentCustomerId": "9876543210",
          "managerLinkId": "345678901",
          "linkStatus": "ACTIVE"
        },
        {
          "customerId": "5550001111",
          "name": null,
          "currency": null,
          "timeZone": null,
          "manager": false,
          "testAccount": false,
          "hidden": false,
          "level": 1,
          "status": null,
          "parentCustomerId": "9876543210",
          "managerLinkId": "345678902",
          "linkStatus": "PENDING"
        }
      ]
    }
  ],
  "directCustomers": [
    { "customerId": "9876543210", "manager": true, "pendingInvitations": [] }
  ],
  "unavailable": [],
  "truncated": false,
  "cachedAt": null,
  "stale": false
}
  • roots has one entry per tree. clients lists every account under the root at any depth, with level (1 is a direct client of the root) and parentCustomerId, followed by invitations the manager sent that the client has not accepted yet (linkStatus: "PENDING"; Google returns no name or currency for them). Refused, canceled and ended links are history and are left out.
  • managerLinks on a root lists the managers linked to that account, including invitations it can still accept.
  • directCustomers lists every account the Google user accesses directly, the ones this connection can accept or decline invitations for, with their pendingInvitations.
  • Customers Google refuses to read (for example a cancelled account) are listed in unavailable with Google's reason instead of failing the call. Up to 50 roots and 50 managers per root are read, and truncated is true when more exist.
  • adAccountId narrows the read to the tree rooted at one directly accessible customer.
  • When the connection is scoped to specific ad accounts, client accounts outside that scope are hidden; managers stay visible.

The read is cached for 10 minutes per connection and carries cachedAt and stale.

Manager links

A manager link connects a manager account to a client. Invite a client with POST /v1/ads/accounts/manager-links:

curl -X POST "https://zernio.com/api/v1/ads/accounts/manager-links" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
    "managerCustomerId": "9876543210",
    "clientCustomerId": "5550001111"
  }'

Response (201):

{
  "managerCustomerId": "9876543210",
  "clientCustomerId": "5550001111",
  "managerLinkId": "345678902",
  "status": "PENDING"
}

The manager must be one the connection's Google user reaches, directly or under another manager; the client can be any Google Ads account. The link stays PENDING until someone with access to the client accepts it, in Google Ads or through the call below. The invitation is not idempotent: Google refuses a second invitation while one is pending. validateOnly: true has Google check the request without sending anything (200, with managerLinkId: null).

PATCH /v1/ads/accounts/manager-links changes one link, identified by managerCustomerId, clientCustomerId and managerLinkId (all from the hierarchy read), with an action:

actionActs asWhat it does
acceptThe clientAccepts a pending invitation. The connection's Google user needs direct access to the client account; access through a manager is not enough, because the link does not exist yet.
declineThe clientDeclines a pending invitation, with the same access rule.
cancelThe managerWithdraws a pending invitation.
unlinkThe managerEnds an active link.
curl -X PATCH "https://zernio.com/api/v1/ads/accounts/manager-links" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
    "managerCustomerId": "9876543210",
    "clientCustomerId": "5550001111",
    "managerLinkId": "345678902",
    "action": "accept"
  }'

The response has the same shape, with the status the link has after the call (ACTIVE, REFUSED, CANCELED or INACTIVE). validateOnly: true checks the change without applying it.

Labels

Google Ads labels are free-form tags you attach to campaigns, ad groups, ads and keywords to group them in reports and in the Google Ads UI. Every label call takes accountId, plus adAccountId (the customer id) when the connection has several Google customers.

POST /v1/ads/labels creates one. name (up to 80 characters) is unique per customer, so a duplicate returns 400; backgroundColor (#RRGGBB) and description (up to 200 characters) are optional.

curl -X POST "https://zernio.com/api/v1/ads/labels" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
    "adAccountId": "1234567890",
    "name": "Q1 launch",
    "backgroundColor": "#1A73E8"
  }'

Response (201): { "customerId": "1234567890", "id": "22334455", "resourceName": "customers/1234567890/labels/22334455" }.

Attach it with POST /v1/ads/labels/{labelId}/assignments, sending up to 1,000 ids in each target list:

curl -X POST "https://zernio.com/api/v1/ads/labels/22334455/assignments" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
    "adAccountId": "1234567890",
    "campaignIds": ["21874563210"],
    "adSetIds": ["165489732105"],
    "adIds": ["165489732105~735198246013"],
    "keywordIds": ["165489732105~987654321"]
  }'

Response (200): { "customerId": "1234567890", "labelId": "22334455", "attached": 3, "unchanged": 1 }.

All ids are Google's own. Campaigns and ad groups take their numeric ids; ads and keywords take the composite ids Google puts in their resource names, {adGroupId}~{adId} and {adGroupId}~{criterionId} (the keyword form is the tail of resourceName on GET /v1/ads/keywords). Attaching is idempotent: a target that already carries the label is counted in unchanged instead of failing the call. DELETE on the same path takes the same body and removes the label from those targets, reporting detached and unchanged.

The rest of the lifecycle:

  • GET /v1/ads/labels lists every non-removed label in one page, with id, name, status, backgroundColor and description. It is cached for 10 minutes and served with stale: true when Google's quota runs out. On a Meta ad account the same endpoint lists Meta's ad labels.
  • PATCH /v1/ads/labels/{labelId} changes the name, color or description; only the fields you send are written, and description: "" clears it.
  • DELETE /v1/ads/labels/{labelId} removes the label, and Google drops it from everything it was attached to.

Creating, editing and assigning labels is Google-only; those calls return 501 on other platforms.

Related

  • Keywords: where the keyword ids for keywordIds come from.
  • Limits and errors: quotas and what the API cannot do.
  • Get manager account hierarchy and Attach a Google Ads label: every field.
Was this page helpful?

Recommendations

Read Google's optimization recommendations for a Google Ads account with their estimated impact, apply them with Google's suggested values or your own, and dismiss the ones you do not want.

Limits and errors

The per-user burst limit and Google quota on live Google Ads calls, what the Google Ads API and Zernio's integration do not expose, and the errors Google returns most often.

On this page

Manager hierarchyManager linksLabelsRelated