APIFinOpsFOCUS v1.2Athena + PostgreSQL

FinOps Analytics — Full Reference

Quper's FinOps module is built entirely on the FOCUS v1.2 (FinOps Open Cost and Usage Specification) data model — the vendor-neutral open standard for cloud cost and usage data. All cost calculations, aggregations, and optimization signals derive from FOCUS-normalized columns, ensuring consistent semantics across cloud providers and billing systems.

FOCUS Data Model

FOCUS v1.2 standardises over 70 columns covering billing accounts, sub-accounts, resource identifiers, charge categories, commitment discounts, cost types, and usage quantities. Quper maps all raw cloud billing data into this schema before any analytics are performed.

Data Architecture — Silver → Gold Pipeline

Analytics flow through a two-layer warehouse architecture before reaching any API endpoint:

LayerTableGrainPurpose
Silver (raw)qup_focus_1_2_cost_usage_rawOne row per FOCUS recordVerbatim FOCUS data synced from Athena. No aggregation. Source of truth.
Gold (daily fact)qup_fact_cost_usage_dailyOne row per resource per dayPre-aggregated daily costs, usage, idle flags, tag governance, and performance metrics. All API responses read from here.
Configqup_config_idle_thresholdsOne row per threshold keyOperator-configurable thresholds for idle, over-provisioning, and off-hours detection.

FOCUS Column Mapping (Key Fields)

CategoryFOCUS Columns
Costbilled_cost, effective_cost, list_cost, contracted_cost
Usageconsumed_quantity, consumed_unit, pricing_quantity
Commitment Discountscommitment_discount_category, commitment_discount_id, commitment_discount_type, commitment_discount_status
Capacity Reservationscapacity_reservation_id, capacity_reservation_status
Pricinglist_unit_price, contracted_unit_price, pricing_currency_effective_cost
Resourceresource_id, resource_name, resource_type
Service & Regionservice_name, service_category, service_subcategory, region_name, region_id
Timebilling_period_start, billing_period_end, charge_period_start, charge_period_end
Accountsbilling_account_id, billing_account_name, sub_account_id, sub_account_name
Metadata (JSON)tags, sku_price_details, x_discounts

Silver Ingestion Algorithm

  1. 1
    Pull from Athena in batches

    Reads the FOCUS-normalised Athena table in batches of 1,000 rows. Column names are normalised case-insensitively — underscores, hyphens, and spaces are treated as equivalent to handle minor schema drift across cloud providers or export tools.

  2. 2
    Map 70+ FOCUS columns

    All cost, usage, commitment, capacity reservation, pricing, resource, service, and tag columns are written verbatim. Tags and pricing detail fields are stored as JSON blobs.

  3. 3
    Write with provenance metadata

    Each batch is stamped with a batch_id (UUID), loaded_at timestamp, and source_file reference. An optional dev-DB mirror writes in parallel for testing without touching production data.

Gold Aggregation Algorithm

  1. 1
    Group by resource + day

    Silver rows are grouped by billing_account_id, provider_name, invoice_id, region_name, service_name, charge_category, resource_id, and charge_period_start truncated to date.

  2. 2
    Extract workload ID from ARN

    When resource_id follows the AWS ARN pattern (arn:partition:service:region:account:resource), workload_id is the first path segment after the final slash, falling back to the seventh colon-delimited segment.

  3. 3
    Determine cost_type with commitment priority

    If any row in the group carries a non-null commitment_discount_type (e.g. Reserved Instance, Savings Plan), that value wins. Otherwise: charge_category Tax → Tax, Credit → Credit, Refund → Refund, all else → OnDemand.

  4. 4
    Aggregate cost and usage

    cost_amount = SUM(effective_cost). amortized_cost = SUM(effective_cost). list_cost = SUM(list_cost). usage_quantity = SUM(consumed_quantity). All aggregations COALESCE null to 0.

  5. 5
    Tag governance flag

    required_tag_missing_flag = TRUE when the aggregated tags JSON is null, empty string, '{}', or the literal string 'null'. No custom tag schema is required.

  6. 6
    Initialise idle fields

    idle_flag = FALSE and idle_cost = 0 as defaults. These are back-filled by the Idle Spend enrichment job after the gold table is populated.

Story 1 — Unit Cost & Efficiency

Unit cost measures what each unit of consumed resource costs. Efficiency measures whether cost is growing faster or slower than usage — the ratio of the two growth rates.

Unit Cost
unit_cost = total_cost ÷ total_usage
NULLIF(total_usage, 0) prevents division-by-zero. NULL returned for zero-usage groups.
Cost Growth %
Δcost% = (current_cost ÷ previous_cost − 1) × 100
NULL when previous_cost = 0 (new resource, no prior period)
Usage Growth %
Δusage% = (current_usage ÷ previous_usage − 1) × 100
NULL when previous_usage = 0
Unit Cost Change %
Δunit_cost% = (current_unit_cost ÷ previous_unit_cost − 1) × 100
Positive = cost rising faster than usage volume
Efficiency Ratio
eff = CLAMP(−10, 10, Δcost% ÷ Δusage%)
When |Δusage%| < 1 %, use Δunit_cost% instead. Capped at ±10 to suppress extreme values from tiny denominators.

Computation Steps

  1. 1
    Build current-period CTE

    Group the gold table by the requested dimension over the current date window. Sum cost_amount or amortized_cost depending on the cost_basis parameter.

  2. 2
    Build previous-period CTE

    Identical grouping over the preceding equivalent window. Both windows are always the same number of days for a fair comparison.

  3. 3
    FULL OUTER JOIN on group key

    Ensures resources present in only one period are still included — current-only items show zero previous cost; previous-only items show zero current cost.

  4. 4
    Apply formulas and rank

    Unit cost, growth percentages, and efficiency ratio are computed per row. Results are ordered by current_cost descending.

Cost Basis Options

cost_basisFOCUS ColumnBest for
amortized (default)effective_costBudgeting and allocation — commitment discounts spread evenly over the term
actualbilled_costInvoice reconciliation — unblended on-demand cost as billed
listlist_costSavings analysis — shows full discount vs. public list price

Story 2 — Unit Cost Time-Series

The time-series variant computes unit cost and efficiency for every day in the requested window, enabling trend charts. It uses SQL window functions rather than self-joins to avoid a full table scan per day.

  1. 1
    Daily aggregation

    Group the gold table by usage_date and the chosen dimension to produce daily total_cost and total_usage per group key.

  2. 2
    LAG window function

    LAG(total_cost) OVER (PARTITION BY group_key ORDER BY usage_date) retrieves the previous day's value within the same group in a single sorted pass. Applied identically for usage.

  3. 3
    Per-day formula application

    Unit cost, cost_growth_pct, usage_growth_pct, and efficiency_ratio are computed from the current and lagged values using the same formulas as Story 1. Days with no prior day have NULL growth values.

Story 3 — Period Comparison

Compares two arbitrary time windows of equal length and surfaces absolute and relative deltas per group. Two comparison modes:

ModePrevious window definition
previousThe N days immediately preceding the current window (e.g. Mar 1–14 vs Feb 15–28)
mtdThe same calendar day span in the prior month (e.g. Mar 1–14 vs Feb 1–14)
Absolute Delta
Δ$ = current_cost − previous_cost
Positive = cost increased. Negative = cost decreased.
Relative Delta %
Δ% = (current_cost − previous_cost) ÷ previous_cost × 100
NULL when previous_cost = 0

Resources appearing only in the current period carry has_previous_data = false. Resources that existed only in the prior period appear with current_cost = 0 and a fully negative delta, surfacing deleted or decommissioned workloads.

Story 4 — Optimization Indicators

Four independent indicators, each targeting a distinct waste category. Thresholds are read from qup_config_idle_thresholds at query time — changing a threshold takes effect immediately without a redeployment.

4a — Idle Spend

The gold table's idle_flag and idle_cost are pre-computed by a background enrichment job using the idle_cpu_threshold and idle_query_threshold config values. The endpoint aggregates them:

Idle Spend %
idle_pct = SUM(idle_cost) ÷ SUM(cost_amount) × 100
Guard: only computed when SUM(cost_amount) > 0

4b — Overprovisioning

A resource group is over-provisioned only when all three performance dimensions are simultaneously below threshold. The SQL aggregates mean values; the flag is applied in Python after the query:

CPU condition
avg_cpu < overprovision_cpu_threshold
Default: 20 %
Queue condition
avg_queue_time_sec < overprovision_queue_time_threshold
Default: 5 s
Latency condition
avg_latency_p95_ms < overprovision_latency_threshold
Default: 100 ms

overprovision_flag = cpu_ok AND queue_ok AND latency_ok— all three must hold. Low CPU with high queue time signals I/O saturation, not over-provisioning, and is not flagged.

4c — Usage Concentration (Top-N Risk)

  1. 1
    Compute per-group usage share

    usage_pct = group_usage ÷ total_usage × 100, computed against a grand-total CTE to avoid correlated subqueries.

  2. 2
    Rank by usage descending

    ROW_NUMBER() OVER (ORDER BY usage_quantity DESC) assigns rank 1 to the largest consumer.

  3. 3
    Compute top-N concentration

    top_n_pct = SUM(usage_pct WHERE rank ≤ concentration_top_n). Default top_n = 3. A high percentage (e.g. > 80 %) signals a concentration risk where one team or service dominates cloud spend.

Story 5 — Baseline & Z-Score Anomaly Detection

Anomaly detection uses a rolling statistical baseline to distinguish genuine cost spikes from normal variation. The baseline window deliberately excludes the two most recent days so that the current anomaly cannot contaminate its own reference period.

Baseline Window: D-16 to D-3

For any day D, the mean and standard deviation are computed from the 14 days ending two days before D. A minimum of 7 data points are required; fewer returns NULL z-scores, preventing false positives during initial data collection.
  1. 1
    Daily aggregation

    Group gold table by usage_date and chosen dimension to produce daily_cost and daily_usage per group key.

  2. 2
    Rolling window function

    SQL window: ROWS BETWEEN 16 PRECEDING AND 3 PRECEDING, partitioned by group_key, ordered by usage_date. AVG() and STDDEV() are computed within this window in a single sorted pass.

  3. 3
    Insufficient-data guard

    Baseline mean and stddev are emitted as NULL when window_size < 7. This avoids anomaly flags when fewer than a week of history exists.

  4. 4
    Compute Z-score

    Z = (daily_value − baseline_mean) ÷ stddev, computed independently for cost and usage. NULL when stddev = 0 (no variation in the baseline window).

  5. 5
    Flag anomalies

    is_anomaly = |Z| > z_threshold. Default z_threshold = 2.0 (≈ 95th percentile under a normal distribution). Configurable 0.5 – 5.0. A threshold of 2.0 means a day must sit more than two standard deviations from the 14-day mean to be flagged.

Z-Score
Z = (daily_value − baseline_mean) ÷ baseline_stddev
Baseline = 14-day rolling average, D-16 to D-3
Anomaly flag
is_anomaly = |Z| > z_threshold (default 2.0)
Applies to both cost and usage dimensions independently

Story 6 — Stability & Volatility

Volatility measures how erratically cost or usage moves over a rolling window. The metric is the Coefficient of Variation (CV) — a normalised, unitless measure of dispersion that is comparable across resources with very different cost scales.

Spend CV
cv_spend = stddev(daily_cost) ÷ mean(daily_cost)
Rolling window, default 14 days, minimum 3 points required
Usage CV
cv_usage = stddev(daily_usage) ÷ mean(daily_usage)
NULL when mean < 0.001 to avoid division by effectively-zero values
Stability labelCV rangeRecommendation
StableCV < 0.15Safe for long-term Reserved Instance or Savings Plan commitments
Moderate0.15 ≤ CV < 0.40Review before committing to multi-year pricing
Volatile0.40 ≤ CV < 0.75On-demand or 1-year commitments preferred
Highly VolatileCV ≥ 0.75Investigate workload scheduling or auto-scaling patterns

Story 7 — Advanced Optimization Metrics

7a — Idle Days

Uses the pre-computed idle_flag column in the gold daily table to count days of zero meaningful activity and the cost incurred during those days.

Idle Day %
idle_pct = COUNT(days WHERE idle_flag) ÷ total_days × 100

7b — Zero-Query Cost Days

Identifies Redshift cluster-days where compute cost was incurred but zero queries were processed — the strongest signal for completely unused capacity.

Zero-Query Cost %
zero_query_pct = SUM(cost WHERE query_count = 0) ÷ SUM(cost) × 100

7c — Off-Hours Spend

Computed from the silver table because hourly granularity (charge_period_start hour) is only available at that layer. The gold table aggregates to day, losing intra-day hour information.

Off-hours classification
off_hours = hour(charge_period_start) < bh_start OR hour ≥ bh_end
Default: bh_start = 9, bh_end = 18 (UTC). Configurable per tenant.
Off-Hours %
off_hours_pct = SUM(effective_cost WHERE off_hours) ÷ SUM(effective_cost) × 100

7d — Underutilised Clusters

Distinguishes under-used clusters (low but non-zero CPU) from idle clusters (zero CPU). Only cluster-days with a measured CPU reading above zero are considered.

Underutilisation %
underutil_pct = days(0 < cpu_avg < threshold) ÷ days(cpu_avg > 0) × 100
Default underutil_cpu_threshold = 30 %

Configuration Thresholds

KeyDefaultUnitUsed by
idle_cpu_threshold5%Idle flag enrichment — CPU below this = idle day
idle_query_threshold0countIdle flag — queries at or below this = idle day
overprovision_cpu_threshold20%Story 4b overprovisioning
overprovision_queue_time_threshold5secondsStory 4b overprovisioning
overprovision_latency_threshold100msStory 4b overprovisioning
concentration_top_n3countStory 4c usage concentration
business_hours_start9UTC hour 0–23Story 7c off-hours spend
business_hours_end18UTC hour 0–23Story 7c off-hours spend
underutil_cpu_threshold30%Story 7d underutilisation

API Endpoints

GET
/api/v1/finops/unit-cost

Unit cost + efficiency ratio for a dimension, two periods

GET
/api/v1/finops/unit-cost/timeseries

Daily unit cost time-series with lag-based growth rates

GET
/api/v1/finops/period-compare

FULL OUTER JOIN comparison of two arbitrary date windows

GET
/api/v1/finops/optimization/idle

Idle spend from pre-computed gold idle_flag

GET
/api/v1/finops/optimization/overprovision

Overprovisioning flags (CPU + queue + latency)

GET
/api/v1/finops/optimization/concentration

Top-N usage concentration risk

GET
/api/v1/finops/baseline

Rolling baseline + Z-score anomaly detection

GET
/api/v1/finops/stability

Coefficient of Variation volatility classification

GET
/api/v1/finops/advanced/idle-days

Count and cost of idle days per group

GET
/api/v1/finops/advanced/zero-query

Days and cost with zero query activity

GET
/api/v1/finops/advanced/off-hours

Off-hours spend from silver hourly granularity

GET
/api/v1/finops/advanced/underutil

Under-utilised cluster detection

GET
/api/v1/finops/config

Retrieve all active threshold values

PUT
/api/v1/finops/config

Bulk upsert threshold values (takes effect immediately)

Grouping Dimensions

group_by valueFOCUS columnTypical use
serviceservice_nameBreak down cost by AWS / cloud service
regionregion_nameIdentify high-cost regions
resourceresource_idPer-resource cost attribution
billing_accountbilling_account_idMulti-account chargeback
workloadworkload_id (ARN-derived)Application-level cost grouping
cost_typecost_type (commitment-derived)On-demand vs. Reserved vs. Savings Plan mix
tag:<key>tags JSON fieldBusiness dimensions: cost_center, team, environment

FOCUS Compliance

All grouping dimensions map directly to FOCUS v1.2 column names or are derived from them. This ensures the API is interoperable with any FOCUS-compatible billing export — not just AWS CUR.

Standard Response Envelope

json
{
  "success": true,
  "data": [ ... ],
  "meta": {
    "period": { "from": "2024-01-01", "to": "2024-01-31" },
    "group_by": "service",
    "cost_basis": "amortized",
    "row_count": 24
  }
}

See API Overview for authentication details and the error response format.