fetch can POST JSON. That is not permission to paste a Trends API key into a <script> tag. The call shape is POST https://api.trendsapi.ai/api with a Bearer header. The envelope's body is a JSON string and must be parsed a second time. A page that needs a chart should fetch a same-origin route that holds the key. Docs: the API reference. Caps: pricing. Live Google board notes: the Google Trends guide.

fetch in a page, not Node

// Runs on a server or a trusted same-origin route. Not in a public bundle.
export async function trendingNow() {
  const resp = await fetch("https://api.trendsapi.ai/api", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.TRENDSAPI_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      mode: "get_top_trends",
      type: "Google Trends",
      limit: 10,
    }),
  });
  if (!resp.ok) {
    throw new Error(`http ${resp.status}`);
  }
  const outer = await resp.json();
  return JSON.parse(outer.body);
}

process.env here assumes a bundler that only runs on the server. In a raw browser script that object does not exist. That is a hint the key is in the wrong place.

Where the key must not live

Public JS, GitHub gists, CodePen, and client-side .env files that ship to the CDN are all leaks. Rotate the key if it was pasted into a gist. Issue a new one from the account flow. Limits: pricing.

response.json then JSON.parse on body

outer.statusCode is the inner status. outer.body is text. JSON.parse(outer.body) yields the board rows or the series. A UI that JSON.stringify(outer) into a <pre> is still showing the unparsed string.

CORS and why a browser tab is the wrong host

A local HTML file opening api.trendsapi.ai will hit CORS and a leaked-key problem at once. The fix is not a browser plugin. The fix is a 20-line route on the site the user is already on. The product MCP server at https://api.trendsapi.ai/mcp is for agents, not for file://.