levelfour
v1.8.0
Published
The official TypeScript SDK for the LevelFour cloud cost optimization API
Downloads
1,883
Maintainers
Readme
LevelFour Node SDK
The official TypeScript SDK for the LevelFour API.
Documentation
Full API reference at docs.levelfour.ai.
Requirements
Node.js >=22. TypeScript >=5.0 for type-correct usage.
This SDK targets Node.js only. Deno, Bun, Cloudflare Workers, and browser environments are not supported.
Installation
npm install levelfourUsage
Set LEVELFOUR_API_KEY in your environment, or pass the key directly:
import LevelFour from "levelfour";
const client = new LevelFour({ apiKey: "l4_live_..." });
// Or use LEVELFOUR_API_KEY env var:
const client = new LevelFour();
const summary = await client.recommendations.getSavingsByProvider();
console.log(summary);Named import also works:
import { LevelFourClient } from "levelfour";
const client = new LevelFourClient();Importing Types
Request and response types are available through the LevelFour namespace:
import { LevelFour } from "levelfour";
type Item = LevelFour.RecommendationItem;
type Detail = LevelFour.RecommendationDetailResponse;Or as direct imports:
import type { LevelFour } from "levelfour";
const handle = (item: LevelFour.RecommendationItem) => { /* ... */ };Cost alerts
client.costAlerts creates, changes, pauses and deletes the alerts that watch an account or a saved cost view. It ships in 0.2.0, and writes need a read-write key.
import LevelFour from "levelfour";
const client = new LevelFour();
const monthly = (budget: number) => ({
kind: "budget" as const,
budget,
reset: "monthly" as const,
rules: [{ amount: 80, unit: "percent" as const }],
});
const created = await client.costAlerts.createAlert({
name: "Production budget",
source_id: "123456789012", // an id from client.costAlerts.listSources()
params: monthly(40000),
recipients: [{ name: "Ana", email: "[email protected]", member: true }],
});
const alert_id = created.data.id;
// Only the fields you pass change. params replaces the whole rule, so send it back with the new figure.
await client.costAlerts.updateAlert({ alert_id, params: monthly(50000) });
await client.costAlerts.updateAlert({ alert_id, enabled: false }); // pause
await client.costAlerts.updateAlert({ alert_id, enabled: true }); // resume
// Deleting takes the alert's history with it. Pausing keeps it.
await client.costAlerts.deleteAlert({ alert_id });Error Handling
import { LevelFourClient, NotFoundError, RateLimitError, LevelFourError } from "levelfour";
const client = new LevelFourClient();
try {
const rec = await client.recommendations.get({ recommendation_id: "abc" });
} catch (err) {
if (err instanceof NotFoundError) {
console.log("Recommendation not found");
} else if (err instanceof RateLimitError) {
console.log("Rate limited");
} else if (err instanceof LevelFourError) {
console.log(`${err.statusCode}: ${err.message}`);
}
}Available errors: BadRequestError (400), AuthenticationError (401), AuthorizationError (403), NotFoundError (404), ConflictError (409), ValidationError (422), RateLimitError (429), InternalServerError (500).
Network-level failures (DNS, TCP, TLS) throw LevelFourConnectionError. Timeouts throw LevelFourTimeoutError. Both extend LevelFourError, so instanceof LevelFourError catches all SDK errors:
import { LevelFourClient, LevelFourError, LevelFourConnectionError, LevelFourTimeoutError } from "levelfour";
try {
await client.recommendations.list();
} catch (err) {
if (err instanceof LevelFourTimeoutError) {
console.error("Request timed out");
} else if (err instanceof LevelFourConnectionError) {
console.error("Connection failed:", err.message);
} else if (err instanceof LevelFourError) {
console.error(`API error ${err.statusCode}:`, err.message);
}
}Configuration
const client = new LevelFourClient({
apiKey: "l4_live_...",
baseURL: "https://custom.api.com",
timeoutInSeconds: 60,
maxRetries: 5,
defaultHeaders: { "X-Custom": "value" },
logging: { level: "debug", silent: false },
});| Option | Default | Description |
|--------|---------|-------------|
| apiKey | LEVELFOUR_API_KEY env var | API key |
| baseURL | https://api.levelfour.ai | API base URL |
| timeoutInSeconds | 30 | Request timeout |
| maxRetries | 2 | Retry attempts for 429, 408, 409, 5xx |
| fetch | globalThis.fetch | Custom fetch implementation |
| defaultHeaders | none | Additional headers on every request |
| logging | { silent: true } | Logger configuration |
Custom Fetch
Pass a fetch-compatible function for proxies, interceptors, or custom TLS:
import { LevelFourClient } from "levelfour";
import { ProxyAgent, fetch as undiciFetch } from "undici";
const dispatcher = new ProxyAgent("http://proxy.corp.example.com:3128");
const client = new LevelFourClient({
fetch: (url, init) => undiciFetch(url, { ...init, dispatcher } as any),
});Logging
The SDK accepts a logging option to control log output. Logging is silent by default.
const client = new LevelFourClient({
logging: { level: "debug", silent: false },
});| Level | Output |
|-------|--------|
| "debug" | Request URL, method, headers, retry attempts |
| "warn" | Warnings only |
| "error" | Errors only |
Per-Request Options
Typed resource methods accept per-request options:
const result = await client.recommendations.list({
timeoutInSeconds: 10,
maxRetries: 0,
abortSignal: controller.signal,
});Raw HTTP methods accept an options object as the second argument:
await client.get("/api/v1/resource", {
headers: { "X-Idempotency-Key": "unique-key" },
timeoutInSeconds: 10,
maxRetries: 0,
});Retries
All API calls automatically retry failed requests with exponential backoff, jitter, and Retry-After header support. By default, retries up to 2 times on:
429Too Many Requests (respectsRetry-Afterheader)408Request Timeout409Conflict5xxServer Errors
const client = new LevelFourClient({ maxRetries: 5 });
// or disable entirely:
const client = new LevelFourClient({ maxRetries: 0 });Timeouts
The default request timeout is 30 seconds. Override per-client:
const client = new LevelFourClient({ timeoutInSeconds: 60 });For per-request cancellation, pass an AbortSignal:
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);
const result = await client.recommendations.list({
abortSignal: controller.signal,
});Pagination
Paginated endpoints return a Page with an async iterator:
for await (const item of await client.recommendations.list()) {
console.log(item);
}Collect all items into an array:
import { collectAll } from "levelfour";
const all = await collectAll(await client.recommendations.list());Or control pagination manually (note: getNextPage() mutates the page in place):
let page = await client.recommendations.list();
console.log(page.data);
while (page.hasNextPage()) {
page = await page.getNextPage();
console.log(page.data);
}Raw HTTP Requests
For undocumented or beta endpoints:
const data = await client.get<MyType>("/api/v1/custom-endpoint");
const result = await client.post<CreateResponse>("/api/v1/custom", {
body: { name: "example" },
});For full response access (headers, status):
const { data, rawResponse } = await client.get("/api/v1/health").withRawResponse();
console.log("Status:", rawResponse.status);All methods (get, post, put, patch, delete, request) accept optional RequestOptions:
await client.get("/api/v1/resource", {
headers: { "X-Custom": "value" },
queryParams: { page: "1" },
timeoutInSeconds: 10,
maxRetries: 0,
});Webhooks
Verify incoming events using HMAC-SHA256:
import { WebhookVerifier } from "levelfour";
const verifier = new WebhookVerifier("whsec_...");
const payload = verifier.verify(request.body, request.headers);Manage webhook endpoints using the generated client.webhooks sub-client:
const endpoints = await client.webhooks.list();
await client.webhooks.register({
url: "https://yourapp.com/webhooks",
event_types: ["recommendation.created"],
});
await client.webhooks.delete({ endpoint_id: endpointId });Install the optional peer dependency:
npm install @opentelemetry/apiWhen @opentelemetry/api is installed, the SDK automatically instruments all HTTP requests with OpenTelemetry spans and metrics. Alternatively, pass a custom fetch function with your own tracing:
import { LevelFourClient } from "levelfour";
const client = new LevelFourClient({
fetch: myCustomTracedFetch,
});Versioning
This SDK follows Semantic Versioning. After v1.0.0, breaking changes will only ship in major version bumps.
Support
Docs are at docs.levelfour.ai. For help, email [email protected].
Security
To report a vulnerability, email [email protected].
License
Apache-2.0.
