cron is a timer. It is not a workflow engine. A crontab line starts curl or a Python file that POSTs https://api.trendsapi.ai/api. The envelope body is a JSON string and must be parsed in the script. No canvas. No DAG UI. When retries and a warehouse load appear, move the same POST to Airflow. Fields: the API reference. Caps: pricing.
crontab starts the client
0 6 * * * flock -n /tmp/trendsapi.lock /usr/local/bin/trends_pull.sh
The shell script reads TRENDSAPI_API_KEY and posts:
curl -sS -X POST https://api.trendsapi.ai/api \
-H "Authorization: Bearer $TRENDSAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"mode":"get_growth","source":"google search","keyword":"bitcoin","percent_growth":["12M"]}'
flock -n exits if yesterday is still running. Two overlapping 200s are two lookups. Official usage notes count successful POSTs. Header shape is on authentication.
Do not put the token in the -d JSON. Do not put it on the crontab line.
The script parses body
import json, os, pathlib, requests
resp = requests.post(
"https://api.trendsapi.ai/api",
headers={"Authorization": f"Bearer {os.environ['TRENDSAPI_API_KEY']}"},
json={"mode": "get_growth", "source": "google search", "keyword": "bitcoin", "percent_growth": ["12M"]},
timeout=60,
)
resp.raise_for_status()
inner = json.loads(resp.json()["body"])
pathlib.Path("/var/lib/trends/bitcoin.json").write_text(json.dumps(inner))
jq can do fromjson on .body if the job stays on curl. Writing the envelope file as if it were rows produces a one-column string. The second parse is on parse the body. Log the HTTP status next to the keyword. A 401 is the environment key. A 429 is the cap, not a bad source string.
When to leave cron
A keyword list that needs per-task retries, a pool, and a BigQuery load is a DAG. Keep cron for one host and a handful of terms. 429 handling is sleep-and-exit, not a tight loop. Error text sits on errors.