Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.o1.exchange/llms.txt

Use this file to discover all available pages before exploring further.

The API uses HTTP status codes plus a JSON body of the form { "error": "<message>" } for every error. This guide covers what each status code means and the right response.

Status code reference

StatusMeaningRetry?
200Successn/a
400Validation error or no routes availableNo (fix the request)
401Missing or invalid x-api-keyNo (fix the key)
404Quote expired (only on /submit)Yes (re-quote)
429Rate limit exceededYes (backoff)
5xxInternal errorYes (backoff with jitter)

Common error messages

{ "error": "amountIn must be > 0" }
{ "error": "no routes for pair" }
{ "error": "slippageBps out of range" }
{ "error": "invalid token address" }
Treatment: show the user a clear message (“This pair has no liquidity right now”), do not retry. If no routes for pair happens often for a specific token, the token may not have routable on-chain liquidity on Base.

On-chain transaction failures

Errors above are HTTP errors. Once you have a submit response and broadcast the transaction, a separate class of failures can happen on-chain:
Revert reasonLikely causeFix
SlippagePool state moved between /quote and inclusion.Increase slippageBps, or re-quote and resend.
TRANSFER_FROM_FAILED / INSUFFICIENT_ALLOWANCEAllowance for tokenIn is below amountIn.Approve the router for amountIn, or use a permit payload.
TRANSFER_FROM_FAILED (with sufficient allowance)The user’s balance is below amountIn.Validate balance before submitting.
Permit failedPermit signature invalid or already consumed.Re-sign with a fresh nonce; verify the EIP-712 domain matches the token.
Out of gasGas estimate was too low (rare; happens during chain congestion).Re-broadcast with a higher gas limit, e.g. gasEstimate.gasUnits × 1.5.
The on-chain Slippage revert is intentional. It means the safety net worked. If a user complains, the most common fix is “use slightly higher slippage” or “re-quote and retry quickly.”
async function callWithRetry<T>(fn: () => Promise<Response>, maxRetries = 2): Promise<T> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const res = await fn();

    if (res.ok) return res.json();

    const body = await res.json().catch(() => ({}));

    // Non-retryable: fix the request and return
    if (res.status === 400 || res.status === 401) {
      throw new Error(body.error ?? `${res.status}`);
    }

    // 404 on /submit: caller must re-quote. Bubble up a typed error.
    if (res.status === 404) {
      throw new Error("QUOTE_EXPIRED");
    }

    // 429 / 5xx: back off and retry
    if (attempt === maxRetries) {
      throw new Error(body.error ?? `${res.status}`);
    }

    const baseDelay = res.status === 429 ? 1000 : 1500;
    const jitter = Math.random() * 500;
    await new Promise((r) => setTimeout(r, baseDelay + jitter));
  }
  throw new Error("unreachable");
}
Then in the submit handler:
try {
  const submit = await callWithRetry(() =>
    fetch(`${API_URL}/submit`, { /* ... */ }),
  );
  // broadcast as usual
} catch (e) {
  if (e.message === "QUOTE_EXPIRED") {
    const fresh = await refreshQuote(params);
    // retry submit with fresh.quoteId
  } else {
    showError(e.message);
  }
}

What NOT to do

Don't retry 400 errors

400 means the request itself is malformed or unroutable. Retrying doesn’t help; fix the request.

Don't hammer on 429

The rate limit window is per API key. Retrying immediately after a 429 makes the situation worse and burns the rest of your minute. Always back off.

Don't surface raw errors to users

Translate error strings into UI-friendly copy. “no routes for pair” → “Couldn’t find liquidity for this pair, try a different amount or token.”

Don't ignore on-chain reverts

Always check receipt.status === "success" after waitForTransactionReceipt. A 0x tx hash is not the same as a successful swap.