How to implement recursion/looping for HTTP requests in n8n?

Describe the problem/error/question

I want to implement recursion-like behavior in n8n.

I call an HTTP Request node and check the response. If the response is met my threshold or condition, I stop. If not, I modify/split the request body and call the HTTP Request again.

I need to repeat this until I get the correct response.

What’s the best way to achieve this in n8n?

What is the error message (if any)?

Please share your workflow

(Select the nodes on your canvas and use the keyboard shortcuts CMD+C/CTRL+C and CMD+V/CTRL+V to copy and paste the workflow.)

Share the output returned by the last node

Information on your n8n setup

  • n8n version:
  • Database (default: SQLite):
  • n8n EXECUTIONS_PROCESS setting (default: own, main):
  • Running n8n via (Docker, npm, n8n cloud, desktop app):
  • Operating system:

I have implemented a workflow, where you can basically divide a number in half and hit an API till you get the desired result .

{“nodes”: [{“parameters”: {},“id”: “286e0efc-9283-4279-bf6e-3d60dd22dc8b”,“name”: “Start”,“type”: “n8n-nodes-base.manualTrigger”,“typeVersion”: 1,“position”: [240,384]},{“parameters”: {“assignments”: {“assignments”: [{“id”: “globalVar”,“name”: “=globalVar”,“value”: “={{ $json.globalVar !== undefined ? $json.globalVar : 100 }}”,“type”: “number”},{“id”: “attempt”,“name”: “attempt”,“value”: “={{ $json.attempt !== undefined ? $json.attempt : 1 }}”,“type”: “number”}]},“options”: {}},“id”: “922bf361-0814-495d-ab38-2020a8d3c8d3”,“name”: “Initialize Variable”,“type”: “n8n-nodes-base.set”,“typeVersion”: 3.4,“position”: [464,384]},{“parameters”: {“url”: “=https://jsonplaceholder.typicode.com/todos/1”,“options”: {}},“id”: “c503d3c0-8fd0-42c4-bacb-f4943d051230”,“name”: “Call API”,“type”: “n8n-nodes-base.httpRequest”,“typeVersion”: 4.4,“position”: [688,384]},{“parameters”: {“conditions”: {“options”: {“caseSensitive”: true,“leftValue”: “”,“typeValidation”: “strict”,“version”: 3},“conditions”: [{“id”: “status-check”,“leftValue”: “={{ $json.status }}”,“rightValue”: “success”,“operator”: {“type”: “string”,“operation”: “equals”}}],“combinator”: “and”},“options”: {}},“id”: “c0238466-b97c-4ade-9422-9cbfbc8447c2”,“name”: “Check Response”,“type”: “n8n-nodes-base.if”,“typeVersion”: 2.3,“position”: [912,304]},{“parameters”: {“assignments”: {“assignments”: [{“id”: “result”,“name”: “result”,“value”: “API call successful”,“type”: “string”},{“id”: “finalValue”,“name”: “finalValue”,“value”: “={{ $json.globalVar }}”,“type”: “number”}]},“options”: {}},“id”: “ceb8af88-9d0b-4eb0-9219-92088d3d7798”,“name”: “Success - Stop”,“type”: “n8n-nodes-base.set”,“typeVersion”: 3.4,“position”: [1136,208]},{“parameters”: {“assignments”: {“assignments”: [{“id”: “globalVar”,“name”: “globalVar”,“value”: “={{ $(‘Initialize Variable’).item.json.globalVar /2}}”,“type”: “number”},{“id”: “attempt”,“name”: “attempt”,“value”: “={{ $(‘Initialize Variable’).item.json.attempt +1}}”,“type”: “number”}]},“options”: {}},“id”: “226e6ce1-7e10-47b6-b7fc-1a26054e8a08”,“name”: “Halve Variable”,“type”: “n8n-nodes-base.set”,“typeVersion”: 3.4,“position”: [1136,400]},{“parameters”: {“mode”: “runOnceForEachItem”,“jsCode”: “// Pass data forward for next iteration\nreturn { globalVar: $input.item.json.globalVar, attempt: $input.item.json.attempt };”},“id”: “1f0c7da9-d019-41da-8dc5-96875202c1cf”,“name”: “Prepare Retry”,“type”: “n8n-nodes-base.code”,“typeVersion”: 2,“position”: [1360,480]}],“connections”: {“Start”: {“main”: [[{“node”: “Initialize Variable”,“type”: “main”,“index”: 0}]]},“Initialize Variable”: {“main”: [[{“node”: “Call API”,“type”: “main”,“index”: 0}]]},“Call API”: {“main”: [[{“node”: “Check Response”,“type”: “main”,“index”: 0}]]},“Check Response”: {“main”: [[{“node”: “Success - Stop”,“type”: “main”,“index”: 0}],[{“node”: “Halve Variable”,“type”: “main”,“index”: 0}]]},“Halve Variable”: {“main”: [[{“node”: “Prepare Retry”,“type”: “main”,“index”: 0}]]},“Prepare Retry”: {“main”: [[{“node”: “Initialize Variable”,“type”: “main”,“index”: 0}]]}},“pinData”: {},“meta”: {“instanceId”: “0f39d8fdd402ddce20d0eb724526828e8c2d8679170e3a08fe6cd471c799fab6”}}

See if this helps

Thank you for the Workflow.I want something like this but in date and binarysearch in recursion. Ihave python code if you are good can u look into this

def fetch_recursive(start_date, end_date, writer):

print(f"Checking range: {start_date} → {end_date}")

if start_date > end_date:
    return

data = get_data(start_date, end_date, keyword)

if not data:
    return

hit_count = data.get("hitCount", 0)

if hit_count <= 1000:

    for item in data.get("noticeList", []):
        record = item["item"]
        if not seen_ids.get(record['id'], False):
                writer.writerow(record)
                seen_ids[record["id"]]=True

    return

else:

    

    mid_date = start_date + (end_date - start_date) // 2


    # Recursive split
    fetch_recursive(start_date, mid_date, writer, keyword)
    fetch_recursive(mid_date + timedelta(days=1), end_date, writer, keyword)

Good question — worth clarifying first: n8n doesn’t support true recursion (a node calling itself or triggering a sub-execution inline), but the loop-back pattern is the exact equivalent and works great.

Two practical approaches:

Option 1 — Code node with while loop (cleanest for retry-until-condition)

All logic in a single node, runs as one execution, easy to add a max-iterations guard:

javascript
let body = { value: $input.first().json.startValue };
let result;
let attempts = 0;
const MAX_ATTEMPTS = 20;

while (attempts < MAX_ATTEMPTS) {
  const response = await this.helpers.httpRequest({
    method: 'POST',
    url: 'https://your-api.example.com/endpoint',
    body,
    json: true,
  });

  if (response.status === 'success') { // replace with your condition
    result = response;
    break;
  }

  // modify body for next iteration
  body.value = body.value / 2;
  attempts++;
}

if (!result) throw new Error(`Condition not met after ${MAX_ATTEMPTS} attempts`);
return [{ json: result }];
```

**Option 2 — Loop Over Items node (visual approach)**

As others mentioned: Set (initialize) → HTTP Request → IF (check condition) → true: done; false: modify body → route back into Loop Over Items. Good if you need to inspect intermediate results in the UI.

**⚠️ Gotcha: never put a Wait node inside the Loop Over Items loop.** n8n treats the Wait as a sub-execution boundary and the loop never terminates — you'll get a runaway execution. If you need a delay between retries, use a Code node with a small inline delay instead.

For pure retry-until-condition logic with no delay needed, Option 1 (Code node while loop) is the most reliable — single execution, full control, no edge cases.

Thanks for solution actually i am trying to write code in js but in my case getting issue in time i have knowledge on python but i don’t know js datetime how it works in my code facing this issue if this fixes i think my workflow works can u help me to fix my js code

const startDate = new Date($input.item.json.currentStartDate);
const endDate = new Date($input.item.json.currentEndDate);

const timeDiff = endDate.getTime() - startDate.getTime();
const midDate = new Date(startDate.getTime() + timeDiff / 2);
const nextDate = new Date(midDate.getTime() + 86400000); // +1 day

const formatDate = (date) => date.toISOString().split(‘T’)[0];

return [
{
json: {
currentStartDate: formatDate(startDate),
currentEndDate: formatDate(midDate),
startDate: $input.item.json.startDate,
endDate: $input.item.json.endDate
}
},
{
json: {
currentStartDate: formatDate(nextDate),
currentEndDate: formatDate(endDate),
startDate: $input.item.json.startDate,
endDate: $input.item.json.endDate
}
}
];

facing issue in bold line