Guides

Filtering and sorting

Overview

LevelFour exposes rich query controls through four tiers. Pick the tier that fits the task:

TierWhat it doesWhen to use
Built-in flagsServer-side filter, sort, group, paginateStandard queries
Interactive TUIVisual drill-down with live filtersExploring data
--jq expressionPost-process JSON with jq syntaxAd-hoc transforms
Shell pipesCompose with grep/awk/xargs/jqFull grep-style flexibility

All recipes on this page have been validated against the live API.

Built-in filter flags

Every list-style subcommand supports repeatable filter flags, date windows, sort, and pagination. The most common flags:

# Filter by one or more services, regions, accounts, environments, or tags
l4 costs breakdown \
  --service EC2 --service RDS \
  --region us-east-1 \
  --account 123456789012 \
  --tag-key Environment --tag-value production

# Date window (preset or explicit)
l4 costs breakdown --preset 30D         # 30D, 6M, 12M
l4 costs breakdown --start 2026-01-01 --end 2026-01-31

# Granularity
l4 costs breakdown --granularity daily   # or monthly

# Multi-dimension grouping
l4 costs breakdown --group-by service --group-by region

# Sort
l4 costs breakdown --sort-by cost --sort-order desc

# Pagination (max 100 per page)
l4 costs breakdown --page 2 --page-size 50

Interactive TUI

For visual exploration, add --tui to either costs breakdown or recommendations list:

l4 costs breakdown --tui
l4 recommendations list --tui

Inside the TUI:

KeyAction
/Live search (substring match across all visible columns)
fOpen filter drawer
tab(in filter mode) cycle dimension
enter(in filter mode) apply filter and re-fetch
FClear all active filters
s / SCycle sort column / toggle asc↔desc
n / pNext / previous page
enterToggle detail pane for selected row
?Full keyboard help
qQuit

Active filters render in the footer. Any flags passed on the command line seed the TUI's initial state, so l4 costs breakdown --service RDS --tui opens with service=RDS already applied.

--jq — the grep of the CLI

The --jq flag applies a jq expression to the response before display. It works on every subcommand that returns data. The CLI uses gojq internally — syntax is compatible with standard jq.

Two quirks to know:

  1. The -r (raw output) flag is NOT available via --jq. For raw strings in pipelines, use --json | jq -r '...' with your system jq instead.

  2. Response envelopes differ between commands:

    • costs breakdown wraps data at .data.items[]
    • recommendations list wraps at .data.data.items[]

    This is because recommendations list bundles the provider overview alongside the list. Copy the envelope path from the recipes below.

Shell pipes

The CLI does not emit spinner or ANSI escapes when stdout is not a terminal, so pipelines compose cleanly:

l4 recommendations list --json | jq '.data.data.items[].service' | sort | uniq -c

Recipes

Every recipe is formatted as:

  • A short problem statement (the why)
  • The command
  • A note on expected output or a variation

1. Top services by cost this month

Find the services consuming the largest share of your current cloud spend. Useful as the first step in any cost review.

l4 costs breakdown \
  --preset 30D \
  --sort-by cost --sort-order desc \
  --page-size 10

Returns the top 10 services by cost over the last 30 days. Drop --page-size for the default 20; raise it to 100 to scan everything.

2. Monthly spend trend (all-time)

See month-over-month spend across your whole organization. Good for budget reviews and spotting seasonality.

l4 costs monthly --json \
  | jq -r '.data.data_points[] | [.month, .amount] | @tsv'

Returns tab-separated YYYY-MM and dollar amount, one line per month. Pipe to column -t for aligned columns.

3. Daily spend for a specific window

Zoom into a date range to hunt for day-level anomalies. Useful when monthly totals look wrong and you want to find the day things spiked.

l4 costs daily --start 2026-03-01 --end 2026-03-31 --json \
  | jq -r '.data.data_points[] | [.date, .amount] | @tsv'

Returns one row per day in the range. Pipe to sort -k2 -n to list days from cheapest to most expensive.

4. Cost breakdown by region

Which regions are driving your AWS bill? Groups the service list by region so you can compare.

l4 costs breakdown \
  --group-by region \
  --sort-by cost --sort-order desc \
  --page-size 20

Returns region-scoped cost rows. Add --service EC2 to scope to a specific service within each region.

5. RDS delete candidates (≥99% savings)

Find RDS recommendations where LevelFour estimates 99% or more savings — usually idle databases or forgotten snapshots where the right action is delete. These are the lowest-effort wins in a cost-optimization sprint.

l4 recommendations list --service RDS --page-size 100 --json \
  | jq '[.data.data.items[] | select(.savings_percentage >= 99)]'

Returns a JSON array of matching recommendations. To get just IDs (useful for scripting):

l4 recommendations list --service RDS --page-size 100 --json \
  | jq -r '.data.data.items[] | select(.savings_percentage >= 99) | .recommendation_id'

6. Top 5 recommendations by monthly savings

Ranked list of the highest-impact recommendations across all services and providers. This is the "show me the money" query.

l4 recommendations list \
  --sort-by monthly_savings --sort-order desc \
  --page-size 5

Returns a table of the top 5. For a JSON-friendly version suitable for scripts, add --json and pipe to jq.

7. Recommendations with ≥50% savings

All recommendations that would cut the underlying resource cost in half or more. Good for prioritizing a sprint — anything above 50% is usually worth the engineering time.

l4 recommendations list --page-size 100 --json \
  | jq '[.data.data.items[] | select(.savings_percentage >= 50)]'

Returns a JSON array. Append | length to the jq expression to get just the count.

8. Count recommendations by service

How are your savings opportunities distributed? This groups the current page of recommendations by service and sorts by count.

l4 recommendations list --page-size 100 --jq \
  '[.data.data.items[] | .service] | group_by(.) | map({service: .[0], count: length}) | sort_by(-.count)'

Returns an array of {service, count} objects sorted highest-first.

9. Count recommendations by status

See how many recommendations are pending vs optimized vs rejected. Useful for tracking team throughput.

l4 recommendations list --page-size 100 --jq \
  '[.data.data.items[] | .status] | group_by(.) | map({status: .[0], count: length})'

Returns an array of {status, count} objects.

10. Discover available filter values

Before filtering, list what values are actually present in your data for a given dimension. Equivalent to the autocomplete dropdowns in the dashboard.

l4 costs filters                 # summary of all dimensions
l4 costs filters service         # all services
l4 costs filters region          # all regions
l4 costs filters account         # all accounts
l4 costs filters tag-key         # all tag keys

The bare l4 costs filters prints a summary table with counts and sample values.

11. Bulk-view RDS delete candidates

Pipe recipe 5's matching IDs directly into recommendations view to walk through each one's full detail in sequence. Great for a review session where you want to see the full justification before approving.

l4 recommendations list --service RDS --page-size 100 --json \
  | jq -r '.data.data.items[] | select(.savings_percentage >= 99) | .recommendation_id' \
  | xargs -I{} l4 recommendations view {}

Prints the full detail for every matching recommendation, in order. Remove the final | xargs ... to just get the ID list.

12. Export top recommendations to CSV

Dump the highest-savings recommendations into CSV for sharing in a spreadsheet or ticket.

l4 recommendations list \
  --sort-by monthly_savings --sort-order desc \
  --page-size 50 --json \
  | jq -r '.data.data.items[] | [.recommendation_id, .service, .monthly_savings, .savings_percentage] | @csv' \
  > top-recommendations.csv

Writes a CSV with four columns and no header. Prepend a header row:

echo '"id","service","monthly_savings","savings_percentage"' > top-recommendations.csv
l4 recommendations list ... | jq -r '... | @csv' >> top-recommendations.csv

13. Combined: RDS in a specific account, sorted by savings

Scope a review to a single account or environment using primary filter flags, then sort by impact.

l4 recommendations list \
  --service RDS \
  --account 123456789012 \
  --sort-by monthly_savings --sort-order desc \
  --page-size 20

Returns a table scoped to the given account. Replace --account with --environment production or --tag Environment=production for tag-based scoping.

14. Full cost analysis for a service across regions

Multi-dimensional grouping: see how EC2 cost breaks down by both service variant and region, over the last quarter.

l4 costs breakdown \
  --preset 6M \
  --service EC2 \
  --group-by region --group-by account_id \
  --sort-by cost --sort-order desc \
  --page-size 50

Returns rows grouped by both region and account, sorted by cost. Swap --preset 6M for --preset 30D to see a shorter window.

15. RDS delete-candidates summary table

The same ≥99%-savings RDS recommendations from Recipe 5, but formatted as a scannable table with the recommendation title inline (from actions.overview). This is the format you want when you're sitting down to actually work through delete candidates.

l4 recommendations list --page-size 100 --json \
  | jq -r '.data.data.items[]
    | select(.service == "RDS" and .savings_percentage >= 99)
    | [.recommendation_id, "$" + (.monthly_savings | tostring) + "/mo", .actions.overview]
    | @tsv' \
  | column -t -s $'\t'

Returns a three-column table: ID, monthly savings, one-line title. Sorted highest-savings-first (API default). Change --service RDS to any other service or drop the .service filter for all providers.

16. Recommendations grouped by account with totals

Aggregate RDS delete-candidates by account and sum their savings. Useful for planning cleanup work — you see instantly which accounts have the biggest concentration of low-hanging fruit and how much each is worth.

l4 recommendations list --page-size 100 --json \
  | jq -r '
    ([.data.data.items[]
      | select(.service == "RDS" and .savings_percentage >= 99)
      | . + {acct_norm: (.account | sub(" \\(.*\\)$"; ""))}
    ]) as $rds
    | ($rds | group_by(.acct_norm) | map({
        account: .[0].acct_norm,
        env: ([.[].environment] | unique | join(", ")),
        count: length,
        monthly: (map(.monthly_savings) | add * 100 | round / 100)
      }) | sort_by(-.monthly)) as $grouped
    | "ACCOUNT\tENVIRONMENT\tCOUNT\tMONTHLY SAVINGS",
      "-------\t-----------\t-----\t---------------",
      ($grouped[] | [.account, .env, .count, "$" + (.monthly | tostring)] | @tsv),
      "-------\t-----------\t-----\t---------------",
      "TOTAL\t\t" + ($rds | length | tostring) + "\t$" + ($rds | map(.monthly_savings) | add * 100 | round / 100 | tostring)
  ' \
  | column -t -s $'\t'

Returns a four-column table (account, environment, count, total monthly savings) with divider rows and a TOTAL footer. The sub(" \\(.*\\)$"; "") normalizes account IDs so 123456789012 (Live) and 123456789012 merge into one row.

SDK equivalents

The same filters and sort fields are typed parameters on the SDK's list methods — use these when you're scripting in a programming language instead of the shell.

Python

from levelfour import LevelFour

client = LevelFour()

# RDS delete candidates
recs = client.recommendations.list_by_provider(
    "aws",
    service=["RDS"],
    page_size=100,
)
candidates = [r for r in recs if r.savings_percentage >= 99]

TypeScript

import { LevelFourClient } from "levelfour";

const client = new LevelFourClient();

const candidates = [];
for await (const rec of await client.recommendations.listByProvider("aws", {
    service: ["RDS"],
    page_size: 100,
})) {
    if (rec.savings_percentage >= 99) candidates.push(rec);
}

Go

ctx := context.Background()
page, err := client.Recommendations.ListByProvider(ctx, "aws",
    &levelfour.ListByProviderRecommendationsRequest{
        Service:  []string{"RDS"},
        PageSize: levelfour.Int(100),
    })
if err != nil { log.Fatal(err) }

iter := page.Iterator()
for iter.Next(ctx) {
    rec := iter.Current()
    if rec.SavingsPercentage >= 99 {
        fmt.Println(rec.RecommendationID)
    }
}

Read-only guarantee

Every recipe on this page is read-only. None of them accept, reject, or execute recommendations. For mutating actions see the recommendations resource guide.