A dbt Python model is a function that POSTs to https://api.trendsapi.ai/api with a Bearer key, then parses body a second time because that field is a JSON string. The same contract covers get_growth, get_time_series, and get_top_trends. On 2026-09-03 the python source last complete week for dbt-core was 2026-08-17 at score 74.6 and 21,779,634 downloads. get_growth still reported recent_date 2026-08-24 at 22.6 / 6,654,275, the same incomplete-tip pattern as Dagster, Prefect, and Airflow. Peak week in the series is 2026-05-11 at 100.0 / 29,149,279, not the lagging tip. A weekly dbt run of four PyPI keywords plus two Google Search keywords stays inside the 100-request free tier. Daily polling of that set does not.

How does a dbt Python model call Trends API?

The HTTP shape is one POST. Mode, source, and keyword sit in the JSON body. Auth is Authorization: Bearer. dbt adds materialization and a job schedule around that call. Python models need a warehouse that runs Python (Snowflake, BigQuery Spark, or Databricks). The warehouse must allow outbound HTTPS to api.trendsapi.ai.

POST https://api.trendsapi.ai/api
Authorization: Bearer <api_key>
Content-Type: application/json

{"mode":"get_growth","source":"python","keyword":"dbt-core","percent_growth":["7D","30D","12M"]}
import json
import os
import urllib.request

import pandas as pd


API = "https://api.trendsapi.ai/api"


def get_growth(keyword: str, source: str) -> dict:
    payload = {
        "mode": "get_growth",
        "source": source,
        "keyword": keyword,
        "percent_growth": ["7D", "30D", "12M"],
    }
    key = os.environ["TRENDSAPI_API_KEY"]
    req = urllib.request.Request(
        API,
        data=json.dumps(payload).encode("utf-8"),
        headers={
            "Authorization": f"Bearer {key}",
            "Content-Type": "application/json",
        },
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=60) as resp:
        envelope = json.loads(resp.read().decode("utf-8"))
    if envelope.get("statusCode") != 200:
        raise RuntimeError(envelope)
    # body is a JSON string; parse it a second time
    return json.loads(envelope["body"])


def model(dbt, session):
    dbt.config(materialized="table")
    rows = []
    watchlist = [
        ("dbt-core", "python"),
        ("dbt-snowflake", "python"),
        ("snowflake-connector-python", "python"),
        ("apache-airflow", "python"),
        ("dbt", "google search"),
        ("snowflake", "google search"),
    ]
    for keyword, source in watchlist:
        body = get_growth(keyword, source)
        for row in body.get("results", []):
            rows.append(
                {
                    "keyword": keyword,
                    "source": source,
                    "period": row.get("period"),
                    "status": row.get("status"),
                    "recent_date": row.get("recent_date"),
                    "recent_value": row.get("recent_value"),
                    "recent_volume": row.get("recent_volume"),
                    "baseline_date": row.get("baseline_date"),
                    "baseline_value": row.get("baseline_value"),
                    "baseline_volume": row.get("baseline_volume"),
                    "growth": row.get("growth"),
                    "volume_growth": row.get("volume_growth"),
                }
            )
    return pd.DataFrame(rows)

Python model wiring follows the dbt Python models docs. The Dagster equivalent is an @asset with a Monday cron. See Dagster Trends API for that path. The Prefect equivalent is a @flow on Prefect Trends API. The Python client without dbt is on Python Trends API.

What did dbt-core PyPI return on 2026-09-03?

get_growth on source python and keyword dbt-core used recent_date 2026-08-24. That week scored 22.6 with 6,654,275 downloads. The 7D baseline week 2026-08-17 scored 74.6 with 21,779,634 downloads, growth -69.71 percent, volume growth -69.45 percent. The 14D baseline 2026-08-10 scored 81.1 / 23,659,609, growth -72.13 percent. The 30D baseline 2026-07-27 scored 75.8 / 22,107,362, growth -70.18 percent. The 3M baseline 2026-05-25 scored 81.1 / 23,646,578, growth -72.13 percent. The 12M baseline 2025-08-25 scored 29.1 / 8,550,027, growth -22.34 percent. YTD vs 2025-12-29 (50.2 / 14,689,406) was -54.98 percent.

get_time_series on the same source puts the 2026-08-24 week in context. Peak week was 2026-05-11 at 100.0 / 29,149,279. The 2026-08-17 week was 74.6 / 21,779,634. The 2026-08-10 week was 81.1 / 23,659,609. PyPI weekly tips often lag Google weekly sources by about a week, so treat 2026-08-24 as an incomplete tip, not a same-day match for Search week 2026-08-29. The drop from 21.78 million to 6.65 million looks like a lagging tip, not a product crash. Store recent_date next to the score.

A model that alerts on 7D volume growth below -25 percent would have fired on that incomplete tip. Gate the alert on recent_date being a complete week. Using 2026-08-17 vs 2026-08-10 as the last complete 7D window gives score growth -8.01 percent and volume growth from 23,659,609 to 21,779,634, about -7.95 percent. Against the 2025-08-25 12M baseline, that complete week is +156.36 percent on score and +154.73 percent on volume. YTD vs 2025-12-29 is +48.61 percent on score.

How do warehouse packages compare on the same python source?

Scores are 0-100 per keyword. Do not rank 22.6 against 51.1 as if they share a scale. Compare absolute recent_volume. All four rows below share the incomplete 2026-08-24 tip. The 7D baseline is the last complete week 2026-08-17.

keyword tip week tip score tip downloads complete week complete downloads 7D growth vs tip
dbt-core 2026-08-24 22.6 6,654,275 2026-08-17 21,779,634 -69.71%
dbt-snowflake 2026-08-24 51.1 1,397,224 2026-08-17 2,466,759 -44.99%
snowflake-connector-python 2026-08-24 47.6 24,620,304 2026-08-17 43,211,244 -45.16%
apache-airflow 2026-08-24 20.5 2,143,430 2026-08-17 4,464,631 -58.92%

snowflake-connector-python led on raw downloads (24,620,304 on the tip week, 43,211,244 on the complete week). dbt-core is next at 21,779,634 complete-week downloads even though its tip score is 22.6. dbt-snowflake is an adapter, not the core package: 2,466,759 complete-week downloads vs 21.78 million for dbt-core. apache-airflow is here as an orchestrator join trap. 4,464,631 complete-week downloads sit below the Snowflake connector and below dbt-core. Do not put Airflow on the warehouse-package chart. Prefect on the same python source scored 43.6 / 1,679,938 on the tip week vs 82.0 / 3,147,593 on 2026-08-17, 7D -46.83 percent. Dagster scored 69.6 / 1,849,133 vs 100.0 / 2,644,156, 7D -30.4 percent.

dbt-snowflake 12M vs 2025-08-25 was +0.99 percent (50.6 / 1,384,788). snowflake-connector-python 12M was +0.42 percent vs 47.4 / 24,560,427. Those long windows are close to flat once the incomplete tip is ignored. dbt-core 12M vs the same 2025-08-25 week is the outlier only if the model uses the 22.6 tip. Against the 2026-08-17 complete week it is +156.36 percent.

What does Google Search show against download volume?

Search weeks end 2026-08-29, five days after the latest PyPI week. Keyword dbt scored 28.0, 7D growth -22.22 percent vs 36.0 on 2026-08-22. 14D was -3.45 percent vs 29.0 on 2026-08-15. 30D was -12.5 percent vs 32.0 on 2026-08-01. 3M was -58.82 percent vs 68.0 on 2026-05-30. 12M was -22.22 percent vs 36.0 on 2025-08-30. YTD vs 23.0 on 2026-01-03 was +21.74 percent. The 3M Search drop lines up with the PyPI peak week of 2026-05-11. Join on keyword plus recent_date, not on score.

snowflake Search scored 29.0 with estimated volume 1,210,000. 7D growth -21.62 percent vs 37.0 / 1,540,000 on 2026-08-22. 14D was -27.5 percent vs 40.0. 30D was -38.3 percent vs 47.0. 3M was -61.33 percent vs 75.0. 12M was -51.67 percent vs 60.0. YTD was -27.5 percent vs 40.0. Volume on Search is derived from the trend value. Do not treat 1,210,000 as an independent measurement, and do not join it onto 43 million snowflake-connector-python downloads.

fivetran Search scored 18.0, 7D -35.71 percent vs 28.0. 30D was -43.75 percent vs 32.0. 12M was -52.63 percent vs 38.0. YTD vs 14.0 on 2026-01-03 was +28.57 percent. Search for the loader is quieter than dbt and snowflake on the same week. Store all three.

YouTube for dbt was 8.0 on 2026-08-29, 7D growth -20.0 percent vs 10.0 on 2026-08-22. 14D was +33.33 percent vs 6.0. 30D was +14.29 percent vs 7.0. 3M was -80.0 percent vs 40.0 on 2026-05-30. 12M was +60.0 percent vs 5.0. YTD was +100.0 percent vs 4.0. Video interest sat far below Search. Store both.

Google News for dbt was 33.0 vs 100.0 on the 7D window (-67.0 percent). 14D was -44.07 percent vs 59.0. 30D was -5.71 percent vs 35.0. 3M was +17.86 percent vs 28.0. 12M baselined at 0.0 and returned growth 999999. Print the raw 33.0 score. Do not store 999999 as a growth feature.

Wikipedia keyword dbt resolved as Dbt with a monthly grain. August 2026 scored 28.6 with 158 page views vs July 51.3 / 185, 30D -44.25 percent. 3M vs May 62.2 / 198 was -54.02 percent. 12M vs 2025-08-01 (47.9 / 181) was -40.29 percent. 7D and 14D returned collapsed_range. Keyword dbt Labs returned 404 no_data. 158 monthly views against 21.78 million PyPI downloads is a title-resolution miss, not a second signal. Skip Wikipedia for this keyword, or store it as a mismatch check.

Source fields for Search are on Google Trends docs.

How many requests does a weekly dbt run use?

Only HTTP 200 responses count against quota. Free tier 100 per month. Starter 5,000. Pro 25,000. Business 100,000.

The model above is 6 get_growth calls. Weekly Monday dbt Cloud cron is 6 × 4 = 24 in a 30-day month, plus retries. Two retries on a flaky call still stay under 100. Daily cron is 6 × 30 = 180 before retries, which exceeds free. Add a get_top_trends Google Trends board and the weekly total is 7 calls, about 28 per month.

get_growth with comma-separated sources can 504. Call one source per request, which is also how dbt retries stay scoped to a single source. A thin BigQuery SQL wrapper that only reads the table this model writes is on BigQuery Trends API. GitHub Actions cron without dbt is on GitHub Actions Trends API.

How should the model fail a run?

Raise after the second parse, not on the HTTP 200. A 200 with inner status error or a 404 no_data is a finding. The Wikipedia dbt Labs 404 is the example: the model should record data_unavailable and keep the rest of the watchlist. Fail the job only when a required source (here python for dbt-core) is missing, or when a stored rule fires after a complete week, such as dbt-core PyPI 7D volume growth below -25 percent on a week that is not the lagging tip.

Do not fire that rule on the 2026-08-24 vs 2026-08-17 7D window until the next python tip arrives. Mixing PyPI week 2026-08-24 with Search week 2026-08-29 without the dates is the usual join bug. A dated snapshot of a calendar-driven Search spike a warehouse job would have caught this week is lunar eclipse tonight Search vs YouTube.