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.