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.