Google Ads Scripts: Can we add/remove in-market/affinity segments (USER_INTEREST) on Search ad groups?
TL;DR: We’re trying to programmatically include and exclude in-market/affinity segments on Search ad groups using Google Ads Scripts. Reading existing segments works. But attempts to add/exclude segments via AdGroup.targeting().newUserListBuilder().withAudienceId(...).build() consistently fail with RESOURCE_NOT_FOUND, even when we pass what appear to be valid audience IDs (not criterion IDs). Looking for a definitive answer: is this supported in Scripts? If yes, what’s the correct builder/API usage for USER_INTEREST segments? If not, what’s the recommended workaround?
Goal: Spin out top-performing audience segments into their own ad groups, set Target & bid, and exclude those segments from other ad groups in the same Search campaign.
What we can do successfully:
Read and rank segments (from campaign_audience_view) to pick winners.
Read existing ad group/campaign audiences via adGroup.targeting().audiences() / campaign.targeting().audiences().
For those existing audiences, we can see:
audience.getAudienceType() → "USER_INTEREST" (as expected for in-market/affinity),
audience.getAudienceId() (small integer like 92501),
audience.getId() (the criterionId attached to that ad group).
What fails: Adding or excluding a USER_INTEREST segment by audienceId using the builder.
Resulting error (from op.getErrors()):
Resource was not found. ({"errorCode":{"mutateError":"RESOURCE_NOT_FOUND"},"message":"Resource was not found.","trigger":{"int64Value":"92501"},"location":{"fieldPathElements":[{"fieldName":"mutate_operations","index":1},{"fieldName":"ad_group_criterion_operation"},{"fieldName":"create"},{"fieldName":"user_list"},{"fieldName":"user_list"}]}})Notes:
We’re not passing a criterionId here; we pass audienceId (the small ID we get via audience.getAudienceId() from an existing attachment).
The failure happens even when we pre-check that the audience isn’t already attached.
We see the same RESOURCE_NOT_FOUND.
When we inspect an existing in-market segment attached to an ad group, we log both IDs:
function debugIds(adGroupId) { var ag = AdsApp.adGroups().withIds([adGroupId]).get().next(); var it = ag.targeting().audiences().get(); while (it.hasNext()) { var a = it.next(); Logger.log( a.getName() + ' | type=' + a.getAudienceType() + // "USER_INTEREST" ' | audienceId=' + a.getAudienceId() + ' | criterionId=' + a.getId() ); } }This consistently shows the two distinct values:
audienceId (what we feed to the builder),
criterionId (the attachment id—used for reporting and removal of that attachment).
We are not feeding criterionId into .withAudienceId().
In Scripts, AdGroup.targeting() exposes:
audiences() / excludedAudiences() (selectors) — these work for reading.
getTargetingSetting() / setTargetingSetting() — these work; we set "USER_INTEREST_AND_LIST" to 'TARGET_ALL_FALSE'.
newUserListBuilder() — the only builder available for audiences (there’s no newAudienceBuilder() in Scripts). The builder lets you .withAudienceId(id).build() (include) or .exclude().build() (exclude).
The builder name implies “user lists”, but the objects we read for in-market segments are SearchAdGroupAudience with getAudienceType() === 'USER_INTEREST'. The docs suggest this builder handles both lists and interests, but it’s not behaving that way for us when creating a new attachment.
Is adding/removing USER_INTEREST segments (in-market/affinity) supported in Google Ads Scripts today?
If yes, can you provide a minimal working snippet (Search ad group) showing the correct builder and the correct ID to use? (We already confirmed we must use audienceId, not criterionId.)
If no, is this supported only via the Google Ads API (not Scripts)? Any timeline or workaround?
Is there a different builder or method we should be using for USER_INTEREST (as opposed to user lists) in Scripts?
Is there a reliable way (from Scripts) to look up the correct audienceId for a given in-market segment when it’s not yet attached anywhere? (Right now, we can only see audienceId by reading an existing attachment.)
Any known limitations when targeting segments at the ad group level vs campaign level in Scripts?
Product: Google Ads Scripts (not the Ads API)
Account/Campaign: Search campaign
What we’ve already ensured:
The ad group exists and is enabled.
Targeting setting set to 'TARGET_ALL_FALSE' for 'USER_INTEREST_AND_LIST'.
We do not attempt to add duplicates.
We pass the audienceId (e.g., 92501), not the criterionId (e.g., 51668103775).
We’re building an automation that:
ranks audience segments by performance (CTR/CPC deltas vs. campaign),
spins out the best ones into dedicated ad groups (Target & bid),
excludes those segments from the rest of the campaign to avoid overlap.
Everything is wired up except the actual “attach/detach USER_INTEREST segment” step, which is blocked by the errors above.
Any definitive guidance or a tiny working example would be hugely appreciated. Thanks! 🙏
Hi,
Thank you for reaching out to the Google Ads Scripts support team.
Please be informed that adding or excluding a “USER_INTEREST” audience segment using “AdsApp.SearchAdGroupAudienceBuilder” is not supported in the Google Ads Scripts. The Builder only supports “USER_LIST” audiences whereas the AdsApp.SearchAdGroupAudience selector reads both “USER_INTEREST” and “USER_LIST” audiences. The method “withAudienceId(audienceId)” you are using to exclude will only accept “USER_LIST” audience Id as input.
I hope this clarifies. Feel free to reach out to us for any further concerns.
![]() |
Google Ads Scripts Team |
[2025-10-21 20:20:37Z GMT] This message is in relation to case "ref:!00D1U01174p.!500Ht01vRPSh:ref" (ADR-00336130)