airflow-provider-shopsavvy

April 7, 2026 · View on GitHub

Apache Airflow provider for ShopSavvy — gives your DAGs first-class operators, hooks, and sensors for scheduled price data ETL, data warehouse population, and price drop alerting.


What's included

ClassDescription
ShopSavvyHookConnection management and HTTP client for the ShopSavvy Data API
ShopSavvySearchOperatorSearch products; push results to XCom or write to a file / S3
ShopSavvyOffersOperatorFetch current offers for a product; output as JSON or CSV
ShopSavvyPriceHistoryOperatorPull price history for a date range; output as JSON or CSV
ShopSavvyPriceSensorPolls until a product's price drops to or below a target

Installation

pip install airflow-provider-shopsavvy

Optional extras:

# S3 output support
pip install "airflow-provider-shopsavvy[amazon]"

# Slack alert support (used in the price-monitor example DAG)
pip install "airflow-provider-shopsavvy[slack]"

Requires Apache Airflow 2.4+ and Python 3.8+.


Configure the Airflow connection

  1. Open the Airflow UI and go to Admin → Connections → + Add Connection.

  2. Fill in:

    FieldValue
    Connection Idshopsavvy_default (or any name you prefer)
    Connection Typeshopsavvy
    Hosthttps://data.shopsavvy.com/v1 (leave blank to use the default)
    PasswordYour ShopSavvy API key
  3. Save.

Environment variable fallback

If no Airflow connection is configured, the hook reads SHOPSAVVY_API_KEY from the environment:

export SHOPSAVVY_API_KEY="ss_live_your_key_here"

Quick start

Search for products

from shopsavvy_provider.operators.search import ShopSavvySearchOperator

search = ShopSavvySearchOperator(
    task_id="search_headphones",
    query="sony wh-1000xm5",
    limit=10,
)

Results are pushed to XCom under the key search_results and returned from execute().

Fetch current offers

from shopsavvy_provider.operators.offers import ShopSavvyOffersOperator

offers = ShopSavvyOffersOperator(
    task_id="get_offers",
    identifier="B0CHBNXN1G",       # ASIN, barcode, URL, model number, or name
    output_path="/data/offers.json",
    output_format="json",
)

Pull price history

from shopsavvy_provider.operators.price_history import ShopSavvyPriceHistoryOperator

history = ShopSavvyPriceHistoryOperator(
    task_id="get_price_history",
    identifier="B0CHBNXN1G",
    start_date="{{ macros.ds_add(ds, -30) }}",  # Airflow template macros work here
    end_date="{{ ds }}",
    output_path="/data/history.csv",
    output_format="csv",
)

Wait for a price drop

from shopsavvy_provider.sensors.price import ShopSavvyPriceSensor

wait = ShopSavvyPriceSensor(
    task_id="wait_for_price_drop",
    identifier="B0CHBNXN1G",
    target_price=249.99,
    retailer="amazon.com",     # optional — watches all retailers if omitted
    poke_interval=3600,        # check every hour
    timeout=60 * 60 * 24 * 7, # give up after 7 days
    mode="reschedule",
)

When the sensor fires, it pushes matching offers to XCom under matching_offers.

Write results to S3

Any operator that accepts output_path also accepts S3 URIs:

ShopSavvyOffersOperator(
    task_id="offers_to_s3",
    identifier="B0CHBNXN1G",
    output_path="s3://my-bucket/shopsavvy/{{ ds }}/offers.json",
    aws_conn_id="aws_default",   # your AWS connection
)

Requires pip install "airflow-provider-shopsavvy[amazon]".


Example DAGs

Two ready-to-use example DAGs are included in the package:

DAGFileDescription
shopsavvy_daily_price_etlexample_dags/daily_price_etl.pyRuns daily, fetches current offers and 30-day price history for a list of tracked products, writes JSON/CSV
shopsavvy_product_price_monitorexample_dags/product_price_monitor.pyHourly sensor-driven pipeline that fires a Slack alert and saves a snapshot when a target price is met

Copy them to your DAGs folder or point Airflow's dags_folder at the package's example_dags/ directory to try them out:

# airflow.cfg
[core]
dags_folder = /path/to/shopsavvy_provider/example_dags

Use your own connection ID

All components accept a conn_id parameter so you can manage multiple API keys:

ShopSavvySearchOperator(
    task_id="search",
    query="laptops",
    conn_id="shopsavvy_prod",   # use a non-default connection
)

API

ShopSavvyHook(conn_id="shopsavvy_default", timeout=30)

MethodParametersDescription
get_conn()Returns a configured requests.Session
search_products(query, limit, offset)query requiredSearch for products
get_current_offers(identifier, retailer, format)identifier requiredCurrent offers for a product
get_price_history(identifier, start_date, end_date, retailer, format)identifier, start_date, end_date requiredPrice history over a date range

ShopSavvySearchOperator

ParameterTypeDefaultDescription
querystrrequiredSearch term
limitint20Max results (max 100)
offsetint0Pagination offset
output_pathstrNoneLocal or S3 path for JSON output
aws_conn_idstraws_defaultAWS connection for S3 writes
push_to_xcomboolTruePush results to XCom
conn_idstrshopsavvy_defaultShopSavvy connection ID

ShopSavvyOffersOperator

ParameterTypeDefaultDescription
identifierstrrequiredBarcode, ASIN, URL, model number, or product name
retailerstrNoneFilter to one retailer
output_pathstrNoneLocal or S3 path
output_formatstrjsonjson or csv
aws_conn_idstraws_defaultAWS connection for S3 writes
push_to_xcomboolTruePush results to XCom
conn_idstrshopsavvy_defaultShopSavvy connection ID

ShopSavvyPriceHistoryOperator

ParameterTypeDefaultDescription
identifierstrrequiredBarcode, ASIN, URL, model number, or product name
start_datestrrequiredYYYY-MM-DD; supports Airflow template macros
end_datestrrequiredYYYY-MM-DD; supports Airflow template macros
retailerstrNoneFilter to one retailer
output_pathstrNoneLocal or S3 path
output_formatstrjsonjson or csv
aws_conn_idstraws_defaultAWS connection for S3 writes
push_to_xcomboolTruePush results to XCom
conn_idstrshopsavvy_defaultShopSavvy connection ID

ShopSavvyPriceSensor

ParameterTypeDefaultDescription
identifierstrrequiredBarcode, ASIN, URL, model number, or product name
target_pricefloatrequiredFires when any offer price is ≤ this value
retailerstrNoneLimit check to one retailer
conn_idstrshopsavvy_defaultShopSavvy connection ID
poke_intervalintAirflow defaultSeconds between pokes
timeoutintAirflow defaultMax wait in seconds
modestrpokepoke or reschedule

Running the tests

pip install "airflow-provider-shopsavvy[dev]"
pytest tests/ -v

Or use the included script:

bash test.sh

License

MIT — see LICENSE.