Metadata-Version: 2.4
Name: datadog-radar
Version: 0.1.0
Summary: Observability FinOps scanner for Datadog — cost, usage, custom metric cardinality, and waste findings from one CLI command.
Author: sarimor
License: MIT
Project-URL: Homepage, https://github.com/sarimor/datadog-radar
Project-URL: Repository, https://github.com/sarimor/datadog-radar
Project-URL: Issues, https://github.com/sarimor/datadog-radar/issues
Keywords: datadog,finops,observability,cost-optimization,cloud-cost,monitoring,hyperscaler-radar
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: System Administrators
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: System :: Monitoring
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.28
Requires-Dist: rich>=13.0
Requires-Dist: typer>=0.9
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: responses>=0.23; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# datadog-radar

Observability FinOps scanner for Datadog. Pulls cost, usage, custom metric cardinality and configuration inventory from one CLI command, then flags the things that actually drive a Datadog bill.

Part of the **Hyperscaler Radar** suite.

---

## Install

```bash
pip install datadog-radar
```

## Auth

Datadog read endpoints need **two** credentials:

1. **API key** — Organization Settings → API Keys
2. **Application key** — Organization Settings → Application Keys

The Application key must belong to a user with `usage_read` and `billing_read`. Cost endpoints are **parent-org only** — a child org will return 403 on `cost` but still scan everything else.

```bash
export DD_API_KEY=<api-key>
export DD_APP_KEY=<app-key>
export DD_SITE=datadoghq.com   # optional; see sites below
```

Or pass `--api-key` / `--app-key` / `--site` inline.

### Supported sites

`datadoghq.com` (default) · `us3.datadoghq.com` · `us5.datadoghq.com` · `datadoghq.eu` · `ap1.datadoghq.com` · `ap2.datadoghq.com` · `ddog-gov.com` · `us2.ddog-gov.com`

---

## Usage

```bash
# Full scan — all domains, rich tables
datadog-radar scan

# Cost and custom metrics only
datadog-radar scan --domains cost,metrics

# EU org, six months of history
datadog-radar scan --site datadoghq.eu --months 6

# JSON to stdout
datadog-radar scan --output json

# JSON to a file plus per-resource CSVs for an SA review packet
datadog-radar scan --out-file report.json --csv-dir ./review/

# Sample more custom metrics (default 100)
datadog-radar scan --domains metrics --metric-limit 500

datadog-radar domains
datadog-radar version
```

### Domains

| Domain | What it scans |
|---|---|
| `cost` | Estimated, projected, historical and billable cost; sub-org breakdown |
| `usage` | Usage summary across all product families |
| `metrics` | Top custom metrics by hourly average; Metrics without Limits configuration |
| `attribution` | Active billing dimensions and monthly cost attribution |
| `inventory` | Hosts, monitors, log indexes, synthetics, RUM apps, dashboards, SLOs, orgs |

---

## Findings engine

The scan is the input; the findings are the point. Each one names the signal it fired on so you can verify it against the customer's contract before acting.

| Severity | Example finding |
|---|---|
| `high` | High-cardinality custom metrics dominating billed timeseries |
| `high` | Log indexes with no `daily_limit` — uncapped overage exposure |
| `high` | Hosts missing `env` / `service` / `team` — chargeback is impossible |
| `high` | Projected month-end cost running ahead of month-to-date pace |
| `medium` | Metrics without Limits never configured — every tag combo billed |
| `medium` | Indexes retained past 15 days that belong in Flex Logs or archive |
| `medium` | Indexes with no exclusion filter |
| `medium` | Browser synthetic tests running every 5 minutes or faster |
| `medium` | Monitors stuck in No Data — decommissioned infra, live config |
| `low` | Muted hosts still reporting and still billing |

Findings are sorted by severity and exported to `findings.csv` alongside the resource inventory.

---

## Example output

```
╭──────────────────────────────────────────────╮
│ Datadog Radar · Observability FinOps Scanner │
│ site: datadoghq.com                          │
╰──────────────────────────────────────────────╯
💰  Cost
┏━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ View       ┃ Org / Period   ┃      Total ┃
┡━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ estimated  │ Acme · 2026-07 │ $48,210.44 │
│ projected  │ Acme · 2026-07 │ $61,980.10 │
│ historical │ Acme · 2026-06 │ $45,102.00 │
└────────────┴────────────────┴────────────┘

🎯  FinOps findings
┏━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Sev     ┃ Category       ┃ Finding                             ┃
┡━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ HIGH    │ custom-metrics │ 2 high-cardinality custom metric(s)  │
│ HIGH    │ logs           │ 1 log index(es) with no daily quota  │
│ HIGH    │ attribution    │ 1 of 2 hosts missing attribution tags│
└─────────┴────────────────┴─────────────────────────────────────┘
```

---

## Python API

```python
from datadog_radar import DatadogClient
from datadog_radar.scanners import run_scan
from datadog_radar.findings import build_findings

client = DatadogClient()                     # reads DD_API_KEY / DD_APP_KEY / DD_SITE
results = run_scan(client, domains=["cost", "metrics"])
for f in build_findings(results):
    print(f["severity"], f["title"])
```

---

## Design notes

**Graceful degradation.** Every API call is wrapped in `_safe()`. A permission gap, a deprecated endpoint, or a child-org 403 degrades one row — the rest of the scan still completes and the gap is reported as an `info` finding.

**Retry.** 429 responses honour `X-RateLimit-Reset`; 5xx and network errors retry with linear backoff.

**Read-only.** Every call is a GET. Nothing is created, modified, or deleted.

---

## Hyperscaler Radar Suite

| Package | Target |
|---|---|
| [`aws-radar`](https://pypi.org/project/aws-radar/) | AWS |
| [`gcp-radar`](https://pypi.org/project/gcp-radar/) | Google Cloud |
| [`azure-radar`](https://pypi.org/project/azure-radar/) | Microsoft Azure |
| [`oci-radar`](https://pypi.org/project/oci-radar/) | Oracle Cloud |
| [`coreweave-radar`](https://pypi.org/project/coreweave-radar/) | CoreWeave |
| [`openai-radar`](https://pypi.org/project/openai-radar/) | OpenAI |
| [`aws-bedrock-radar`](https://pypi.org/project/aws-bedrock-radar/) | AWS Bedrock |
| [`datadog-radar`](https://pypi.org/project/datadog-radar/) | Datadog ← you are here |

---

## Publish

```bash
pip install build twine
python -m build
twine check dist/*
twine upload dist/*
```

## License

MIT © [sarimor](https://github.com/sarimor)
