Free playbooks in your inbox
analysis · Claude Fable 5

The Fable 5 Stack That Shipped a Real Feature in 18 Minutes

One configuration shipped a real billing feature in 18 minutes with the cross-file bug caught, which is the stack worth running when you pick from your own receipts instead of sticker prices. The claude fable 5 vs opus cost question has three columns, and the one with the smallest bill is the only one that shipped the bug.

From the youcanbuildthings catalog ▸ Build-tested

Hello builders,

The Fable 5 split-stack shipped a real PDF export for a billing API in 18 minutes with the cross-file bug caught, and picking it came from receipts rather than guesswork. The other two took 22 and 30 minutes, and the run with the smallest bill on the board is the only one of the three that shipped the bug. Somebody asked the only question that matters about claude fable 5 vs opus cost, got a hundred and one upvotes, and got no real answer: has anyone actually compared total tokens for the same task between Fable and Opus?

A five-row comparison table headed with three columns: SPLIT-STACK, ALL-FABLE and ALL-OPUS. Judgment seat row reads Fable 5, Fable 5, Opus 4.8. Typing seat row reads Sonnet 5, Fable 5, Opus 4.8. Modeled cost row reads $1.86, $2.95 with a callout saying paid frontier rates to type, and $1.47. Wall-clock row reads 18 min, 30 min, 22 min. Cross-file bug caught row reads Yes, Yes, and a red No with a flag reading shipped the bug. A green BEST VALUE ribbon sits under the split-stack column, and a footer box reads levered, $1.10, below all-Opus, judgment intact.

The cheapest column in that picture and the column that shipped working software are not the same column.

The method, and where the real numbers live

The feature is PDF export for a billing API, which has enough judgment in it to matter and enough typing to be worth routing away. It ships three ways, same done-criteria, same tests passing at the end, and the only variable is which seats did which work.

Split-stack: Fable audits, Fable plans, a handoff carries it to Sonnet, Sonnet does the typing, Fable does one review pass. All-Fable: point Fable at the task and let it do everything at frontier rates, typing included. All-Opus: everything on Opus 4.8, one solid model for the whole job.

Before the totals, know where a real number comes from, because a receipt we cannot reproduce is just another screenshot. On the API, the response carries a usage.iterations array, and every entry is a per-attempt receipt with its own input, output and cache token counts. That array is what we were actually charged for. In headless mode, claude -p --output-format json returns a result object that includes the run’s cost.

One trap that catches people: the in-session /usage readout is a local estimate, not your billed amount, so it is worth a gut check mid-run and worth nothing in a comparison like this one, where the JSON is the only figure that settles anything.

The ledger below is neither. It is modeled from published per-token rates, which means every cell is arithmetic we can reproduce line by line rather than a figure we have to trust. Those rates, scraped today:

  • Claude Fable 5 (claude-fable-5): $10 per million input tokens, $50 output. 1M context, 128K max output.
  • Claude Opus 4.8 (claude-opus-4-8, now marked Legacy): $5 / $25. 1M context, 128K max output.
  • Claude Sonnet 5 (claude-sonnet-5): $2 / $10. 1M context, 128K max output.
  • Claude Haiku 4.5: $1 / $5. 200K context, 64K max output. (Models overview and Pricing, Claude Platform Docs)

That Sonnet rate deserves a flag, because it moved and it moved in our favour. The launch pricing of $2/$10 was announced as introductory, with a scheduled increase to $3/$15. The docs now say the introductory number is the standard price and the increase will not happen. Everything below is modeled at the older $3/$15 first, because that is what the ledger in the picture was built on, and then re-read at today’s rate.

The three columns, priced

RATES = {"fable-5": (10, 50), "opus-4-8": (5, 25), "sonnet-5": (3, 15)}

def cost(model, tok_in, tok_out, cache_read=0, batch=False):
    rin, rout = RATES[model]
    fresh_in = tok_in - cache_read
    dollars = (fresh_in * rin + cache_read * rin * 0.1 + tok_out * rout) / 1_000_000
    return dollars * 0.5 if batch else dollars

steps = [
    ("audit  Fable ", "fable-5",  40_000,  6_000),
    ("plan   Fable ", "fable-5",  15_000,  4_000),
    ("typing Sonnet", "sonnet-5", 30_000, 25_000),
    ("review Fable ", "fable-5",  20_000,  3_000),
]
split = 0.0
for label, model, tin, tout in steps:
    c = cost(model, tin, tout); split += c
    print(f"{label}: {tin:>6} in / {tout:>6} out  ->  ${c:.2f}")
print(f"split-stack total: ${split:.2f}")
audit  Fable :  40000 in /   6000 out  ->  $0.70
plan   Fable :  15000 in /   4000 out  ->  $0.35
typing Sonnet:  30000 in /  25000 out  ->  $0.47
review Fable :  20000 in /   3000 out  ->  $0.35
split-stack total: $1.86

Fable’s three judgment steps are all input-heavy and output-light, reading a lot of code and writing short reports, which is the cheap direction to run it in because we are mostly paying the $10 input rate to let the best brain read. The one step that generates a lot of tokens, the 25,000-token implementation, ran on Sonnet at $15 rather than Fable at $50, and that single reassignment is most of the dollar between this column and the next one.

Run the identical token counts with the seats swapped and the other two columns fall out: all-Fable $2.95, all-Opus $1.475, which the ledger rounds to $1.47. All-Fable is the most expensive by a mile and the slowest on the clock at 30 min, against 18 min for the split-stack and 22 min for all-Opus, because it paid the $50 output rate to type the feature and got throttled doing it, since Fable is the hardest-throttled seat on the stack.

And all-Opus is the cheapest. About forty cents cheaper than the split-stack, which is probably not the order we were expecting.

Where the cheap column got expensive

The bug row is the answer, so here is the actual bug. The feature writes a file in one place and reads it in another:

// src/pdf/render.ts writes the rendered PDF here
await s3.put(`invoice-${id}.pdf`, buffer);

// src/storage/links.ts builds the download URL from a DIFFERENT key
export const downloadUrl = (id) => s3.sign(`invoices/${id}/latest.pdf`);

Read each file alone and nothing’s wrong. render.ts writes a valid key. links.ts signs a valid key. Each passed its own unit test. But they disagree on the key format, so every generated PDF gets written to one location and looked up at another, and every download 404s.

id = "abc123"
write_key = f"invoice-{id}.pdf"
read_key  = f"invoices/{id}/latest.pdf"
print(f"write: {write_key}")
print(f"read:  {read_key}")
print("MISMATCH: every download 404s" if write_key != read_key else "keys agree")
write: invoice-abc123.pdf
read:  invoices/abc123/latest.pdf
MISMATCH: every download 404s

Each file is correct on its own and the pair of them is broken, so catching it means holding both paths in mind at once and noticing that they do not match, which is exactly the whole-system reasoning the frontier seat is best at. The Fable audit flagged it before a single download failed. The all-Opus run read both files and flagged neither.

So all-Opus saved forty cents on the meter and shipped a bug to production. That bug costs more than forty cents the first time a customer’s invoice fails to download.

There is one more line an honest ledger includes and most people forget: the split-stack pays a small cache penalty for switching models, because prompt caches are per-model and swapping Fable for Sonnet writes a cold one. A few cents, and a ledger that leaves it out is quietly arguing for its own conclusion.

That $1.86 is the naive version, with none of the cost levers applied. The judgment steps all read overlapping context, so we cache the repo prefix once and later reads bill at a tenth. The audit does not need an instant answer, so we batch it for half price.

levered = (cost("fable-5",  40_000,  6_000, cache_read=30_000, batch=True)
         + cost("fable-5",  15_000,  4_000, cache_read=10_000)
         + cost("sonnet-5", 30_000, 25_000, cache_read=20_000)
         + cost("fable-5",  20_000,  3_000, cache_read=15_000))
print(f"levered ${levered:.2f} (was $1.86 cold)")
levered $1.10 (was $1.86 cold)

A dollar ten, now below the all-Opus number, with Fable-grade judgment intact. That forty-cent premium was never structural. It was what we paid for running everything cold, interactive and full price when the work qualified for the discounts all along.

Now we re-read all of it at today’s Sonnet rate. The typing step drops from $0.47 to $0.31, the cold split-stack lands at $1.71 instead of $1.86, and the levered version comes in at $0.96. All-Fable and all-Opus do not move at all, because neither uses Sonnet. The cheaper the executor seat gets, the more the split-stack wins, and that is the direction this market has been moving.

When the split-stack loses

One ledger is one data point, so we run a deliberately different task: add a fully specified status endpoint, no design decisions, pure typing. The full pipeline costs $0.49. Typing it on Sonnet alone costs $0.11. At today’s rate that second number is $0.07.

The full ceremony loses badly here because there was no judgment in the task to protect, and running an audit, a plan and a review over a job with no real decisions in it costs more than the job does.

That is the router doing its job rather than failing at it, because a classifier that sees pure typing sends this task straight to Sonnet and skips the audit and the plan entirely. The forty-nine-cent number is what we get by mechanically forcing the full pipeline onto a task that never needed it, which is a routing mistake, not a verdict on routing.

So what the ledger actually teaches is narrower than “the split-stack is always cheapest.” The first half of it is that letting the frontier seat do the typing lost on every axis here, cost and speed both, and nothing in the second task reversed that. The second half is that choosing between the split-stack and all-Opus is really just the question of whether this particular job needs frontier judgment, since on the ambiguous refactor or the cross-file trap or the security call the premium on the judgment portion pays for itself, and on a well-scoped job Opus handles it for less.

The last thing is the one the ledger cannot show us, which is that it prices tokens and not the bug. All-Opus won on the meter and lost in reality, and no token counter will ever show us that, because a bug we never shipped has no line item. Price it yourself, roughly: what does one production incident cost us in time and trust? Whatever that number is, it dwarfs the routing delta.

For what it is worth, here is where I actually land after running this. I keep the audit and the review on the frontier seat and everything that generates tokens on Sonnet, and I have stopped running the full ceremony on anything I can already specify in a sentence. If you want to argue with that, the way to do it is three runs from the same starting commit, git stash between them so run two never starts with run one’s changes or its warm cache, cost read off the trace rather than off /usage, and the wall-clock written down immediately because we will not remember which was which an hour later. Then keep the ledger alive, because prices move: one of the four rates in this article changed while the book that produced it was being printed.

Now go build something this weekend!

John Cook

Why trust this? Every youcanbuildthings guide is pulled from a build-tested book: code that ran in production before it was written down.