Skip to main content

Errors & retries

Branch on success, not on the status code

Every failure — validation, upstream, rate limiting — returns the same envelope:

{
"success": false,
"error": {
"code": "MISSING_PARAM",
"message": "marketUid is required",
"details": null
}
}
FieldUse
error.codeStable and machine-readable. Branch on this.
error.messageFor humans and logs. Wording may change; do not parse it.
error.detailsOptional extra context; shape varies by code.

The HTTP status is not a reliable signal. A missing required parameter currently comes back as 502 with ORIGIN_FAILED, not as 400:

curl -i "https://portal.1delta.io/v1/data/lending/pools"   # no chainId
HTTP/2 502
{
"success": false,
"error": {
"code": "ORIGIN_FAILED",
"message": "All origins failed",
"details": [
{ "origin": 0, "status": 500, "details": "{\"ok\":false,\"error\":\"chainId is required\"}" }
]
}
}

So the only correct check is:

const body = await res.json();
if (!body.success) {
throw new Error(`${body.error.code}: ${body.error.message}`);
}

Checking res.ok first will mislabel a validation mistake as an outage.

Codes

CodeMeaningRetry?
MISSING_PARAMA required parameter was absent.No — fix the request.
INVALID_PARAMPresent but malformed: bad marketUid, unknown lender, non-integer amount.No — fix the request.
NOT_FOUNDNo such market, pool, or position.No.
VALIDATION_FAILEDA pre-flight on-chain check could not be confirmed.Sometimes — often an RPC hiccup.
ACTION_FAILEDThe transaction could not be built: no route, insufficient liquidity, unsupported path.Sometimes — retry with different amounts or slippage.
ORIGIN_FAILEDAn upstream data source or protocol origin failed. Also what a missing parameter currently produces.Yes, if the request itself is valid.

Statuses you should handle

StatusMeaningHandling
200Success. Still check success in the body.
400Validation error.Fix the request.
404No such resource.Fix the request.
429Rate limited.Back off exponentially, then retry.
500Server error.Retry with backoff.
502Upstream origin failed — or a bad parameter. Inspect error.code.Retry only if the request is valid.

Rate limits

Requests are limited per IP when unauthenticated. Send an API key to raise the limit:

curl -H "x-api-key: YOUR_KEY" "https://portal.1delta.io/v1/data/chains"

Get a key at auth.1delta.io.

An unrecognised key is ignored, not rejected — a bad key never causes a 401, it just leaves you on the unauthenticated limit. If you are being throttled while sending a key, verify the key is actually valid rather than assuming the header is being read.

Treat 429 as retryable with exponential backoff and jitter rather than coding against a fixed budget.

async function call(url: string, attempt = 0): Promise<any> {
const res = await fetch(url, { headers: { "x-api-key": process.env.ONEDELTA_KEY! } });

if (res.status === 429 || res.status >= 500) {
if (attempt >= 4) throw new Error(`giving up after ${attempt} retries: ${res.status}`);
const backoff = 2 ** attempt * 500 + Math.random() * 250;
await new Promise((r) => setTimeout(r, backoff));
return call(url, attempt + 1);
}

const body = await res.json();
if (!body.success) throw new Error(`${body.error.code}: ${body.error.message}`);
return body;
}

Note this still retries a 502 caused by a bad parameter. Inspect error.code === "ORIGIN_FAILED" versus a validation code if you want to avoid burning retries on a request that can never succeed.

Failures that happen on-chain, not in the API

A 200 with calldata does not guarantee the transaction succeeds. The usual causes of a revert after a successful build:

  • An approval from actions.permissions was not mined before the action was sent.
  • More than one entry from actions.alternatives was executed.
  • The quote went stale — rates and routes move every block.
  • slippage was too tight for the route, or was passed as a percentage instead of basis points.
  • The position's health factor would drop below the liquidation threshold.

Simulate first where the endpoint supports it: POST the same parameters with the user's current balanceData and aprData, or pass simulate=true on lending actions, and inspect the projected health factor before signing.