pandas does not call Trends API. requests (or httpx) does. After the envelope arrives, body is a JSON string and must be parsed a second time. Then DataFrame.from_records is a local convenience. For get_time_series that inner value is an array of points with date, value, keyword, and source. For get_growth it is an object with a results list of windows. Docs: the API reference. Caps: pricing. This page is the frame, not an ETL scheduler. Google series notes: the Google Trends guide.

From inner results to a DataFrame

import json
import os
import pandas as pd
import requests

resp = requests.post(
    "https://api.trendsapi.ai/api",
    headers={
        "Authorization": f"Bearer {os.environ['TRENDSAPI_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "mode": "get_time_series",
        "source": "google search",
        "keyword": "bitcoin",
    },
    timeout=60,
)
resp.raise_for_status()
inner = json.loads(resp.json()["body"])
# get_time_series: body is a JSON array of {date, value, keyword, source}
frame = pd.DataFrame.from_records(inner)

A live pull on 2026-08-27 for bitcoin on google search returned weekly objects with those four keys (value 35 on 2026-08-22). If inner is a dict instead, the mode was not get_time_series.

DatetimeIndex on get_time_series

if "date" in frame.columns:
    frame["date"] = pd.to_datetime(frame["date"])
    frame = frame.set_index("date").sort_index()

A line chart wants that index. Forward-fill is a research choice, not an API field.

percent_growth rows as a small frame

growth_resp = requests.post(
    "https://api.trendsapi.ai/api",
    headers={
        "Authorization": f"Bearer {os.environ['TRENDSAPI_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "mode": "get_growth",
        "source": "google search",
        "keyword": "bitcoin",
        "percent_growth": ["3M", "12M", "YTD"],
    },
    timeout=60,
)
growth_inner = json.loads(growth_resp.json()["body"])
windows = pd.DataFrame.from_records(growth_inner.get("results") or [])

Three rows, not a weekly index. Join to the series on keyword, not on date.

Why volume and value need two axes

value is the 0-100 score. volume is an absolute when the source has it. ax.plot(frame["value"]) and ax.twinx().plot(frame["volume"]) is the honest chart. A single plot(["value", "volume"]) will lie. The product MCP server at https://api.trendsapi.ai/mcp can feed the same inner object into a notebook that already imports pandas.