In serverless apps the real bill is usually the cloud APIs you call — databases, queues, storage — priced by fragmented tiered rules, so one API call buried in a loop can quietly cost thousands of dollars, and today's tools either make you guess invocation counts by hand or only measure cost after you have already deployed and been billed; this paper (INESC-ID / IST Lisbon — Bernardo Ribeiro, Mafalda Ferreira, Jose Fragoso Santos, Rodrigo Bruno, Nuno Santos) presents Skyler, which statically analyzes the code and its infrastructure templates before deployment, builds symbolic cost formulas encoded as SMT, and pinpoints the economic sinks — hitting under 1 percent error on AWS and Google and 4.5 percent on Azure.
The argument in one glance
-
Problem
In serverless apps, the bill is driven by API calls you cannot easily count
Serverless computing (AWS Lambda, Google Cloud Functions, Azure Functions) sells a simple promise: write functions, deploy them, and pay only for what runs, with the cloud handling scaling. But the money does not go where people expect. A serverless function rarely does much on its own; it calls other managed cloud services — a database like DynamoDB, a queue like SQS, storage like S3, a text-analysis API like Comprehend — and it is those API calls, each billed under its own tiered pricing rules, that usually dominate the bill. In API-heavy workflows the compute time is often under one percent of the total cost. The trouble is that the number of API calls is not a fixed property of the code: it depends on which branches run, whether a loop repeats, how big the input is, and whether one function asynchronously triggers another. A single overlooked API call inside a loop or a recursive call can, in a real deployment, add thousands of dollars — the well-known Prime Video case saw a surge in S3 and Step Function calls push costs up 90 percent, forcing a redesign. Worse, exposed endpoints invite Denial-of-Wallet attacks, where an adversary deliberately triggers expensive workflows to run up the victim bill. Developers need to know, before they deploy, which code paths are the expensive ones.
-
What people did
Use a provider pricing calculator, or profile the app after deploying it
Two families of tools exist. Provider pricing calculators (the AWS Pricing Calculator, the Google Cloud Calculator) let a developer estimate a bill by entering, among other things, how many times each API will be called. And dynamic profilers (systems like Jolteon and Orion) deploy the application, measure how it actually behaves under load, and tune its runtime configuration to reduce cost. One asks the human to predict usage up front; the other watches real usage after the fact.
-
Why it fell short
Calculators need counts nobody can guess; profilers only work after you pay
Both break on the same reality — that serverless cost is workflow- and input-dependent. A calculator is only as good as the invocation counts you feed it, and for anything past a toy app those counts are nearly impossible to produce by hand: control flow branches, loops repeat, functions trigger each other asynchronously through queues, and API costs scale with input size (DynamoDB charges per kilobyte written and per four kilobytes read; Comprehend charges per hundred characters; SQS bills a message in sixty-four-kilobyte chunks, so a large message counts as several requests). Omitting one such detail — say, SQS fragmentation — can underestimate a queue cost by 4 times. Manually enumerating every workflow and multiplying it through fragmented, provider-specific pricing tables is error-prone and does not scale. Dynamic profilers sidestep the guessing, but only by deploying first: they require the application to be running, they focus on compute rather than the downstream API charges that dominate the bill, and by the time they report, the money has already been spent — and a Denial-of-Wallet path has already been exploitable. What was missing was a way to estimate API-invocation cost accurately and per-path before any deployment.
-
This paper's idea
Statically analyze the code and its templates, then reason about cost symbolically
Skyler is an offline, static analysis framework that estimates the API-invocation cost of a serverless application before it is deployed, and pinpoints its economic sinks — the code paths whose API usage disproportionately drives the bill. It runs as a three-stage pipeline. First, a serverless graph generator parses both the application source (JavaScript) and its Infrastructure-as-Code templates (such as AWS SAM or Terraform, which describe the deployed services, functions, event sources, and regions) into a single language- and platform-agnostic representation of the application control and data flow — a serverless graph. Second, a cost modeling engine walks that graph and, using a pluggable per-provider pricing plugin, attaches to each API call a symbolic cost equation that captures the real billing rules (per-kilobyte, per-hundred-characters, per-chunk, tiered). These combine into one symbolic cost expression, encoded as SMT formulas, parameterized by input features like payload size and item counts. Third, a symbolic simulation engine runs what-if queries over that model — which workflow dominates cost, how spend scales with input size, whether AWS or Google or Azure is cheaper — exposing worst-case paths and cost concentration. Because the analysis is symbolic, it reasons about whole ranges of inputs rather than one run. Skyler supports JavaScript apps across AWS Lambda, Google Cloud Functions, and Azure Functions, and reports mean absolute percentage error under 1 percent for AWS and Google and 4.5 percent for Azure.
Why this problem is worth solving
The cost of a serverless application is not a property of its compute — it is a property of the API calls it makes, and that is exactly what is hardest to see. People instinctively equate cost with computation: how long the code runs, how much memory it uses. In serverless, that instinct is misleading. A function is glue between managed services, and each call to those services — a database write, a queue send, a text-analysis request — carries its own charge under its own pricing rules. The paper measures that for API-heavy workflows the compute portion is often below one percent of the total bill; the API calls are the bill. This matters because it means the usual cost-optimization toolkit, which is built to shave compute time and memory, is aimed at the wrong target. It also means cost is coupled to program behavior in a way compute is not: the number and size of API calls depends on which branch runs, how many times a loop iterates, and how large the input is. So predicting serverless cost is really a program-analysis problem about API usage, not a resource-accounting problem about CPU seconds. Getting this framing right is what lets a tool find the design mistakes — an API call in a hot loop, a needless retry — that turn into runaway bills.
And these mistakes are not rare edge cases; they are structural, and their consequences are large and sometimes adversarial. An economic sink is a code path whose API usage disproportionately drives up cost. They are easy to create and hard to see: a call placed inside a loop, a workflow that fans out to several services, a large payload pushed through a per-byte-priced API. The Prime Video case study in the paper shows how ordinary such a mistake is — a surge in S3 and Step Function calls raised cost by 90 percent and forced an architectural redesign after the fact. The paper also reports that a single input processed on a full moderation path can be 1368 times costlier than on a minimal path, and that growing a payload from one kilobyte to two hundred kilobytes can amplify cost by over 50 times. Beyond honest mistakes, exposed endpoints enable Denial-of-Wallet attacks, in which an adversary intentionally triggers expensive workflows to inflate the victim bill. Because these paths are invisible in a static pricing table and only become obvious once real traffic (or an attacker) exercises them, catching them before deployment — when the fix is a code change, not an emergency bill — is where the value is. That is precisely the window a pre-deployment static analysis can occupy and a post-deployment profiler cannot.
What people did before, and where it broke
- Provider pricing calculators. The AWS Pricing Calculator and Google Cloud Calculator estimate a bill from developer-supplied usage, including how many times each API is called. Where it broke. For realistic workflows the invocation counts are nearly impossible to produce by hand, because control flow, loops, async triggers, and input-dependent behavior make the true count a function of runtime conditions, not a constant.
- Dynamic profilers and runtime tuners. Systems like Jolteon and Orion deploy the application, profile it under load, and tune runtime configuration to cut cost. Where it broke. They require the app to be deployed first, they concentrate on compute rather than the downstream API charges that dominate the bill, and by the time they report, the money is already spent.
- Static pricing tables and manual reasoning. Read each service pricing page and add it up by hand. Where it broke. Billing is fragmented and non-obvious — DynamoDB read versus write capacity units, Comprehend per-hundred-characters, SQS per-sixty-four-kilobyte chunk — so omitting one detail (like SQS fragmentation) can underestimate a cost by 4 times.
- Compute-focused cost optimization. A large body of work reduces serverless cost by targeting execution time, memory sizing, and cold starts. Where it broke. In API-heavy workflows compute is often under one percent of the bill, so optimizing it leaves the dominant cost — the API calls — untouched.
- The gap. No tool estimated API-invocation cost accurately, per code path, and before deployment — reasoning symbolically about how cost depends on inputs and control flow across a provider fragmented pricing rules.
How Skyler works
From an undeployed serverless app to a per-path cost report
-
-
new
-
new
-
new
-
new
-
new
The idea
Reason about cost by inspecting the code, before it ever runs. Skyler never deploys the application; it analyzes the source and its infrastructure templates statically, following every branch, loop, and async trigger to reason about which paths are expensive ahead of time. Reasoning about every path a program can take purely by inspecting its source, before it runs, so a problem hiding on a rare branch surfaces early rather than in production, is the same static, path-sensitive discipline the work on flow-sensitive bug detection in operating-system code (typestate analysis) covered elsewhere on this site applies to find defects without executing the program. Skyler brings that ahead-of-time, path-aware analysis to a new quantity — money — turning a pricing question into a program-analysis one.
Encode cost as symbolic SMT formulas so a solver can reason over all inputs at once. Rather than measure the cost of one concrete run, Skyler builds a symbolic cost expression parameterized by input features and encodes it as SMT formulas, so it can explore worst-case paths and how spend bends with input size across a whole space of possibilities. Expressing a program’s behavior as symbolic formulas in the SMT format, so a solver reasons about the entire input space rather than a single execution, is the same reliance on SMT formulas that the work on fuzzing SMT solvers with synthesized generators (Once4All) covered elsewhere on this site is built around. Skyler uses that symbolic machinery not to test a solver but to answer what-if cost questions — exposing the path that runs 1368 times costlier than the cheap route.
Attack the bill directly by modeling where the money actually goes. Skyler treats cost as the first-class quantity to reason about, mapping each API call to its real price so a developer can see, and fix, the expensive design choice before committing to it. Making the cloud bill itself the thing to optimize — modeling and reducing spend rather than only latency or throughput — is the same cost-first framing the work on optimizing the price of cutting a datacenter carbon footprint (going green for less green) covered elsewhere on this site adopts. Skyler applies that framing to serverless API charges, where the fragmented, per-operation pricing makes the true cost of a design especially hard to see without a model.
Following the motivating example through Skyler
From an undeployed message-posting app to a named economic sink
A trace, drawn from the paper's motivating example, of how Skyler estimates cost for a small serverless app with createUser and createPost endpoints backed by DynamoDB, SQS, and Comprehend.
Step 1 of 5
Give Skyler the code and the templates
The developer hands Skyler the JavaScript for the Lambda functions behind /createUser and /createPost, plus the Infrastructure-as-Code template describing the API Gateway, the DynamoDB tables, the SQS queue, and the Comprehend service. No deployment happens.
Skyler works entirely offline, before anything is provisioned or billed.
Why it works
- Static analysis sees every path, so no bill is needed to find the costly one. Dynamic profilers reveal cost only after a workflow has run and been charged → but analyzing the source and its infrastructure templates covers every branch and async trigger up front → so a ruinous path is caught before a single request is billed.
- One graph unifies code and configuration into a portable model. Cost lives partly in the functions and partly in the deployment wiring, which no single view captures → but parsing both the JavaScript and the Infrastructure-as-Code template into one serverless graph → so the same analysis runs unchanged across AWS, Google Cloud, and Azure.
- Symbolic SMT formulas capture how cost bends with input, not one lucky run. A concrete measurement fixes the input size and misses the rest of the space → but encoding cost as symbolic expressions parameterized by payload size and item counts → so Skyler exposes worst-case paths like the one that runs 1368 times costlier than the cheap route.
- Pluggable pricing models encode each provider fragmented billing exactly. Real rules are non-obvious — per-kilobyte capacity units, per-hundred-character blocks, per-sixty-four-kilobyte message chunks → but attaching a provider-specific pricing plugin to every API call in the graph → so Skyler avoids the kind of omission that underestimates a queue cost by 4 times.
- A query-driven engine turns a total into an actionable hotspot. A single dollar figure tells a developer nothing about where to fix → but running what-if queries over the symbolic model for the dominant path, the input sensitivity, the cheaper provider → so the economic sink itself is named, not just the sum.
What it costs (trade-offs and scope)
- Someone must write and maintain a pricing plugin per provider. Skyler’s accuracy rests on models that encode each cloud billing semantics, so supporting a new provider — or tracking a pricing change — means authoring or updating that plugin by hand.
- JavaScript only, so far. The authors target JavaScript precisely because it is one of the hardest languages to analyze, but serverless apps written in Python, Go, or Java would need new language front ends before Skyler can read them.
- A pre-deployment estimate still rests on assumed usage. Skyler models how cost depends on inputs, but turning that into a concrete dollar figure needs the developer to supply request rates and input-size distributions — assumptions that, if wrong, move the prediction.
- Azure is modeled less exactly than AWS or Google. Mean absolute percentage error is under 1 percent for AWS and Google but 4.5 percent for Azure, reflecting how much harder some providers billing is to capture faithfully.
- It is offline analysis, not a live guardrail. Skyler flags economic sinks before deployment and answers what-if questions, but it does not sit in the running system enforcing a budget or blocking a Denial-of-Wallet attack in real time.
The comparison
Estimating the cost of a serverless application
| Property | Provider calculators | Dynamic profilers | Skyler |
|---|---|---|---|
| Works before deployment | |||
| Captures downstream API cost | |||
| Handles loops, branches, input size | |||
| Pinpoints economic sinks | |||
| Reported accuracy |
Check yourself
Check your understanding
1. In serverless applications, what usually dominates the bill, and why does that make cost hard to predict?
2. Why do provider calculators and dynamic profilers fall short for serverless cost prediction?
3. What is Skyler, and what are the three stages of its pipeline?
4. Why does encoding cost as symbolic SMT formulas matter, rather than just measuring one run?
Sources
The paper is “Skyler: Static Analysis for Predicting API-Driven Costs in Serverless Applications” by Bernardo Ribeiro, Mafalda Ferreira, Jose Fragoso Santos, Rodrigo Bruno, and Nuno Santos (INESC-ID / Instituto Superior Tecnico, Universidade de Lisboa), published at ASPLOS 2026, DOI 10.1145/3779212.3790221. The artifact is available at github.com/arg-inescid/Skyler.
This explainer is written from the paper’s full text (the authors’ PDF). Skyler is a static analysis framework for pre-deployment estimation of the API-invocation cost of serverless applications: it parses application source (JavaScript) and Infrastructure-as-Code templates into a language- and platform-agnostic serverless graph, attaches symbolic cost equations to API calls using pluggable provider-specific pricing plugins, and runs queries over cost expressions encoded as SMT formulas to expose economic sinks. It supports AWS Lambda, Google Cloud Functions, and Azure Functions and reports mean absolute percentage error under 1 percent for AWS and Google and 4.5 percent for Azure. The illustrative figures (compute under one percent of API-heavy bills, a 1368-times cost gap between paths, payload growth amplifying cost by over 50 times, an SQS-fragmentation omission underestimating cost by 4 times, and the Prime Video 90 percent increase) are from the paper’s own motivating analysis and cited case study. Background on serverless computing, SMT, and Infrastructure-as-Code is standard context. The numbers are the paper’s own; exact mechanisms and full results should be confirmed in the paper.