Go uses net/http and encoding/json. The URL is POST https://api.trendsapi.ai/api. The envelope is statusCode plus a string body. That string must be parsed a second time. A first-pass struct that types Body as another struct will fail to decode. context.WithTimeout is the request deadline, not a scrape cookie. source steam wants a store display name. Docs: the API reference. Caps: pricing. Steam names: the Steam guide.
Two structs for the envelope
type envelope struct {
StatusCode int `json:"statusCode"`
Body string `json:"body"`
}
type growthRow struct {
Period string `json:"period"`
Growth float64 `json:"growth"`
RecentValue float64 `json:"recent_value"`
Status string `json:"status"`
}
Add fields as the inner object uses them. Do not invent a gprop field.
json.Unmarshal body as string first
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
payload, _ := json.Marshal(map[string]any{
"mode": "get_growth",
"source": "steam",
"keyword": "Elden Ring",
"percent_growth": []string{"3M"},
})
req, _ := http.NewRequestWithContext(ctx, http.MethodPost, "https://api.trendsapi.ai/api", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+os.Getenv("TRENDSAPI_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode >= 300 {
log.Fatalf("http %d", resp.StatusCode)
}
var env envelope
if err := json.NewDecoder(resp.Body).Decode(&env); err != nil {
log.Fatal(err)
}
var inner map[string]any
if err := json.Unmarshal([]byte(env.Body), &inner); err != nil {
log.Fatal(err)
}
Elden Ring is a labeled sample. First store search result wins, so the name must be unambiguous. A worker that fans out sources should share one http.Client and cancel siblings on the first 429.
context.WithTimeout
The context is the request deadline. It is not a Google tz. On 429, stop the worker and read pricing.
table-driven source tests
A test table of {source, keyword} pairs (steam/Elden Ring, google search/bitcoin) catches a typo in source before production. t.Parallel() plus one client is enough. Log StatusCode from the envelope on every test. A 200 with an inner error is still a completed call. The product MCP server at https://api.trendsapi.ai/mcp is not a Go import.