DEV Community

arun rajkumar
arun rajkumar

Posted on

The UK Treasury is asking whether bank APIs should stay free. Do the maths now.

Every open banking pitch has a line in it that nobody reads twice: the bank APIs are free.

That's true today. It's also the biggest assumption in most open banking business plans, and HM Treasury has now asked, in writing, whether it should stay true.

What the Treasury actually said

In July, HM Treasury put out a consultation on modernising the Payment Services Regulations. It closed on 6 October. The open banking bits, as I read them:

  • A new statutory right of access for VRPs.
  • A worry that free access gives banks little reason to invest in their APIs.
  • FCA oversight of open banking pricing, with the door open to charging for some kinds of access.
  • A future framework where the FCA has a say over standards, participation, pricing, funding and disputes.

It also asks whether the rules on authentication, consent and liability still work when an AI agent is the one initiating the payment.

Two caveats before anyone panics. Nothing is in force, and the legislation isn't expected until 2027 or 2028. And I'm working from public summaries, so read the consultation itself before you quote me.

The newest rail already has a price attached, though. In January the FCA and PSR said they wouldn't prioritise a competition investigation into the central access fee that UKPI is building for commercial VRP. That position runs until legislation arrives or July 2027, whichever is sooner. So "free" is already only half the story.

Why I care

We're a payment initiation provider, so I'm not a neutral observer. Bank API access sits underneath everything we do. If the cost of it moves, we feel it first and our merchants feel it second.

If you build on open banking, the useful question isn't whether banks will charge. It's whether your margin survives if they do.

That takes about ten minutes to answer. This is the script I'd start with:

// Does the product still work if bank API access stops being free?
const monthlyTxns = 20_000;
const yourFeePence = 25;     // what you charge per payment
const otherCostsPence = 6;   // infra, support, fraud ops. Use your real numbers.

for (const accessFeePence of [0, 1, 5, 10]) {
  const marginPence = yourFeePence - otherCostsPence - accessFeePence;
  const monthly = (marginPence * monthlyTxns) / 100;
  console.log(`${accessFeePence}p access fee: ${marginPence}p/txn, £${monthly}/month`);
}
Enter fullscreen mode Exit fullscreen mode

Those numbers are made up. They aren't Atoa's. The only output that matters is the access fee at which your margin hits zero. Know that number before a regulator or a bank tells you theirs.

What I'd do

Find your break-even fee, and work it out per payment, per API call, and per VRP if you do recurring.

Give bank API cost its own line in your pricing model. Burying it in "infra" is how you miss it.

Be wary of pricing as a percentage of the basket. If access charges arrive, I'd guess they're per call or per payment, not per pound. A fee that's trivial on a £4,000 order can wipe out a £4 one.

And if you're building anything agentic, start logging the consent and authentication evidence for each payment now. If liability rules change, that's the record you'll be asked for.

The other side of it

Charging isn't automatically bad. Free access is part of why some bank APIs are patchy, and a paid API with a real SLA might be a better thing to build on. Cards carry fees too, so the honest comparison is all-in cost against interchange and scheme fees, not against zero.

I still think account-to-account stays cheaper than cards. But "cheaper than cards" and "free" are different claims, and only one of them belongs in a business plan.

If you want to see the payment flow end to end, the Atoa sandbox is free: docs.atoa.me

So, what does your margin look like at 5p a payment?

Top comments (9)

Collapse
 
khalisollis profile image
Khali Sollis •

This is a brilliantly practical and sharp wake-up call for anyone building in the open banking space. We’ve all gotten so used to treating "free bank APIs" as an immutable law of nature, forgetting that it’s essentially an artifact of regulatory subsidy rather than a sustainable market equilibrium.

The author makes a crucial point: free access often translates to underfunded, patchy infrastructure. A paid model with actual SLAs and accountability might actually elevate the ecosystem—if the pricing structure doesn't crush low-basket-size transactions in the process. Dropping that little code snippet to test break-even margins is absolute gold. It’s the kind of gritty, operational realism that separates a viable business model from a fragile pitch deck waiting on a regulatory rug-pull.

Collapse
 
mickyarun profile image
arun rajkumar •

The low-basket point is the one that actually bites, and the post undersold it. Two people in this thread found the arithmetic wrong in my favour.

Break-even on a £25 basket is 25 − 6 = 19p, and the script in the post only loops 0/1/5/10p — so it cannot print the number the post calls the only one that matters. @ilragioniere caught that. The sharper half is that 6p is an invented figure: at a real 14p of other costs, break-even is 11p, and the 10p row still prints a positive margin while sitting at 91% of the cliff.

The fee isn't even the bigger term. At £2,000/mo of fixed cost and 14,000 completions, fixed is 14.3p of that 19p before a bank charges anything. So "will this fee kill my margin" and "does this business work at this volume" are two different questions sharing one piece of arithmetic, and the post pointed at the smaller one.

On the SLA trade you raise: paid access with real accountability would lift the floor, but the term to watch is whether the fee is per call or per payment. Per call prices your retry policy, and status polls are most of the call count — @arhancanli's point, and it's the only term in the chain an engineer actually controls.

Collapse
 
ilragioniere profile image
Riccardo Vitali •

On the fallback: you are right, and I compared the wrong two things. The merchant pays something either way, so the fallback costs the lost saving rather than the gross card fee. On a 25p payment that is 32.5p, about 1.3 completed payments, not more than two. I priced the A2A at zero and then charged the card fee in full, which is the same mistake as reading a fee against revenue instead of against the alternative.

The split you named has a second consequence. If break-even is volume-free and viability is not, one script cannot answer both, and a row that mixes them is the least stable thing on the page while looking like the most. Your -2.4p at 14,000 completions is roughly 3,000 completions from flipping sign: at 17,000 the fixed term is 11.8p and the row sits at about +0.1p. Nothing in a single-volume table warns a reader how close the boundary is, and the row that gets quoted after someone's traffic changes is usually one of those.

So: two scripts, and a flag on near-boundary rows rather than on negative ones. The fee question has an answer at any volume. The business question has none until someone states a volume, and the honest output would say that instead of printing a margin that implies it has one.

Collapse
 
ilragioniere profile image
Riccardo Vitali •

Two answers, since you asked for the number at 5p.

The script cannot print the number you say is the only one that matters. Break-even is fee minus other costs, so 19p per payment. The loop runs 0, 1, 5, 10p. Margin never reaches zero, so the one output you told people to look for is the one the loop cannot produce. Compute it directly, or run the array past 19.

Worth adding: break-even is a per-payment ratio, so the 20,000 never enters it. Volume sizes the monthly damage, not the cliff. That makes the invented volume harmless and the invented other-costs line the risky one: if someone's real infra cost is 14p, their break-even is 11p, and 10p has already eaten most of it. Your post says "use your real numbers". The script just does not reward anyone for doing it.

On the rail already having a price. The January FCA/PSR position is that they will not prioritise a competition investigation into UKPI's central access fee. That is a regulator declining to spend on a case, with an expiry date, not a fee that has been priced and approved. Both readings point the same way, but they are different sentences, and yours is the more defensible one: the fee exists and nobody is challenging it yet. "The rail has a price" reads as settled, and nothing has been settled.

One thing on the agentic paragraph: per call and per payment only diverge where the agent retries or polls. That is also where the authorisation evidence has to be strongest, so the cost model and the liability log are the same design decision, not two.

Collapse
 
mickyarun profile image
arun rajkumar •

You are right, and it is worse than a loop that stops short.

The post says the only output that matters is the fee where margin hits zero, then ships a loop over 0, 1, 5, 10. Break-even is 25 − 6 = 19. The loop cannot reach it. I told people what to look for and handed them a tool that cannot print it. The fix is not a longer array, it is one line: const breakEven = yourFeePence - otherCostsPence. I searched for something I could have solved.

Your second point lands harder than the first. If break-even is a per-payment ratio then the 20,000 is decoration, and I caveated the invented number that does not matter. The one that does is the 6p. Break-even moves 1p for every 1p of other costs, so someone running 14p of real infra has a break-even of 11p, and the 10p row in my loop has already taken 91% of it while printing a positive margin. Anyone whose costs are above mine reads my output as comfortable while being nearly underwater.

On the FCA line, I will take your sentence. "Will not prioritise an investigation, until legislation or July 2027" is a regulator declining to spend, with an expiry date. Mine says the newest rail "already has a price attached", which reads as a fee that has been through a process and come out the other side. The fee exists and nobody is challenging it yet. That is the version I can carry.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.