Skip to main content
Refusals are HTTP statuses on the call that was refused. Nothing is started and nothing is billed unless the POST answered 202. Check what you sent before retrying a 401: an API key starts lf_live_ and a session token starts eyJ. A 403 and a 422 will answer the same way for ever, so fix the request rather than repeating it.

402 Payment Required

This is the ordinary out-of-funds signal, not a fault. A search holds 5.00ofyourbalancebeforeitdoesanywork,soacallerwith5.00 of your balance before it does any work, so a caller with 2.00 is refused up front rather than finding out after the providers have been paid.
availableMicrousd is what you may spend, which is your balance minus heldMicrousd, the part already committed to runs in flight. neededMicrousd is the reservation, not the price of the search. The three *Microusd fields are integers in micro-USD; the two *Usd fields are the same figures as strings.
Do not retry a 402 on a timer. Nothing about the request was wrong, so a retry fails identically until the balance changes. It changes in two ways: a top-up, or a run in flight finishing and releasing its hold. Branch on error == "insufficient_balance", surface the message, and either wait for a run to land or send the user to top up. A retry loop here burns quota and tells the user nothing.

When the search itself fails

A run that was accepted and then failed is not an HTTP error anywhere: the POST already returned 202. It shows up as an error event in the stream, followed by cost, usage and done, and the run lands with status: "error". The 402 case leaves one more trace worth knowing about: a run row with status: "error" and no events, because the run document is written before the reservation is attempted. It will appear in GET /v1/searches.

Key management errors