Most fallback chains are written in the order a team happens to have configured API keys. The primary is the good model, the secondary is the cheaper one, the tertiary is the one someone remembered to add. That ordering is a price list, not an engineering decision. It says nothing about what happens when the primary call is slow rather than failing.
The right starting point is a per-request latency budget. Every user-facing request gets a number: the wall-clock time after which the answer is no longer useful, whether the request succeeded or not. For a chat reply that streams, that may be time to first token. For an agent that chains tool calls, it is the sum of the whole chain, because latency compounds at every step and the user waits on the total. For a background enrichment job, the budget may be generous enough that latency barely matters and price dominates.
The budget does two jobs. First, it defines the trigger: when the primary has consumed its share of the budget without producing a usable answer, the fallback fires. Second, it defines the price of waiting. If you have decided a request has 3,000 ms, then every millisecond past that line has a real cost, in abandonment, retries and support load. You can put a dollar figure on it once you know your conversion or retention curve. Even a rough figure is enough to make the ordering decision correctly.
This is the same discipline as setting timeouts on edge functions. A platform will enforce its own limits whether or not you planned for them: Supabase documents a 150 second wall clock limit on the free plan and a 504 when the function does not respond in time, and Netlify sets a 40 second response header timeout. Your fallback logic needs its own budget smaller than those ceilings, otherwise the platform kills the request before the ladder reaches the second rung.



