Ruby talks to POST https://api.trendsapi.ai/api with net/http. Auth is a Bearer header. The envelope field body is a JSON string and must be parsed a second time. ENV.fetch fails before the POST if the token is missing. Faraday is optional. source amazon takes a shopper phrase, not an ASIN. Short Amazon windows can collapse when the stored series is coarse. Docs: the API reference. Caps: pricing. Amazon phrases: the Amazon guide.
Net::HTTP::Post
require "json"
require "net/http"
require "uri"
uri = URI("https://api.trendsapi.ai/api")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.read_timeout = 60
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("TRENDSAPI_API_KEY")}"
req["Content-Type"] = "application/json"
req.body = {
mode: "get_growth",
source: "amazon",
keyword: "air fryer",
percent_growth: ["1M", "3M"],
}.to_json
res = http.request(req)
raise "http #{res.code}" unless res.is_a?(Net::HTTPSuccess)
air fryer is a labeled sample. Short Amazon windows can collapse if the stored series is coarse. 1M is the safer first preset.
JSON.parse twice
outer = JSON.parse(res.body)
inner = JSON.parse(outer.fetch("body"))
fetch raises if body is missing. inner["results"] is the growth list on this mode. A series mode returns a JSON array of points instead. JSON.parse(..., symbolize_names: true) is a local taste. The two-step parse stays either way.
ENV.fetch
.env via dotenv is fine in development. Production should inject the variable. Do not print it in a Sidekiq log line.
Faraday is optional
# Faraday.post("https://api.trendsapi.ai/api") do |req|
# req.headers["Authorization"] = "Bearer #{ENV.fetch("TRENDSAPI_API_KEY")}"
# req.headers["Content-Type"] = "application/json"
# req.body = payload.to_json
# end
Same envelope. Same second parse. A 429 is this product's limit. Read pricing. Sidekiq jobs should mark the retry as a plan or burst event, not as a random timeout. The product MCP server at https://api.trendsapi.ai/mcp is not a Ruby gem.