@growthbook/mcp 1.5.1 → 1.6.0

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
@@ -17,6 +17,24 @@ Use the following env variables to configure the MCP server.
17
17
  | GB_EMAIL | Required | Your email address used with GrowthBook. Used when creating feature flags and experiments.|
18
18
  | GB_API_URL | Optional | Your GrowthBook API URL. Defaults to `https://api.growthbook.io`. |
19
19
  | GB_APP_ORIGIN | Optional | Your GrowthBook app URL Defaults to `https://app.growthbook.io`. |
20
+ | GB_HTTP_HEADER_* | Optional | Custom HTTP headers to include in all GrowthBook API requests. Use the pattern `GB_HTTP_HEADER_<NAME>` where `<NAME>` is converted to proper HTTP header format (underscores become hyphens). Examples: `GB_HTTP_HEADER_X_TENANT_ID=abc123` becomes `X-Tenant-ID: abc123`, `GB_HTTP_HEADER_CF_ACCESS_TOKEN=<token>` becomes `Cf-Access-Token: <token>`. Multiple custom headers can be configured. |
20
21
 
22
+ **Custom Headers Examples**
23
+
24
+ For multi-tenant deployments or proxy configurations, you can add custom headers:
25
+
26
+ ```bash
27
+ # Multi-tenant identification
28
+ GB_HTTP_HEADER_X_TENANT_ID=tenant-123
29
+
30
+ # Cloudflare Access proxy authentication
31
+ GB_HTTP_HEADER_CF_ACCESS_TOKEN=eyJhbGciOiJSUzI1NiIs...
32
+ ```
33
+
34
+ **Security Best Practices**
35
+ - Always use HTTPS for API communication (default for GrowthBook Cloud)
36
+ - Store API keys and sensitive headers in environment variables, never hardcode them
37
+ - Use the Authorization header (via GB_API_KEY) for authentication
38
+ - Custom headers are useful for multi-tenant scenarios, proxy routing, or additional context
21
39
 
22
40
  Add the MCP server to your AI tool of choice. See the [official docs](https://docs.growthbook.io/integrations/mcp) for complete a complete guide.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@growthbook/mcp",
3
3
  "mcpName": "io.github.growthbook/growthbook-mcp",
4
- "version": "1.5.1",
4
+ "version": "1.6.0",
5
5
  "description": "MCP Server for interacting with GrowthBook",
6
6
  "access": "public",
7
7
  "homepage": "https://github.com/growthbook/growthbook-mcp",
@@ -1,4 +1,4 @@
1
- import { handleResNotOk, fetchWithRateLimit, } from "../utils.js";
1
+ import { handleResNotOk, fetchWithRateLimit, buildHeaders, } from "../utils.js";
2
2
  import envPaths from "env-paths";
3
3
  import { writeFile, readFile, mkdir, unlink } from "fs/promises";
4
4
  import { join } from "path";
@@ -10,18 +10,14 @@ const userDefaultsFile = join(experimentDefaultsDir, "user-defaults.json");
10
10
  export async function createDefaults(apiKey, baseApiUrl) {
11
11
  try {
12
12
  const experimentsResponse = await fetchWithRateLimit(`${baseApiUrl}/api/v1/experiments`, {
13
- headers: {
14
- Authorization: `Bearer ${apiKey}`,
15
- },
13
+ headers: buildHeaders(apiKey, false),
16
14
  });
17
15
  await handleResNotOk(experimentsResponse);
18
16
  const experimentData = await experimentsResponse.json();
19
17
  if (experimentData.experiments.length === 0) {
20
18
  // No experiments: return assignment query and environments if possible
21
19
  const assignmentQueryResponse = await fetchWithRateLimit(`${baseApiUrl}/api/v1/data-sources`, {
22
- headers: {
23
- Authorization: `Bearer ${apiKey}`,
24
- },
20
+ headers: buildHeaders(apiKey, false),
25
21
  });
26
22
  await handleResNotOk(assignmentQueryResponse);
27
23
  const dataSourceData = await assignmentQueryResponse.json();
@@ -30,9 +26,7 @@ export async function createDefaults(apiKey, baseApiUrl) {
30
26
  }
31
27
  const assignmentQuery = dataSourceData.dataSources[0].assignmentQueries[0].id;
32
28
  const environmentsResponse = await fetchWithRateLimit(`${baseApiUrl}/api/v1/environments`, {
33
- headers: {
34
- Authorization: `Bearer ${apiKey}`,
35
- },
29
+ headers: buildHeaders(apiKey, false),
36
30
  });
37
31
  await handleResNotOk(environmentsResponse);
38
32
  const environmentsData = await environmentsResponse.json();
@@ -55,10 +49,7 @@ export async function createDefaults(apiKey, baseApiUrl) {
55
49
  if (experimentData.hasMore) {
56
50
  const mostRecentExperiments = await fetchWithRateLimit(`${baseApiUrl}/api/v1/experiments?offset=${experimentData.total -
57
51
  Math.min(50, experimentData.count + experimentData.offset)}&limit=${Math.min(50, experimentData.count + experimentData.offset)}`, {
58
- headers: {
59
- Authorization: `Bearer ${apiKey}`,
60
- "Content-Type": "application/json",
61
- },
52
+ headers: buildHeaders(apiKey),
62
53
  });
63
54
  await handleResNotOk(mostRecentExperiments);
64
55
  const mostRecentExperimentData = await mostRecentExperiments.json();
@@ -113,9 +104,7 @@ export async function createDefaults(apiKey, baseApiUrl) {
113
104
  }
114
105
  // Fetch environments
115
106
  const environmentsResponse = await fetchWithRateLimit(`${baseApiUrl}/api/v1/environments`, {
116
- headers: {
117
- Authorization: `Bearer ${apiKey}`,
118
- },
107
+ headers: buildHeaders(apiKey, false),
119
108
  });
120
109
  await handleResNotOk(environmentsResponse);
121
110
  const environmentsData = await environmentsResponse.json();
@@ -1,4 +1,4 @@
1
- import { handleResNotOk, fetchWithRateLimit, } from "../utils.js";
1
+ import { handleResNotOk, fetchWithRateLimit, buildHeaders, } from "../utils.js";
2
2
  import { z } from "zod";
3
3
  /**
4
4
  * Tool: get_environments
@@ -14,10 +14,7 @@ export function registerEnvironmentTools({ server, baseApiUrl, apiKey, }) {
14
14
  }, async () => {
15
15
  try {
16
16
  const res = await fetchWithRateLimit(`${baseApiUrl}/api/v1/environments`, {
17
- headers: {
18
- Authorization: `Bearer ${apiKey}`,
19
- "Content-Type": "application/json",
20
- },
17
+ headers: buildHeaders(apiKey),
21
18
  });
22
19
  await handleResNotOk(res);
23
20
  const data = await res.json();
@@ -1,4 +1,4 @@
1
- import { fetchWithRateLimit, handleResNotOk } from "../../utils.js";
1
+ import { fetchWithRateLimit, handleResNotOk, buildHeaders } from "../../utils.js";
2
2
  import { computeVerdict, formatLift, getYearMonth, median, round, } from "./summary-logic.js";
3
3
  // Metric Lookup with caching
4
4
  const metricCache = new Map();
@@ -48,10 +48,7 @@ async function getMetricLookup(baseApiUrl, apiKey, metricIds) {
48
48
  const regularResults = await processBatch(regularMetricIds, MAX_CONCURRENT_FETCHES, async (metricId) => {
49
49
  try {
50
50
  const res = await fetchWithRateLimit(`${baseApiUrl}/api/v1/metrics/${metricId}`, {
51
- headers: {
52
- Authorization: `Bearer ${apiKey}`,
53
- "Content-Type": "application/json",
54
- },
51
+ headers: buildHeaders(apiKey),
55
52
  });
56
53
  await handleResNotOk(res);
57
54
  const data = await res.json();
@@ -74,10 +71,7 @@ async function getMetricLookup(baseApiUrl, apiKey, metricIds) {
74
71
  const factResults = await processBatch(factMetricIds, MAX_CONCURRENT_FETCHES, async (metricId) => {
75
72
  try {
76
73
  const res = await fetchWithRateLimit(`${baseApiUrl}/api/v1/fact-metrics/${metricId}`, {
77
- headers: {
78
- Authorization: `Bearer ${apiKey}`,
79
- "Content-Type": "application/json",
80
- },
74
+ headers: buildHeaders(apiKey),
81
75
  });
82
76
  await handleResNotOk(res);
83
77
  const data = await res.json();
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { generateLinkToGrowthBook, getDocsMetadata, handleResNotOk, SUPPORTED_FILE_EXTENSIONS, paginationSchema, fetchWithRateLimit, fetchWithPagination, featureFlagSchema, fetchFeatureFlag, mergeRuleIntoFeatureFlag, } from "../../utils.js";
2
+ import { generateLinkToGrowthBook, getDocsMetadata, handleResNotOk, SUPPORTED_FILE_EXTENSIONS, paginationSchema, fetchWithRateLimit, fetchWithPagination, featureFlagSchema, fetchFeatureFlag, mergeRuleIntoFeatureFlag, buildHeaders, } from "../../utils.js";
3
3
  import { getDefaults } from "../defaults.js";
4
4
  import { handleSummaryMode } from "./experiment-summary.js";
5
5
  export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin, user, }) {
@@ -31,10 +31,7 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
31
31
  if (experimentId) {
32
32
  try {
33
33
  const res = await fetchWithRateLimit(`${baseApiUrl}/api/v1/experiments/${experimentId}`, {
34
- headers: {
35
- Authorization: `Bearer ${apiKey}`,
36
- "Content-Type": "application/json",
37
- },
34
+ headers: buildHeaders(apiKey),
38
35
  });
39
36
  await handleResNotOk(res);
40
37
  const data = await res.json();
@@ -45,9 +42,7 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
45
42
  }
46
43
  try {
47
44
  const resultsRes = await fetchWithRateLimit(`${baseApiUrl}/api/v1/experiments/${experimentId}/results`, {
48
- headers: {
49
- Authorization: `Bearer ${apiKey}`,
50
- },
45
+ headers: buildHeaders(apiKey, false),
51
46
  });
52
47
  await handleResNotOk(resultsRes);
53
48
  const resultsData = await resultsRes.json();
@@ -104,9 +99,7 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
104
99
  }
105
100
  try {
106
101
  const resultsRes = await fetchWithRateLimit(`${baseApiUrl}/api/v1/experiments/${experiment.id}/results`, {
107
- headers: {
108
- Authorization: `Bearer ${apiKey}`,
109
- },
102
+ headers: buildHeaders(apiKey, false),
110
103
  });
111
104
  await handleResNotOk(resultsRes);
112
105
  const resultsData = await resultsRes.json();
@@ -160,10 +153,7 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
160
153
  const queryParams = new URLSearchParams();
161
154
  queryParams.append("limit", "100");
162
155
  const res = await fetchWithRateLimit(`${baseApiUrl}/api/v1/attributes?${queryParams.toString()}`, {
163
- headers: {
164
- Authorization: `Bearer ${apiKey}`,
165
- "Content-Type": "application/json",
166
- },
156
+ headers: buildHeaders(apiKey),
167
157
  });
168
158
  await handleResNotOk(res);
169
159
  const data = await res.json();
@@ -258,10 +248,7 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
258
248
  try {
259
249
  const experimentRes = await fetchWithRateLimit(`${baseApiUrl}/api/v1/experiments`, {
260
250
  method: "POST",
261
- headers: {
262
- Authorization: `Bearer ${apiKey}`,
263
- "Content-Type": "application/json",
264
- },
251
+ headers: buildHeaders(apiKey),
265
252
  body: JSON.stringify(experimentPayload),
266
253
  });
267
254
  await handleResNotOk(experimentRes);
@@ -283,10 +270,7 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
283
270
  const flagPayload = mergeRuleIntoFeatureFlag(existingFeature, newRule, experimentDefaults.environments);
284
271
  const flagRes = await fetchWithRateLimit(`${baseApiUrl}/api/v1/features/${featureId}`, {
285
272
  method: "POST",
286
- headers: {
287
- Authorization: `Bearer ${apiKey}`,
288
- "Content-Type": "application/json",
289
- },
273
+ headers: buildHeaders(apiKey),
290
274
  body: JSON.stringify(flagPayload),
291
275
  });
292
276
  await handleResNotOk(flagRes);
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { getDocsMetadata, handleResNotOk, generateLinkToGrowthBook, paginationSchema, featureFlagSchema, fetchWithRateLimit, fetchWithPagination, fetchFeatureFlag, mergeRuleIntoFeatureFlag, } from "../utils.js";
2
+ import { getDocsMetadata, handleResNotOk, generateLinkToGrowthBook, paginationSchema, featureFlagSchema, fetchWithRateLimit, fetchWithPagination, fetchFeatureFlag, mergeRuleIntoFeatureFlag, buildHeaders, } from "../utils.js";
3
3
  import { exec } from "child_process";
4
4
  import { getDefaults } from "./defaults.js";
5
5
  export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, user, }) {
@@ -23,10 +23,7 @@ export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, us
23
23
  }
24
24
  else {
25
25
  const envRes = await fetchWithRateLimit(`${baseApiUrl}/api/v1/features/environments`, {
26
- headers: {
27
- Authorization: `Bearer ${apiKey}`,
28
- "Content-Type": "application/json",
29
- },
26
+ headers: buildHeaders(apiKey),
30
27
  });
31
28
  await handleResNotOk(envRes);
32
29
  const envData = await envRes.json();
@@ -51,10 +48,7 @@ export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, us
51
48
  try {
52
49
  const res = await fetchWithRateLimit(`${baseApiUrl}/api/v1/features`, {
53
50
  method: "POST",
54
- headers: {
55
- Authorization: `Bearer ${apiKey}`,
56
- "Content-Type": "application/json",
57
- },
51
+ headers: buildHeaders(apiKey),
58
52
  body: JSON.stringify(payload),
59
53
  });
60
54
  await handleResNotOk(res);
@@ -123,10 +117,7 @@ export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, us
123
117
  const payload = mergeRuleIntoFeatureFlag(existingFeature, newRule, defaultEnvironments);
124
118
  const res = await fetchWithRateLimit(`${baseApiUrl}/api/v1/features/${featureId}`, {
125
119
  method: "POST",
126
- headers: {
127
- Authorization: `Bearer ${apiKey}`,
128
- "Content-Type": "application/json",
129
- },
120
+ headers: buildHeaders(apiKey),
130
121
  body: JSON.stringify(payload),
131
122
  });
132
123
  await handleResNotOk(res);
@@ -134,7 +125,7 @@ export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, us
134
125
  const linkToGrowthBook = generateLinkToGrowthBook(appOrigin, "features", featureId);
135
126
  const { docs, language, stub } = getDocsMetadata(fileExtension);
136
127
  const text = `This is the API response: ${JSON.stringify(data)}
137
-
128
+
138
129
  Additionally, here is a template of what to show to the user:
139
130
 
140
131
  **✅ Your feature flag \`my-flag-name\` is ready!.**
@@ -175,10 +166,7 @@ export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, us
175
166
  if (featureFlagId) {
176
167
  try {
177
168
  const res = await fetchWithRateLimit(`${baseApiUrl}/api/v1/features/${featureFlagId}`, {
178
- headers: {
179
- Authorization: `Bearer ${apiKey}`,
180
- "Content-Type": "application/json",
181
- },
169
+ headers: buildHeaders(apiKey),
182
170
  });
183
171
  await handleResNotOk(res);
184
172
  const data = await res.json();
@@ -233,10 +221,7 @@ ask if they want to remove references to the feature flag from the codebase.
233
221
  offset: offset?.toString(),
234
222
  });
235
223
  const res = await fetchWithRateLimit(`${baseApiUrl}/api/v1/features?${queryParams.toString()}`, {
236
- headers: {
237
- Authorization: `Bearer ${apiKey}`,
238
- "Content-Type": "application/json",
239
- },
224
+ headers: buildHeaders(apiKey),
240
225
  });
241
226
  await handleResNotOk(res);
242
227
  const data = await res.json();
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { generateLinkToGrowthBook, handleResNotOk, paginationSchema, fetchWithRateLimit, fetchWithPagination, } from "../utils.js";
2
+ import { generateLinkToGrowthBook, handleResNotOk, paginationSchema, fetchWithRateLimit, fetchWithPagination, buildHeaders, } from "../utils.js";
3
3
  export function registerMetricsTools({ server, baseApiUrl, apiKey, appOrigin, }) {
4
4
  /**
5
5
  * Tool: get_metrics
@@ -27,18 +27,12 @@ export function registerMetricsTools({ server, baseApiUrl, apiKey, appOrigin, })
27
27
  let res;
28
28
  if (metricId.startsWith("fact__")) {
29
29
  res = await fetchWithRateLimit(`${baseApiUrl}/api/v1/fact-metrics/${metricId}`, {
30
- headers: {
31
- Authorization: `Bearer ${apiKey}`,
32
- "Content-Type": "application/json",
33
- },
30
+ headers: buildHeaders(apiKey),
34
31
  });
35
32
  }
36
33
  else {
37
34
  res = await fetchWithRateLimit(`${baseApiUrl}/api/v1/metrics/${metricId}`, {
38
- headers: {
39
- Authorization: `Bearer ${apiKey}`,
40
- "Content-Type": "application/json",
41
- },
35
+ headers: buildHeaders(apiKey),
42
36
  });
43
37
  }
44
38
  await handleResNotOk(res);
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { handleResNotOk, paginationSchema, fetchWithRateLimit, fetchWithPagination, } from "../utils.js";
2
+ import { handleResNotOk, paginationSchema, fetchWithRateLimit, fetchWithPagination, buildHeaders, } from "../utils.js";
3
3
  export function registerSdkConnectionTools({ server, baseApiUrl, apiKey, }) {
4
4
  /**
5
5
  * Tool: get_sdk_connections
@@ -85,10 +85,7 @@ export function registerSdkConnectionTools({ server, baseApiUrl, apiKey, }) {
85
85
  if (!environment) {
86
86
  try {
87
87
  const res = await fetchWithRateLimit(`${baseApiUrl}/api/v1/environments`, {
88
- headers: {
89
- Authorization: `Bearer ${apiKey}`,
90
- "Content-Type": "application/json",
91
- },
88
+ headers: buildHeaders(apiKey),
92
89
  });
93
90
  await handleResNotOk(res);
94
91
  const data = await res.json();
@@ -115,10 +112,7 @@ export function registerSdkConnectionTools({ server, baseApiUrl, apiKey, }) {
115
112
  try {
116
113
  const res = await fetchWithRateLimit(`${baseApiUrl}/api/v1/sdk-connections`, {
117
114
  method: "POST",
118
- headers: {
119
- Authorization: `Bearer ${apiKey}`,
120
- "Content-Type": "application/json",
121
- },
115
+ headers: buildHeaders(apiKey),
122
116
  body: JSON.stringify(payload),
123
117
  });
124
118
  await handleResNotOk(res);
package/server/utils.js CHANGED
@@ -56,6 +56,62 @@ export function getAppOrigin() {
56
56
  userAppOrigin = userAppOrigin?.trim().replace(/\/+$/, "");
57
57
  return `${userAppOrigin || defaultAppOrigin}`;
58
58
  }
59
+ /**
60
+ * Parses custom HTTP headers from environment variables with the prefix GB_HTTP_HEADER_*
61
+ * Converts environment variable names to proper HTTP header format:
62
+ * GB_HTTP_HEADER_X_TENANT_ID -> X-Tenant-ID
63
+ * GB_HTTP_HEADER_CF_ACCESS_TOKEN -> Cf-Access-Token
64
+ *
65
+ * Example usage:
66
+ * GB_HTTP_HEADER_X_TENANT_ID=abc123 -> { "X-Tenant-ID": "abc123" }
67
+ * GB_HTTP_HEADER_CF_ACCESS_TOKEN=<token> -> { "Cf-Access-Token": "<token>" }
68
+ */
69
+ export function getCustomHeaders() {
70
+ const customHeaders = {};
71
+ const headerPrefix = "GB_HTTP_HEADER_";
72
+ for (const [key, value] of Object.entries(process.env)) {
73
+ if (key.startsWith(headerPrefix) && value) {
74
+ // Extract the header name part after the prefix
75
+ const headerNamePart = key.slice(headerPrefix.length);
76
+ // Convert underscore-separated name to proper HTTP header format
77
+ // Example: X_TENANT_ID -> X-Tenant-ID, CF_ACCESS_TOKEN -> Cf-Access-Token
78
+ const headerName = headerNamePart
79
+ .split("_")
80
+ .map((part, index) => {
81
+ // Special handling for common prefixes like X, API, etc.
82
+ if (part.length === 1 || part === "API" || part === "ID") {
83
+ return part;
84
+ }
85
+ // Capitalize first letter, lowercase the rest
86
+ return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
87
+ })
88
+ .join("-");
89
+ customHeaders[headerName] = value;
90
+ }
91
+ }
92
+ return customHeaders;
93
+ }
94
+ /**
95
+ * Builds HTTP headers for GrowthBook API requests, merging required headers
96
+ * with any custom headers configured via GB_HTTP_HEADER_* environment variables.
97
+ *
98
+ * Custom headers are applied first, then required headers (Authorization, Content-Type)
99
+ * are added. This ensures required headers always take precedence.
100
+ *
101
+ * @param apiKey - The GrowthBook API key for authorization
102
+ * @param includeContentType - Whether to include Content-Type header (default: true)
103
+ * @returns Headers object ready for fetch requests
104
+ */
105
+ export function buildHeaders(apiKey, includeContentType = true) {
106
+ const headers = {
107
+ ...getCustomHeaders(),
108
+ Authorization: `Bearer ${apiKey}`,
109
+ };
110
+ if (includeContentType) {
111
+ headers["Content-Type"] = "application/json";
112
+ }
113
+ return headers;
114
+ }
59
115
  export function getDocsMetadata(extension) {
60
116
  switch (extension) {
61
117
  case ".tsx":
@@ -372,10 +428,7 @@ export async function fetchWithRateLimit(url, options, retries = 3) {
372
428
  */
373
429
  export async function fetchFeatureFlag(baseApiUrl, apiKey, featureId) {
374
430
  const res = await fetchWithRateLimit(`${baseApiUrl}/api/v1/features/${featureId}`, {
375
- headers: {
376
- Authorization: `Bearer ${apiKey}`,
377
- "Content-Type": "application/json",
378
- },
431
+ headers: buildHeaders(apiKey),
379
432
  });
380
433
  await handleResNotOk(res);
381
434
  const data = await res.json();
@@ -432,20 +485,14 @@ export async function fetchWithPagination(baseApiUrl, apiKey, endpoint, limit, o
432
485
  });
433
486
  }
434
487
  const res = await fetchWithRateLimit(`${baseApiUrl}${endpoint}?${queryParams.toString()}`, {
435
- headers: {
436
- Authorization: `Bearer ${apiKey}`,
437
- "Content-Type": "application/json",
438
- },
488
+ headers: buildHeaders(apiKey),
439
489
  });
440
490
  await handleResNotOk(res);
441
491
  return await res.json();
442
492
  }
443
493
  // Most recent behavior: fetch total count first, then calculate offset
444
494
  const countRes = await fetchWithRateLimit(`${baseApiUrl}${endpoint}?limit=1`, {
445
- headers: {
446
- Authorization: `Bearer ${apiKey}`,
447
- "Content-Type": "application/json",
448
- },
495
+ headers: buildHeaders(apiKey),
449
496
  });
450
497
  await handleResNotOk(countRes);
451
498
  const countData = await countRes.json();
@@ -464,10 +511,7 @@ export async function fetchWithPagination(baseApiUrl, apiKey, endpoint, limit, o
464
511
  });
465
512
  }
466
513
  const mostRecentRes = await fetchWithRateLimit(`${baseApiUrl}${endpoint}?${mostRecentQueryParams.toString()}`, {
467
- headers: {
468
- Authorization: `Bearer ${apiKey}`,
469
- "Content-Type": "application/json",
470
- },
514
+ headers: buildHeaders(apiKey),
471
515
  });
472
516
  await handleResNotOk(mostRecentRes);
473
517
  return await mostRecentRes.json();