@revclamp/google-ads-transparency-mcp 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -122,6 +122,7 @@ npm test
122
122
  | **[RevClamp Enterprise](https://revclamp.com/mcp)** | [`revclamp-mcp`](https://www.npmjs.com/package/revclamp-mcp) | Pre-deploy subscription revenue, webhook idempotency & compliance guard (`Stripe`, `Paddle`, `Lemon Squeezy`, `Shopify`) |
123
123
  | **[RevClamp AdPulse](https://apify.com/revclamp/google-ads-transparency-mcp)** | [`@revclamp/google-ads-transparency-mcp`](https://www.npmjs.com/package/@revclamp/google-ads-transparency-mcp) | Verified Google Ads advertiser entity disambiguation & active/historical creative intelligence |
124
124
  | **[RevClamp TalentPulse](https://apify.com/revclamp/unified-ats-jobs-mcp)** | [`@revclamp/unified-ats-jobs-mcp`](https://www.npmjs.com/package/@revclamp/unified-ats-jobs-mcp) | Unified 5-in-1 B2B ATS job postings, salary ranges & tech-stack hiring signals (`Greenhouse`, `Workday CXS`, `Ashby`, `Lever`, `SmartRecruiters`) |
125
+ | **[RevClamp ContactPulse](https://apify.com/revclamp/b2b-domain-contact-enricher-mcp)** | [`@revclamp/b2b-domain-contact-enricher-mcp`](https://www.npmjs.com/package/@revclamp/b2b-domain-contact-enricher-mcp) | B2B domain role-contact enrichment, RFC 9116 `security.txt` audit & DNS-over-HTTPS MX/SPF/DMARC deliverability posture |
125
126
 
126
127
  ## Terms of Use, Data Provenance & Independent Tool Disclaimer
127
128
 
package/actor_config.json CHANGED
@@ -21,7 +21,7 @@
21
21
  ],
22
22
  "manifest": {
23
23
  "name": "google-ads-transparency-mcp",
24
- "version": "1.0.8",
24
+ "version": "1.0.10",
25
25
  "title": "RevClamp AdPulse - Google Ads Transparency Scraper & MCP",
26
26
  "websiteUrl": "https://revclamp.com",
27
27
  "description": "Find verified Google Ads advertiser entities and extract active or historical ad creatives (Text, Image, Video), preview links, and run dates without browser or proxy overhead. Independent intelligence tool by RevClamp; not affiliated with or endorsed by Google LLC."
package/index.mjs CHANGED
@@ -28,6 +28,7 @@ import {
28
28
  COMPOSITE_CURSOR_PREFIX,
29
29
  MAX_CURSOR_LENGTH,
30
30
  MAX_QUERY_LENGTH,
31
+ KNOWN_ADVERTISER_DIRECTORY,
31
32
  CORPORATE_LEGAL_SUFFIX_TOKENS,
32
33
  collapseDottedAcronyms,
33
34
  foldBrandText,
@@ -100,6 +101,7 @@ export {
100
101
  COMPOSITE_CURSOR_PREFIX,
101
102
  MAX_CURSOR_LENGTH,
102
103
  MAX_QUERY_LENGTH,
104
+ KNOWN_ADVERTISER_DIRECTORY,
103
105
  CORPORATE_LEGAL_SUFFIX_TOKENS,
104
106
  collapseDottedAcronyms,
105
107
  foldBrandText,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@revclamp/google-ads-transparency-mcp",
3
3
  "mcpName": "com.revclamp/google-ads-transparency-mcp",
4
- "version": "1.0.8",
4
+ "version": "1.0.10",
5
5
  "type": "module",
6
6
  "description": "RevClamp AdPulse - Google Ads Transparency Scraper & MCP. Find verified advertiser entities and extract active or historical ad creatives (Text, Image, Video), preview links, and run dates with zero browser overhead. Independent tool by RevClamp; not affiliated with Google LLC.",
7
7
  "homepage": "https://revclamp.com",
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "com.revclamp/google-ads-transparency-mcp",
4
4
  "title": "RevClamp AdPulse - Google Ads Transparency Scraper & MCP",
5
5
  "description": "Google Ads Transparency Center advertiser & creative intelligence MCP server by RevClamp.",
6
- "version": "1.0.8",
6
+ "version": "1.0.10",
7
7
  "websiteUrl": "https://revclamp.com",
8
8
  "repository": {
9
9
  "url": "https://github.com/lhphong09t2/revclamp-mcp",
@@ -27,7 +27,7 @@
27
27
  "registryType": "npm",
28
28
  "registryBaseUrl": "https://registry.npmjs.org",
29
29
  "identifier": "@revclamp/google-ads-transparency-mcp",
30
- "version": "1.0.8",
30
+ "version": "1.0.10",
31
31
  "runtimeHint": "npx",
32
32
  "transport": {
33
33
  "type": "stdio"
@@ -43,7 +43,7 @@ export const ENCODED_BRAND_ICON_DATA_URI = `data:image/svg+xml;utf8,${encodeURIC
43
43
  export const MCP_SERVER_INFO = Object.freeze({
44
44
  name: 'google-ads-transparency-mcp',
45
45
  title: 'RevClamp AdPulse - Google Ads Transparency Scraper & MCP',
46
- version: '1.0.8',
46
+ version: '1.0.9',
47
47
  description:
48
48
  'Find verified Google Ads advertiser entities and extract active or historical ad creatives (Text, Image, Video), preview links, and run dates. Independent intelligence tool by RevClamp; not affiliated with or endorsed by Google LLC.',
49
49
  // Forward-compatible MCP Registry & Glama metadata extensions (ignored by older stdio clients)
@@ -56,6 +56,261 @@ export const MAX_QUERY_LENGTH = 512;
56
56
  const MIN_VALID_EPOCH_SEC = 946684800; // 2000-01-01T00:00:00Z
57
57
  const MAX_VALID_EPOCH_SEC = 4102444800; // 2100-01-01T00:00:00Z
58
58
 
59
+ /**
60
+ * Curated directory of verified Google Ads Transparency legal entities (`AR...`)
61
+ * for top global brands so common queries resolve in 0 SearchSuggestions RPC calls
62
+ * and degrade gracefully if upstream SearchSuggestions encounters rate-limiting.
63
+ */
64
+ export const KNOWN_ADVERTISER_DIRECTORY = Object.freeze(
65
+ Object.assign(Object.create(null), {
66
+ nike: Object.freeze({
67
+ advertiserId: 'AR16735076323512287233',
68
+ advertiserName: 'Nike, Inc.',
69
+ regionCode: 'US',
70
+ minAds: 9000,
71
+ maxAds: 10000,
72
+ }),
73
+ stripe: Object.freeze({
74
+ advertiserId: 'AR16951989901585285121',
75
+ advertiserName: 'Stripe, Inc.',
76
+ regionCode: 'US',
77
+ minAds: 8000,
78
+ maxAds: 9000,
79
+ }),
80
+ shopify: Object.freeze({
81
+ advertiserId: 'AR01625195283841286145',
82
+ advertiserName: 'Shopify Inc.',
83
+ regionCode: 'CA',
84
+ minAds: 40000,
85
+ maxAds: 50000,
86
+ }),
87
+ apple: Object.freeze({
88
+ advertiserId: 'AR03186575737440174081',
89
+ advertiserName: 'Apple Inc.',
90
+ regionCode: 'US',
91
+ minAds: 1000,
92
+ maxAds: 2000,
93
+ }),
94
+ openai: Object.freeze({
95
+ advertiserId: 'AR02788840342533701633',
96
+ advertiserName: 'OpenAI OpCo, LLC',
97
+ regionCode: 'US',
98
+ minAds: 40000,
99
+ maxAds: 50000,
100
+ }),
101
+ hubspot: Object.freeze({
102
+ advertiserId: 'AR10072600183532683265',
103
+ advertiserName: 'Hubspot, Inc.',
104
+ regionCode: 'US',
105
+ minAds: 2000,
106
+ maxAds: 3000,
107
+ }),
108
+ google: Object.freeze({
109
+ advertiserId: 'AR00753945370957971457',
110
+ advertiserName: 'Google LLC',
111
+ regionCode: 'US',
112
+ minAds: 21,
113
+ maxAds: 21,
114
+ }),
115
+ microsoft: Object.freeze({
116
+ advertiserId: 'AR15495076796520464385',
117
+ advertiserName: 'Microsoft Corporation',
118
+ regionCode: 'US',
119
+ minAds: 60000,
120
+ maxAds: 70000,
121
+ }),
122
+ amazon: Object.freeze({
123
+ advertiserId: 'AR12815412684405080065',
124
+ advertiserName: 'Amazon',
125
+ regionCode: 'US',
126
+ minAds: 2000,
127
+ maxAds: 3000,
128
+ }),
129
+ meta: Object.freeze({
130
+ advertiserId: 'AR17753757646030110721',
131
+ advertiserName: 'Meta',
132
+ regionCode: 'US',
133
+ minAds: 500,
134
+ maxAds: 600,
135
+ }),
136
+ netflix: Object.freeze({
137
+ advertiserId: 'AR11279752625278418945',
138
+ advertiserName: 'Netflix, Inc.',
139
+ regionCode: 'US',
140
+ minAds: 9000,
141
+ maxAds: 10000,
142
+ }),
143
+ airbnb: Object.freeze({
144
+ advertiserId: 'AR15908226811872411649',
145
+ advertiserName: 'Airbnb, Inc.',
146
+ regionCode: 'US',
147
+ minAds: 1000000,
148
+ maxAds: 1100000,
149
+ }),
150
+ uber: Object.freeze({
151
+ advertiserId: 'AR10766558596670947329',
152
+ advertiserName: 'Uber B.V.',
153
+ regionCode: 'NL',
154
+ minAds: 20000,
155
+ maxAds: 30000,
156
+ }),
157
+ salesforce: Object.freeze({
158
+ advertiserId: 'AR14047752120044617729',
159
+ advertiserName: 'Salesforce, Inc.',
160
+ regionCode: 'US',
161
+ minAds: 20000,
162
+ maxAds: 30000,
163
+ }),
164
+ adobe: Object.freeze({
165
+ advertiserId: 'AR15000638237259595777',
166
+ advertiserName: 'Adobe, Inc',
167
+ regionCode: 'US',
168
+ minAds: 100000,
169
+ maxAds: 200000,
170
+ }),
171
+ cloudflare: Object.freeze({
172
+ advertiserId: 'AR10379917531786772481',
173
+ advertiserName: 'Cloudflare, Inc.',
174
+ regionCode: 'US',
175
+ minAds: 500,
176
+ maxAds: 600,
177
+ }),
178
+ datadog: Object.freeze({
179
+ advertiserId: 'AR02089091059349979137',
180
+ advertiserName: 'DATADOG, INC.',
181
+ regionCode: 'US',
182
+ minAds: 9000,
183
+ maxAds: 10000,
184
+ }),
185
+ datadoghq: Object.freeze({
186
+ advertiserId: 'AR02089091059349979137',
187
+ advertiserName: 'DATADOG, INC.',
188
+ regionCode: 'US',
189
+ minAds: 9000,
190
+ maxAds: 10000,
191
+ }),
192
+ vercel: Object.freeze({
193
+ advertiserId: 'AR03920035274037395457',
194
+ advertiserName: 'Vercel Inc.',
195
+ regionCode: 'US',
196
+ minAds: 200,
197
+ maxAds: 300,
198
+ }),
199
+ figma: Object.freeze({
200
+ advertiserId: 'AR15466204033491927041',
201
+ advertiserName: 'Figma INC',
202
+ regionCode: 'US',
203
+ minAds: 1000,
204
+ maxAds: 2000,
205
+ }),
206
+ atlassian: Object.freeze({
207
+ advertiserId: 'AR16220030717323640833',
208
+ advertiserName: 'Atlassian US, Inc.',
209
+ regionCode: 'US',
210
+ minAds: 20000,
211
+ maxAds: 30000,
212
+ }),
213
+ snowflake: Object.freeze({
214
+ advertiserId: 'AR00247099845179867137',
215
+ advertiserName: 'Snowflake Inc.',
216
+ regionCode: 'US',
217
+ minAds: 4000,
218
+ maxAds: 5000,
219
+ }),
220
+ twilio: Object.freeze({
221
+ advertiserId: 'AR18227580619968217089',
222
+ advertiserName: 'Twilio Inc.',
223
+ regionCode: 'US',
224
+ minAds: 1000,
225
+ maxAds: 2000,
226
+ }),
227
+ booking: Object.freeze({
228
+ advertiserId: 'AR02934798844673654785',
229
+ advertiserName: 'Booking.com',
230
+ regionCode: 'NL',
231
+ minAds: 16500000,
232
+ maxAds: 16600000,
233
+ }),
234
+ oracle: Object.freeze({
235
+ advertiserId: 'AR08687048956523315201',
236
+ advertiserName: 'Oracle America, Inc.',
237
+ regionCode: 'US',
238
+ minAds: 3000,
239
+ maxAds: 4000,
240
+ }),
241
+ sap: Object.freeze({
242
+ advertiserId: 'AR14859734842907557889',
243
+ advertiserName: 'SAP SE',
244
+ regionCode: 'DE',
245
+ minAds: 2000,
246
+ maxAds: 3000,
247
+ }),
248
+ cisco: Object.freeze({
249
+ advertiserId: 'AR13942224429984514049',
250
+ advertiserName: 'Cisco Systems, Inc.',
251
+ regionCode: 'US',
252
+ minAds: 7000,
253
+ maxAds: 8000,
254
+ }),
255
+ nvidia: Object.freeze({
256
+ advertiserId: 'AR01839840586606051329',
257
+ advertiserName: 'NVIDIA Corporation',
258
+ regionCode: 'US',
259
+ minAds: 600,
260
+ maxAds: 700,
261
+ }),
262
+ canva: Object.freeze({
263
+ advertiserId: 'AR06374349942554624001',
264
+ advertiserName: 'Canva, Pty Ltd',
265
+ regionCode: 'AU',
266
+ minAds: 30000,
267
+ maxAds: 40000,
268
+ }),
269
+ asana: Object.freeze({
270
+ advertiserId: 'AR06554691289986629633',
271
+ advertiserName: 'Asana, Inc.',
272
+ regionCode: 'US',
273
+ minAds: 6000,
274
+ maxAds: 7000,
275
+ }),
276
+ zendesk: Object.freeze({
277
+ advertiserId: 'AR13756467712907804673',
278
+ advertiserName: 'Zendesk, Inc.',
279
+ regionCode: 'US',
280
+ minAds: 3000,
281
+ maxAds: 4000,
282
+ }),
283
+ okta: Object.freeze({
284
+ advertiserId: 'AR10734849505959608321',
285
+ advertiserName: 'Okta, Inc',
286
+ regionCode: 'US',
287
+ minAds: 1000,
288
+ maxAds: 2000,
289
+ }),
290
+ crowdstrike: Object.freeze({
291
+ advertiserId: 'AR13642683652595777537',
292
+ advertiserName: 'CROWDSTRIKE, INC.',
293
+ regionCode: 'US',
294
+ minAds: 8000,
295
+ maxAds: 9000,
296
+ }),
297
+ servicenow: Object.freeze({
298
+ advertiserId: 'AR04119835984416210945',
299
+ advertiserName: 'ServiceNow, Inc',
300
+ regionCode: 'US',
301
+ minAds: 10000,
302
+ maxAds: 20000,
303
+ }),
304
+ workday: Object.freeze({
305
+ advertiserId: 'AR09563861960156512257',
306
+ advertiserName: 'Workday, Inc.',
307
+ regionCode: 'US',
308
+ minAds: 5000,
309
+ maxAds: 6000,
310
+ }),
311
+ })
312
+ );
313
+
59
314
  /**
60
315
  * Canonical Google Ads Geo Target Criterion IDs mapped by ISO 3166-1 alpha-2 country code.
61
316
  * Verified against live `LookupService/GetCreativeById` field `17` (`{ "1": 2840, "5": 20261104 }`)
@@ -429,6 +684,13 @@ export const CORPORATE_LEGAL_SUFFIX_TOKENS = new Set([
429
684
  'lp',
430
685
  'holdings',
431
686
  'holding',
687
+ 'opco',
688
+ 'holdco',
689
+ 'topco',
690
+ 'bidco',
691
+ 'midco',
692
+ 'newco',
693
+ 'finco',
432
694
  'group',
433
695
  'international',
434
696
  'global',
@@ -651,10 +913,18 @@ export function parseBrandQueryTokens(rawQuery) {
651
913
  : rawBrandTokens;
652
914
 
653
915
  const brandStem = brandTokens[0] || foldedStemSource.replace(/[^\p{L}\p{N}]/gu, '');
654
- const brandStemWithoutHq =
655
- isDomainQuery && brandStem.length >= 6 && brandStem.endsWith('hq')
916
+ const candidateStemWithoutHq =
917
+ brandStem.length >= 6 && brandStem.endsWith('hq')
656
918
  ? brandStem.slice(0, -2)
657
919
  : brandStem;
920
+ const brandStemWithoutHq =
921
+ candidateStemWithoutHq !== brandStem &&
922
+ brandTokens.length === 1 &&
923
+ (isDomainQuery ||
924
+ Object.hasOwn(KNOWN_ADVERTISER_DIRECTORY, brandStem) ||
925
+ Object.hasOwn(KNOWN_ADVERTISER_DIRECTORY, candidateStemWithoutHq))
926
+ ? candidateStemWithoutHq
927
+ : brandStem;
658
928
 
659
929
  return {
660
930
  rawQuery: trimmed,
@@ -1273,8 +1543,18 @@ export function scoreAdvertiserCandidate(candidate, queryMeta, preferredCountry
1273
1543
  ? Boolean(leadingEntityWords[0] === brandTokens[0] && allBrandTokensPresentAsWords)
1274
1544
  : firstWordMatchesStem;
1275
1545
 
1546
+ const hasHqStemReduction = Boolean(
1547
+ brandTokens.length === 1 &&
1548
+ brandStemWithoutHq &&
1549
+ brandStemWithoutHq !== brandStem &&
1550
+ brandStemWithoutHq.length >= 2
1551
+ );
1276
1552
  let domainConcatPrefixWords = [];
1277
- if (queryMeta?.isDomainQuery && brandStemWithoutHq && brandStemWithoutHq.length >= 2) {
1553
+ if (
1554
+ (queryMeta?.isDomainQuery || hasHqStemReduction) &&
1555
+ brandStemWithoutHq &&
1556
+ brandStemWithoutHq.length >= 2
1557
+ ) {
1278
1558
  for (const wordList of [leadingEntityWords, nameWords]) {
1279
1559
  let acc = '';
1280
1560
  for (let k = 0; k < Math.min(5, wordList.length); k += 1) {
@@ -1294,12 +1574,11 @@ export function scoreAdvertiserCandidate(candidate, queryMeta, preferredCountry
1294
1574
  }
1295
1575
 
1296
1576
  const domainStemPrefixMatch = Boolean(
1297
- queryMeta?.isDomainQuery &&
1577
+ (queryMeta?.isDomainQuery || hasHqStemReduction) &&
1298
1578
  brandStemWithoutHq &&
1299
1579
  brandStemWithoutHq.length >= 2 &&
1300
- ((leadingEntityWords[0] === brandStemWithoutHq &&
1301
- (brandTokens.length <= 1 || allBrandTokensPresentAsWords)) ||
1302
- domainConcatPrefixWords.length > 0)
1580
+ (brandTokens.length <= 1 || allBrandTokensPresentAsWords) &&
1581
+ (leadingEntityWords[0] === brandStemWithoutHq || domainConcatPrefixWords.length > 0)
1303
1582
  );
1304
1583
 
1305
1584
  const matchedCoreTokens = exactBrandWordMatch
@@ -1529,10 +1808,15 @@ export async function executeGoogleAdsRpcPost(endpointUrl, fReqPayload, options
1529
1808
  headers: {
1530
1809
  'content-type': 'application/x-www-form-urlencoded;charset=UTF-8',
1531
1810
  accept: 'application/json, text/plain, */*',
1811
+ 'accept-language': 'en-US,en;q=0.9',
1532
1812
  origin: 'https://adstransparency.google.com',
1533
1813
  referer: 'https://adstransparency.google.com/',
1814
+ 'sec-fetch-dest': 'empty',
1815
+ 'sec-fetch-mode': 'cors',
1816
+ 'sec-fetch-site': 'same-origin',
1817
+ 'x-same-domain': '1',
1534
1818
  'user-agent':
1535
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
1819
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36',
1536
1820
  },
1537
1821
  body: encodedBody,
1538
1822
  redirect: 'manual',
@@ -1942,12 +2226,66 @@ export async function resolveAdvertiserEntities(options = {}) {
1942
2226
  };
1943
2227
  }
1944
2228
 
1945
- // Multi-token domains (e.g. on-running.com) join all tokens ("on running") rather than truncating to first token
2229
+ // Match single-brand or domain stem against KNOWN_ADVERTISER_DIRECTORY when !includeSubsidiaries
2230
+ const directoryKey =
2231
+ queryMeta.brandTokens.length === 1 ? String(queryMeta.brandStem || '').toLowerCase() : '';
2232
+ const fallbackDirectoryKey =
2233
+ queryMeta.brandTokens.length === 1
2234
+ ? String(queryMeta.brandStemWithoutHq || '').toLowerCase()
2235
+ : '';
2236
+ const rawKnownEntry = directoryKey
2237
+ ? (Object.hasOwn(KNOWN_ADVERTISER_DIRECTORY, directoryKey)
2238
+ ? KNOWN_ADVERTISER_DIRECTORY[directoryKey]
2239
+ : null) ||
2240
+ (fallbackDirectoryKey && Object.hasOwn(KNOWN_ADVERTISER_DIRECTORY, fallbackDirectoryKey)
2241
+ ? KNOWN_ADVERTISER_DIRECTORY[fallbackDirectoryKey]
2242
+ : null) ||
2243
+ null
2244
+ : null;
2245
+ const normalizedFilterCountry = rawCountry === 'UK' ? 'GB' : rawCountry;
2246
+ const knownDirectoryEntity =
2247
+ rawKnownEntry &&
2248
+ (!normalizedFilterCountry ||
2249
+ normalizedFilterCountry === 'ALL' ||
2250
+ normalizedFilterCountry === rawKnownEntry.regionCode)
2251
+ ? scoreAdvertiserCandidate(rawKnownEntry, queryMeta, normalizedFilterCountry)
2252
+ : null;
2253
+
2254
+ const singleEntityDirectoryEligible = maxEntities === 1;
2255
+ const shouldUseDirectoryBeforeRpc =
2256
+ Boolean(knownDirectoryEntity) &&
2257
+ singleEntityDirectoryEligible &&
2258
+ options.preferDirectory === true;
2259
+
2260
+ if (shouldUseDirectoryBeforeRpc) {
2261
+ return {
2262
+ ok: true,
2263
+ status: 'OK',
2264
+ resolutionSource: 'KNOWN_ADVERTISER_DIRECTORY',
2265
+ queryMeta,
2266
+ primaryEntity: knownDirectoryEntity,
2267
+ selectedEntities: [knownDirectoryEntity],
2268
+ allCandidates: [knownDirectoryEntity],
2269
+ rpcAttempts: 0,
2270
+ billableCount: 0,
2271
+ };
2272
+ }
2273
+
2274
+ // Multi-token domains (e.g. on-running.com) join all tokens ("on running") rather than truncating to first token;
2275
+ // single-token -hq domains/queries (e.g. datadoghq.com, datadoghq) search by brandStemWithoutHq ("datadog").
2276
+ const effectiveSingleTokenStem =
2277
+ queryMeta.brandStemWithoutHq && queryMeta.brandStemWithoutHq !== queryMeta.brandStem
2278
+ ? queryMeta.brandStemWithoutHq
2279
+ : queryMeta.brandStem;
1946
2280
  const searchQueryStr = queryMeta.isDomainQuery
1947
2281
  ? queryMeta.brandTokens.length > 1
1948
2282
  ? queryMeta.brandTokens.join(' ')
1949
- : queryMeta.brandStem
1950
- : queryMeta.rawQuery;
2283
+ : effectiveSingleTokenStem
2284
+ : queryMeta.brandTokens.length === 1 &&
2285
+ queryMeta.brandStemWithoutHq &&
2286
+ queryMeta.brandStemWithoutHq !== queryMeta.brandStem
2287
+ ? queryMeta.brandStemWithoutHq
2288
+ : queryMeta.rawQuery;
1951
2289
 
1952
2290
  const endpointUrl = options.endpointUrl || GOOGLE_ADS_RPC_ENDPOINTS.SearchSuggestions;
1953
2291
  const rpcRes = await executeGoogleAdsRpcPost(
@@ -1957,6 +2295,24 @@ export async function resolveAdvertiserEntities(options = {}) {
1957
2295
  );
1958
2296
 
1959
2297
  if (!rpcRes.ok) {
2298
+ if (
2299
+ rpcRes.status === 'UPSTREAM_RATE_LIMITED' &&
2300
+ knownDirectoryEntity &&
2301
+ singleEntityDirectoryEligible &&
2302
+ options.fallbackToDirectoryOnRateLimit !== false
2303
+ ) {
2304
+ return {
2305
+ ok: true,
2306
+ status: 'OK',
2307
+ resolutionSource: 'KNOWN_ADVERTISER_DIRECTORY_FALLBACK',
2308
+ queryMeta,
2309
+ primaryEntity: knownDirectoryEntity,
2310
+ selectedEntities: [knownDirectoryEntity],
2311
+ allCandidates: [knownDirectoryEntity],
2312
+ rpcAttempts: rpcRes.attempts,
2313
+ billableCount: 0,
2314
+ };
2315
+ }
1960
2316
  return {
1961
2317
  ok: false,
1962
2318
  status: rpcRes.status,
@@ -2760,7 +3116,13 @@ export async function fetchAndExtractStaticAdCopy(previewUrl, options = {}) {
2760
3116
  method: 'GET',
2761
3117
  headers: {
2762
3118
  accept: 'application/javascript, text/javascript, text/plain, */*',
3119
+ 'accept-language': 'en-US,en;q=0.9',
2763
3120
  referer: 'https://adstransparency.google.com/',
3121
+ 'sec-fetch-dest': 'script',
3122
+ 'sec-fetch-mode': 'no-cors',
3123
+ 'sec-fetch-site': 'cross-site',
3124
+ 'user-agent':
3125
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36',
2764
3126
  },
2765
3127
  redirect: 'manual',
2766
3128
  signal: controller.signal,