SDKs

Python SDK

Installation

pip install levelfour

Client Setup

Sync Client

from levelfour import LevelFour

client = LevelFour(
    api_key="l4_live_...",
    base_url="https://api.levelfour.ai",
    timeout=30.0,
    max_retries=2,
)

With environment variable auto-detection:

client = LevelFour()

The client supports context manager usage:

with LevelFour() as client:
    summary = client.recommendations.get_savings_by_provider()

Async Client

import asyncio
from levelfour import AsyncLevelFour

async def main():
    async with AsyncLevelFour() as client:
        summary = await client.recommendations.get_savings_by_provider()

asyncio.run(main())

The async client accepts httpx.AsyncClient via the http_client parameter.

Constructor Options

ParameterTypeDefault
api_keystr | NoneLEVELFOUR_API_KEY env var
base_urlstrhttps://api.levelfour.ai
timeoutfloat30.0
max_retriesint2
http_clienthttpx.Client | NoneBuilt-in
headersdict[str, str] | NoneNone

Cloning with Modified Options

read_only_client = client.with_options(timeout=60.0)

Recommendations

summary = client.recommendations.get_savings_by_provider()

potential = client.recommendations.get_potential_savings()

overview = client.recommendations.get_overview()

processing = client.recommendations.list_in_progress()

detail = client.recommendations.get("rec_123")

page = client.recommendations.list(
    page=1,
    page_size=50,
    sort_by="monthly_savings",
    sort_order="desc",
)

Recommendations Audit

summary = client.recommendations.audit.get_summary()

page = client.recommendations.audit.list(
    page=1,
    page_size=50,
    sort_by="monthly_savings",
    sort_order="desc",
    start="2025-01-01",
    end="2025-03-31",
    preset="6M",
    provider="aws",
    service=["EC2", "RDS"],
    environment=["production"],
    account_id=["123456789012"],
)

Costs

summary = client.costs.get_summary()

breakdown = client.costs.list(
    format="table",
    period="2025-03",
    provider_id="aws",
    page=1,
    page_size=50,
    sort_by="cost",
    sort_order="desc",
)

daily = client.costs.get_daily_costs(
    start="2025-03-01T00:00:00.000Z",
    end="2025-03-31T00:00:00.000Z",
)

monthly = client.costs.get_monthly_costs()

Providers

providers = client.providers.list()

top = client.providers.get_top_recommendations("aws")

recs = client.providers.list_recommendations(
    "aws",
    page=1,
    page_size=50,
    sort_by="monthly_savings",
    sort_order="desc",
    service=["EC2"],
    display_status=["available", "pending"],
)

overview = client.providers.get_recommendations_overview("aws")

filters = client.providers.get_recommendation_filters("aws")

savings = client.providers.list_realized_savings(
    "aws",
    page=1,
    page_size=50,
    preset="6M",
)

savings_summary = client.providers.get_realized_savings_summary("aws")

spending = client.providers.get_costs_summary("aws")

spending_filters = client.providers.get_costs_filters(
    "aws",
    start="2025-01-01T00:00:00.000Z",
    end="2025-03-31T00:00:00.000Z",
)

spending_list = client.providers.list_costs(
    "aws",
    format="table",
    page=1,
    page_size=50,
)

timeline = client.providers.get_costs_timeline(
    "aws",
    start="2025-01-01T00:00:00.000Z",
    end="2025-03-31T00:00:00.000Z",
)

potential = client.providers.get_potential_savings_summary("aws")

potential_list = client.providers.list_potential_savings(
    "aws",
    page=1,
    page_size=50,
)

API Keys

keys = client.api_keys.list()

new_key = client.api_keys.create(name="CI Pipeline", scope="read")

client.api_keys.revoke("key_123")

rotated = client.api_keys.rotate("key_123")

Webhooks

endpoints = client.webhooks.list()

endpoint = client.webhooks.register(
    url="https://example.com/webhook",
    event_types=["recommendation.accepted", "optimization.completed"],
    description="Production webhook",
)

client.webhooks.delete("ep_123")

Auth

me = client.auth.get_whoami()

Pagination

Methods that return paginated results return a SyncPager (or AsyncPager for the async client). You can iterate items automatically, iterate pages, or navigate manually.

Auto-iterate all items

for rec in client.recommendations.list(page_size=50):
    print(rec.recommendation_id)

Async auto-iterate

async for rec in await async_client.recommendations.list(page_size=50):
    print(rec.recommendation_id)

Iterate pages

for page in client.recommendations.list(page_size=50).iter_pages():
    print(f"Page has {len(page.items)} items")

Manual pagination

page = client.recommendations.list(page_size=50)
while page:
    for item in page.items:
        print(item)
    page = page.next_page()

See Pagination for more details.

Error Handling

from levelfour import LevelFour, NotFoundError, RateLimitError, LevelFourError

client = LevelFour()

try:
    detail = client.recommendations.get("rec_nonexistent")
except NotFoundError as e:
    print(f"Not found: {e.message}")
except RateLimitError as e:
    print(f"Rate limited. Retry after: {e.retry_after}")
except LevelFourError as e:
    print(f"API error {e.status_code}: {e.message}")

All exceptions inherit from LevelFourError and expose status_code, code, message, and details attributes.

See Error Handling for the full exception hierarchy.

Webhook Verification

from levelfour.webhooks.verifier import WebhookVerifier, WebhookVerificationError

verifier = WebhookVerifier("whsec_your_signing_secret")

try:
    payload = verifier.verify(
        payload=request_body,
        headers={
            "webhook-id": headers["webhook-id"],
            "webhook-timestamp": headers["webhook-timestamp"],
            "webhook-signature": headers["webhook-signature"],
        },
    )
    print(f"Verified event: {payload['type']}")
except WebhookVerificationError as e:
    print(f"Verification failed: {e}")

Custom timestamp tolerance (default is 300 seconds):

payload = verifier.verify(payload=body, headers=hdrs, tolerance_seconds=600)

See Webhooks for event types and payload details.

Request Options

Override client defaults on a per-request basis:

from levelfour.core.request_options import RequestOptions

opts: RequestOptions = {
    "timeout_in_seconds": 60,
    "max_retries": 5,
    "additional_headers": {"X-Request-Id": "abc123"},
}

summary = client.recommendations.get_savings_by_provider(request_options=opts)
OptionTypeDescription
timeout_in_secondsintOverride request timeout
max_retriesintOverride max retry attempts
additional_headersdict[str, Any]Extra headers for this request
additional_query_parametersdict[str, Any]Extra query parameters
additional_body_parametersdict[str, Any]Extra body parameters