Home/Blog/API Error Handling

Betfair API Error Handling: The Common Issues and How to Fix Them

Most Betfair API problems are not mysterious — they are a handful of named errors that every bot meets sooner or later. The difference between a fragile script and a reliable one is not avoiding errors; it is recognising each error code, knowing whether to retry, re-authenticate, adjust or abandon, and reading the execution report correctly when an order half-fails. This guide walks the errors you will actually hit and the handling that deals with each.

Updated June 202611 min readAdvanced
Quick Answer

The most common Betfair API errors are INVALID_SESSION_INFORMATION (re-login or keep-alive), BET_TAKEN_OR_LAPSED and INVALID_ODDS (the price moved — re-read and re-price), MARKET_SUSPENDED / MARKET_NOT_OPEN_FOR_BETTING (wait, don't retry), INSUFFICIENT_FUNDS, and TOO_MANY_REQUESTS / TOO_MUCH_DATA (back off or lighten the request). Always read the execution report's status and per-instruction error codes, not just whether the call returned.

This page contains affiliate links — if you open an account through them we may earn a commission at no cost to you. It never changes our verdict.

This is a sub of our Betfair API developer guide, and it picks up where the rate-limits guide leaves off: once you are inside the limits, the next thing that breaks a bot is mishandling the errors the API genuinely returns in normal operation. Some are your fault, some are the market changing under you, and some are transient infrastructure. Treating them all the same — or worse, ignoring the response and assuming a returned call means a placed bet — is how money quietly leaks out of an automated strategy.

The three categories of error

It helps to sort Betfair API errors into three buckets, because each demands a different response. Authentication and session errors mean the API will not talk to you at all until you fix your credentials or token. Request and data errors mean the call itself was malformed or too large — the fix is in your code, not the market. Betting execution errors are the subtle, important ones: the call succeeded as an API request, but the bet did not get placed as you intended, because the price moved, the market suspended, or your account state blocked it. The third bucket is where most real losses hide, because a naive bot logs “order sent” and moves on, never noticing the order actually lapsed.

Authentication and session errors

These stop everything. INVALID_SESSION_INFORMATION is the one you will see most: your session token has expired or is invalid, and the answer is to re-authenticate (or, better, to have kept the session alive). NO_SESSION and NO_APP_KEY mean you forgot to send the token or the application key header; INVALID_APP_KEY means the key is wrong or not the one you think. On the login side, non-interactive (bot) login requires a certificate, so CERT_AUTH_REQUIRED means you tried to log in programmatically without one. And watch for account-state responses like ACCOUNT_NOW_LOCKED or PENDING_AUTHENTICATION — those are not code bugs, they are Betfair telling you to sort something out on the account itself. The crucial habit is to keep your session alive with periodic keep-alive calls so you rarely hit INVALID_SESSION_INFORMATION in the first place, and to handle it gracefully (re-login, then retry the original call) when you do.

Request and data errors

These are self-inflicted and fixable in code. INVALID_INPUT_DATA means your request body is malformed — wrong types, a bad enum value, a missing required field. TOO_MUCH_DATA means you breached the 200-point market-data weighting cap covered in the rate-limits guide; never retry it unchanged, lighten the projection or split the markets. TOO_MANY_REQUESTS means you hit a transaction or concurrency limit; back off and retry with increasing delay. REQUEST_SIZE_EXCEEDS_LIMIT means the request payload itself is too big. And transient ones like SERVICE_BUSY or TIMEOUT_ERROR are Betfair’s side being momentarily slow — these are the legitimate candidates for a short, bounded retry. The rule that keeps you sane: retry transient infrastructure errors with backoff; never blindly retry a validation error, because it will fail identically every time.

Betting execution errors

This is the category that costs money, so it deserves the most care. When you call placeOrders, the response is an execution report, and individual instructions can fail even when the overall call returned. The errors you will meet constantly in live trading include: BET_TAKEN_OR_LAPSED (you asked for a price that is no longer available — the market moved between your read and your order); INVALID_ODDS (the price you sent is not a valid Betfair tick); BELOW_MINIMUM_BET_SIZE or INVALID_BET_SIZE (your stake is under the minimum or malformed); INSUFFICIENT_FUNDS (not enough balance for the liability); MARKET_SUSPENDED and MARKET_NOT_OPEN_FOR_BETTING (the market is in-play-suspended or closed — common in fast in-play moments); RUNNER_REMOVED (a non-runner); and LOSS_LIMIT_EXCEEDED if you have set responsible-gambling limits. Each needs a tailored response, not a generic retry — re-pricing for BET_TAKEN_OR_LAPSED, waiting for MARKET_SUSPENDED, stopping for INSUFFICIENT_FUNDS.

Reading the execution report properly

Non-runners, price changes and the surprises in racing

Horse racing throws a specific family of errors that catch out anyone moving a bot from football or tennis: the non-runner. When a horse is withdrawn, Betfair applies a Rule 4 reduction and the market behaves differently, and your resting orders on that runner return RUNNER_REMOVED. Your handling has to expect runners to disappear between your market read and your order, recalculate liabilities after a withdrawal, and not treat a removed runner as a code bug. Similarly, BET_LAPSED_PRICE_IMPROVEMENT_TOO_LARGE can appear when the price has moved so far in your favour that Betfair lapses the bet rather than matching it at an implausibly good price — a protection, not an error in your logic. The general principle is that the market is a live, changing thing, and a robust bot re-reads state immediately before acting rather than trusting a snapshot taken seconds earlier. Pre-race racing markets in particular move fast in the final minutes, so the gap between “what I saw” and “what is true now” is exactly where these errors live.

The single most important skill is reading the execution report at all three of its levels, because a bot that only checks “did the HTTP call succeed” is flying blind. At the top, the report has a status: SUCCESS, FAILURE, PROCESSED_WITH_ERRORS or TIMEOUT. The middle dangerous one is PROCESSED_WITH_ERRORS — it means some instructions worked and some did not, so you must inspect each one. Below that sits an overall error code (things like BET_ACTION_ERROR or INVALID_ACCOUNT_STATE), and below that, crucially, a per-instruction report with its own status and error code for each individual bet. You must drill into the instruction reports to know which bets actually matched, at what size and price, and which lapsed. A TIMEOUT status is the trap: the bet may have been placed even though you did not get confirmation, so you must reconcile against listCurrentOrders before assuming anything — never just resend.

From the desk — an INVALID_SESSION_INFORMATION that nearly left a position open

The setup: a bot holding an open back position on a football match-odds market, waiting to lay off and green up. It had been running quietly for over an hour while I did other things.

The failure: when the hedge condition triggered, the placeOrders call to lay off came back not with a fill but with INVALID_SESSION_INFORMATION. The session token had silently expired during the idle period — I had not implemented keep-alive — so the bot could not place the closing trade and the position sat open while the price drifted.

What I did wrong: the original code treated any failed placeOrders as “skip and try next cycle,” so it kept looping and failing on the dead token instead of re-authenticating. The position stayed unhedged for the better part of a minute — in a moving market, that is real exposure I never intended to carry.

The fix: I added two things. First, a keep-alive call on a timer so the session never goes stale during idle periods. Second, explicit handling: on INVALID_SESSION_INFORMATION, re-login immediately, then retry the original order rather than waiting for the next cycle. With both in place, a token expiry became a sub-second blip instead of an open-position scare.

The lesson: authentication errors are not edge cases to log and ignore — on an order that closes a position, they are urgent. Keep the session alive proactively, and make session-recovery automatic and immediate. The bot that re-authenticates and retries in the same breath never notices the problem; the one that doesn’t leaves money on the table.

Avoiding duplicate bets after timeouts

The nastiest failure mode in automated betting is the duplicate: you send an order, the connection times out before you get a response, you assume it failed and resend — and now you have two bets where you wanted one. Betfair gives you the tool to prevent this: the customer reference field on instructions. By attaching a unique reference to each order, you make placement idempotent — if a timeout leaves you unsure whether the bet landed, you can check or resend safely because Betfair can recognise the duplicate. The discipline is simple but non-negotiable for any bot that handles real money: never resend an order after a TIMEOUT without first reconciling against listCurrentOrders to see whether it actually went on. Assuming a timeout means failure is how traders wake up to double stakes and unintended liability.

What to retry, and what never to

Getting retry logic right is most of error handling. Retry with exponential backoff: transient infrastructure errors — SERVICE_BUSY, TIMEOUT_ERROR, TOO_MANY_REQUESTS — where the request is valid and will likely succeed shortly. Re-price and retry: BET_TAKEN_OR_LAPSED, where the market simply moved — re-read the book, decide if the trade still makes sense at the new price, and only then resend. Wait, then retry: MARKET_SUSPENDED, which clears on its own when the market resumes. Never retry unchanged: validation errors like INVALID_INPUT_DATA, INVALID_ODDS, TOO_MUCH_DATA — they are deterministic and will fail identically, so fix the request first. Stop and alert: INSUFFICIENT_FUNDS, ACCOUNT_NOW_LOCKED, REJECTED_BY_REGULATOR — these need a human, not a retry. Encoding this decision table is what separates a bot that self-heals from one that either gives up too early or hammers a doomed request forever.

A practical handling pattern

In practice, wrap every API call in a layer that classifies the response before your strategy logic ever sees it. A clean pattern looks like this: catch the exception or read the error code; map it to one of the five actions above (backoff-retry, re-price, wait, fix, or stop); for order calls, always parse the full execution report and per-instruction reports rather than trusting the top-level status; log every error with the request that caused it and the action taken; and surface anything in the “stop and alert” class to you immediately. Build this once, as a reusable wrapper, and every strategy you run on top inherits robust handling for free. It pairs naturally with sensible rate-limit discipline, a live data feed from the Stream API, and an honest understanding of the things that go wrong with bots. None of it is glamorous, but it is the difference between a bot you can leave running and one you have to babysit.

Logging so you can diagnose later

Errors you cannot see are errors you cannot fix, and a bot that swallows exceptions silently is the worst kind of unreliable — it loses money in ways you only discover from the statement. Log every API error with three things: the exact request that triggered it, the full error response including the execution report, and a timestamp precise enough to line up against market movements. When something goes wrong at 14:32 you want to be able to reconstruct exactly what the bot asked for and what Betfair said back. Keep a running count of each error type, too: a slow rise in BET_TAKEN_OR_LAPSED tells you your pricing is lagging the market; a spike in TIMEOUT_ERROR points at the connection or Betfair’s load; recurring INVALID_INPUT_DATA means a bug you have not found. Good logging turns error handling from guesswork into diagnosis, and it is the cheapest insurance you can build into an automated strategy.

The verdict

Error handling is not a bolt-on; on the Betfair API it is the core of writing anything that touches real money. The errors themselves are finite and well-named — learn the dozen that matter, sort them into retry / re-price / wait / fix / stop, keep your session alive, make placement idempotent with a customer reference, and always read the execution report down to the per-instruction level. Do that and the API stops feeling unpredictable: every failure has a known, coded response, and your bot recovers from the ordinary chaos of live markets instead of being broken by it. Build the foundations in the developer guide and the Python tutorial, respect the rate limits, and test every path on the delayed key before you ever run it live.

FAQ

What does INVALID_SESSION_INFORMATION mean on the Betfair API?

Your session token has expired or is invalid, so the API will not process the request. Fix it by re-authenticating and then retrying the original call immediately. Better, prevent it by sending periodic keep-alive calls so the session never goes stale during idle periods.

What is the difference between BET_TAKEN_OR_LAPSED and INVALID_ODDS?

BET_TAKEN_OR_LAPSED means the price you asked for is no longer available because the market moved — re-read the book and re-price. INVALID_ODDS means the price you sent is not a valid Betfair tick value at all — round it to the nearest valid increment before resending. One is a market problem, the other is a code problem.

Why did my placeOrders call succeed but no bet was placed?

Because the API call succeeding is not the same as the bet matching. The execution report can return PROCESSED_WITH_ERRORS, meaning some instructions failed — you must inspect the per-instruction reports to see which bets matched and which lapsed. Always read the report down to instruction level, never just the top status.

How do I avoid placing duplicate bets after a timeout?

Attach a unique customer reference to each order to make placement idempotent, and never resend after a TIMEOUT status without first checking listCurrentOrders to see whether the bet actually went on. Assuming a timeout means failure and blindly resending is the classic way to end up double-staked.

Which Betfair API errors should I retry?

Retry transient infrastructure errors — SERVICE_BUSY, TIMEOUT_ERROR, TOO_MANY_REQUESTS — with exponential backoff. Re-price and retry BET_TAKEN_OR_LAPSED. Wait then retry MARKET_SUSPENDED. Never retry validation errors like INVALID_INPUT_DATA or TOO_MUCH_DATA unchanged, and stop for INSUFFICIENT_FUNDS or account-locked errors, which need human action.

Start at the Betfair API developer guide and the getting-started flow, set up your application key, then build in Python or Node.js. Stay inside the rate limits, take a live feed from the Stream API, and understand the broader risks of automated trading before going live.

Risk note

A bot that mishandles errors can leave positions open, place duplicate bets, or churn money far faster than you can intervene. Most Betfair traders lose money overall, and automation amplifies mistakes as readily as edges. Test every error path on the delayed application key with small stakes before trusting real money to it. Past results don’t guarantee future returns. 18+ only; help at BeGambleAware.org.

Sort every error into retry, re-price, wait, fix or stop — keep the session alive, make orders idempotent, and read the full execution report.

Betfair API Developer Guide Open Betfair Account →