💸 Explain.io — Airflow Cost Agent
Explain.io is a FinOps tool for Data Engineers. This lightweight Airflow plugin automatically attributes Google Cloud BigQuery compute costs to the exact DAG and Task that triggered them.
No more guessing which pipeline caused the bill spike. No code changes required.
✨ Features
- Zero Code Changes — Uses Airflow's Listener interface to automatically intercept all BigQuery task completions. You don't need to modify any DAG or add any callbacks.
- Zero Blast Radius — All network requests run asynchronously in a separate thread with strict timeouts. If the Explain.io API is unreachable, your DAG will still succeed. Exceptions are silently caught and logged.
- Instant Dashboard — View your pipeline costs, projected monthly spend, and heaviest DAGs at explain.io.
🔍 How It Works
┌──────────────┐ ┌─────────────────────┐ ┌──────────────────┐
│ Airflow │ │ Explain.io Agent │ │ Explain.io API │
│ (Scheduler) │────▶│ (Listener Plugin) │────▶│ (Backend) │
│ │ │ │ │ │
│ Task succeeds│ │ 1. Intercepts event │ │ Fetches BQ cost │
│ │ │ 2. Extracts job ID │ │ from GCP API │
│ │ │ 3. POSTs to API │ │ and stores it │
└──────────────┘ └─────────────────────┘ └──────────────────┘
The agent registers as an Airflow Listener plugin. On every task success, it checks if the task was a BigQuery operator, extracts the job ID via XCom, and sends it to the Explain.io API — all in a background thread.
📋 Requirements
| Dependency | Minimum Version | Why |
|---|---|---|
| Python | 3.9+ | Language baseline |
| Apache Airflow | 2.5.0+ | The agent uses Airflow's Listener API, which was introduced in 2.5. Earlier versions do not support auto-intercepting task completions via plugins. |
⚠️ Airflow 2.0–2.4 is not supported. The Listener plugin interface does not exist in those versions, so the agent cannot auto-intercept task completions without requiring DAG code changes.
🚀 Installation
Quick Start (Any Environment)
pip install explainio-airflow-agent
Then configure your API Key using Environment Variables. This ensures the agent works reliably without any pipeline code changes across all Airflow versions (2.x and 3.x).
| Variable | Required | Description |
|---|---|---|
EXPLAIN_IO_API_KEY |
✅ | Your API key. Set via AIRFLOW__... or .env. |
EXPLAIN_IO_API_URL |
❌ | API endpoint. Defaults to https://api.explain.io/api/v1/ingest |
Verify it's working:
airflow plugins
You should see cost_agent_plugin in the output.
📦 Installation by Platform
Self-Hosted: Docker (Recommended)
Don't use
_PIP_ADDITIONAL_REQUIREMENTS— it reinstalls packages on every container start and is only meant for quick testing.
Extend the official Airflow image with a Dockerfile:
FROM apache/airflow:3.1.6
USER airflow
RUN pip install --no-cache-dir explainio-airflow-agent
Then in your docker-compose.yaml, replace image: with build::
x-airflow-common:
&airflow-common
build: ./airflow # instead of: image: apache/airflow:3.1.6
environment:
EXPLAIN_IO_API_KEY: ${EXPLAIN_IO_API_KEY}
EXPLAIN_IO_API_URL: ${EXPLAIN_IO_API_URL}
# ... other env vars
Build and start:
docker compose build
docker compose up -d
Important: All Airflow components (scheduler, webserver, workers, triggerer) must use the same image. The shared
x-airflow-commonanchor ensures this automatically.
Self-Hosted: Bare Metal / virtualenv
Install directly into the Python environment where Airflow runs:
pip install explainio-airflow-agent
Set the environment variables:
export EXPLAIN_IO_API_KEY="your-api-key"
export EXPLAIN_IO_API_URL="https://api.explain.io/api/v1/ingest" # optional
If you run a multi-node setup (e.g., separate scheduler and workers), the package must be installed on every node.
AWS MWAA (Managed Workflows for Apache Airflow)
-
Create or update your
requirements.txtwith the Airflow constraints file:--constraint "https://raw.githubusercontent.com/apache/airflow/constraints-2.10.1/constraints-3.11.txt" explainio-airflow-agent==0.1.2
Adjust the constraints URL to match your MWAA Airflow version and Python version. See MWAA docs.
-
Upload
requirements.txtto your MWAA environment's S3 bucket. -
In the AWS Console → MWAA → your environment → Edit → point to the new
requirements.txtpath → Save. -
Set
EXPLAIN_IO_API_KEYand (optionally)EXPLAIN_IO_API_URLunder Environment variables → Airflow configuration options, using the prefixAIRFLOW__:- Key:
EXPLAIN_IO_API_KEY, Value:your-api-key
- Key:
-
Wait for the environment update to complete (can take 20–40 minutes).
Troubleshooting: Check CloudWatch Logs → Log group: airflow-{env-name}-requirements_install for pip errors.
Google Cloud Composer
Option A — Console UI
- Go to Environments → click your environment → PyPI Packages tab.
- Click Edit → Add Package.
- Enter:
- Package name:
explainio-airflow-agent - Version:
>=0.1.2
- Package name:
- Save. Composer will restart workers and the scheduler.
Option B — gcloud CLI
gcloud composer environments update YOUR_ENV_NAME \
--location YOUR_LOCATION \
--update-pypi-package "explainio-airflow-agent>=0.1.2"
Environment variables
Set via the Console UI (Environment Variables tab) or CLI:
gcloud composer environments update YOUR_ENV_NAME \
--location YOUR_LOCATION \
--update-env-variables EXPLAIN_IO_API_KEY=your-api-key
Astronomer (Astro CLI)
-
Add the package to
requirements.txtin your Astro project root:explainio-airflow-agent==0.1.2
-
Set environment variables in your
.envfile:EXPLAIN_IO_API_KEY=your-api-key -
Restart:
astro dev restart
-
Verify:
astro dev bash --scheduler "airflow plugins"
For Astro Cloud deployments, add the environment variables via the Astro UI under Deployments → Environment Variables.
⚙️ Configuration Reference
| Variable | Required | Default | Description |
|---|---|---|---|
EXPLAIN_IO_API_KEY |
✅ | — | Your Explain.io API key. If not set, the plugin logs an info message and becomes a no-op. |
EXPLAIN_IO_API_URL |
❌ | https://api.explain.io/api/v1/ingest |
Override the API endpoint (e.g., for self-hosted backends). |
🔧 Troubleshooting
Built-in Diagnostics
Run the built-in diagnostic check from the Airflow shell — no DAG deployment needed:
airflow shell
>>> from explainio_airflow_agent.client import diagnose
>>> diagnose()
This will check:
- ✅ Whether your API key is configured
- ✅ Whether your API URL is set correctly
- ✅ Whether the agent can reach the Explain.io backend and authenticate
Plugin not showing in airflow plugins
- Make sure the package is installed in the same Python environment as Airflow.
- Run
pip show explainio-airflow-agentto confirm it's installed. - Check Airflow scheduler logs for import errors.
Events not appearing in the dashboard
- Run
diagnose()first (see above). - Confirm
EXPLAIN_IO_API_KEYis set. Without it, the agent silently disables itself. - Check that
EXPLAIN_IO_API_URLpoints to a reachable endpoint. - Look for
[ExplainIO]log lines in the Airflow task logs.
AWS MWAA: Package fails to install
- Ensure the
--constraintline inrequirements.txtmatches your MWAA Airflow + Python version. - Check the
requirements_installlog stream in CloudWatch.
Supported Workloads
Explain.io perfectly isolates and captures BigQuery costs for the following workloads:
- Native Airflow Operators (e.g.,
BigQueryInsertJobOperator): Works instantly out-of-the-box. - dbt-core (via
BashOperator,KubernetesPodOperator, or Cosmos): Requires a 1-line configuration in yourprofiles.yml(see below). - Custom Scripts (e.g., raw Python/Bash): Supported if you manually inject the
airflow_dag_idandairflow_task_idlabels into your BigQuery job configuration.
dbt Support (Required Configuration)
Because dbt runs externally and does not natively report BigQuery job IDs back to Airflow, Explain.io requires you to configure dbt to pass context labels to BigQuery to guarantee 100% accurate cost attribution.
1. Configure dbt Labels
To prevent cost collisions when multiple DAGs run concurrently, inject the Airflow context variables into your BigQuery jobs by adding this to your profiles.yml:
# profiles.yml
my_bigquery_profile:
target: dev
outputs:
dev:
type: bigquery
# ... other config ...
labels:
airflow_dag_id: "{{ env_var('AIRFLOW_CTX_DAG_ID', '') }}"
airflow_task_id: "{{ env_var('AIRFLOW_CTX_TASK_ID', '') }}"
Explain.io perfectly isolates the costs to the correct DAG using these labels. If you skip this step, Explain.io will not be able to track your dbt costs.
2. Enable Per-Model Attribution (Optional)
For per-dbt-model cost breakdowns, add job labeling to your dbt_project.yml:
# dbt_project.yml
query-comment:
job-label: true
This tells dbt to label every BigQuery job with the model name (node_id). Explain.io reads these labels to show you exactly which dbt models are costing the most.
GCP Permissions
Your dedicated Explain.io service account needs the following roles:
- BigQuery Resource Viewer (
roles/bigquery.resourceViewer): To read BigQuery job metadata and labels (bigquery.jobs.listAll).
Security Note: Explain.io only requires metadata permissions. We cannot read your actual table data.
Grant it via:
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
--member="serviceAccount:YOUR_SA@YOUR_PROJECT.iam.gserviceaccount.com" \
--role="roles/bigquery.resourceViewer"
📄 License
MIT
Metadata
Release files for explainio-airflow-agent 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| explainio_airflow_agent-0.4.1.tar.gz | 13.0 kB | With provenance | Details for explainio_airflow_agent-0.4.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| explainio_airflow_agent-0.4.1-py3-none-any.whl | Python 3 | none | any | Details for explainio_airflow_agent-0.4.1-py3-none-any.whl |
Total release size: 23.2 kB
Release files / explainio_airflow_agent-0.4.1.tar.gz
| Download URL | explainio_airflow_agent-0.4.1.tar.gz |
|---|---|
| Size | 13.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
96b6bb613491751300de43462223540b50d820a51dc839b50bad9457068771c2
|
|
BLAKE2b-256 checksum How to use checksums |
1ec2c0008237f20a21d1c39977d0d03e1e7a87903505eeaaab33c16f7bc5d019
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 2, 2026.
Transparency logRelease files / explainio_airflow_agent-0.4.1-py3-none-any.whl
| Download URL | explainio_airflow_agent-0.4.1-py3-none-any.whl |
|---|---|
| Size | 10.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a22f877664575d87df87c9a80a82d4a20d6477f2ec51f6585d6e9f0f9016cb88
|
|
BLAKE2b-256 checksum How to use checksums |
b31830509cdf88bae558e9b65d34a4b8c67c3d13987ca459bd32540115da5952
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 2, 2026.
Transparency logSecurity for explainio-airflow-agent 0.4.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for explainio_airflow_agent-0.4.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 2, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.