GHL API pagination timing out mid-run on HTTP Request node — looking for reliable fix

I’m using the HTTP Request node with built-in pagination (Response Contains Next URL) to fetch contacts from the GoHighLevel API. My setup:
Pagination mode: Response Contains Next URL
Next URL: {{ $response.body.meta.nextPageUrl || ‘’ }}
Complete when: {{ !$response.body.meta.nextPageUrl }}
Page limit: 100 pages, limit=50 per page
Interval between requests: 1000ms
Timeout: 60000ms
Retry on fail: 3 tries, 5000ms wait
The problem: on larger contact lists (3000-4000 contacts), GHL’s API returns “Request Timeout after 30000ms” on a random page mid-pagination. When this happens n8n stops the entire node and only sends to the contacts it already fetched — so instead of 3500 contacts I might only get 300-600.
The timeout is GHL’s server-side 30s limit, not mine. Retry on fail doesn’t seem to apply per-page during pagination — it only retries the whole node from scratch.
Questions:
Does Retry on Fail actually retry individual failed pages during pagination, or does it restart the whole pagination from page 1?
Is there a way to make pagination resume from where it left off after a timeout?
Would a Loop/Split in Batches approach using the startAfter and startAfterId cursor parameters be more reliable than built-in pagination for this use case?
Any other approaches to make large GHL contact fetches reliable?
Running n8n v2.27.4 self-hosted on DigitalOcean.

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:

Hi @Maria_Loreto
I think so retry on fail reruns the whole node from page 1, it can’t resume from the page that timed out, and the contacts already fetched are dropped when it fails. The reliable fix is to stop using built-in pagination and page manually, so each page is its own request and Retry on Fail applies per page.

Turn Pagination off on the HTTP Request node and build a loop:

  1. HTTP Request fetches a single page, taking the cursor from the previous response as query params:
startAfter: {{ $json.meta?.startAfter || '' }}
startAfterId: {{ $json.meta?.startAfterId || '' }}
  1. On that node, Settings > Retry on Fail (3 tries, 5000ms). A timed-out page now retries on its own instead of restarting the whole run.
  2. Push each page’s contacts to your DB / Sheet / data store right after the request, so pages are saved as you go and a mid-run failure loses nothing.
  3. Add a Wait node (1000ms) to keep your interval, then an IF node checking {{ !!$json.meta.nextPageUrl }}: true loops back into the HTTP Request node, false ends.

Each page stays independently retryable and earlier pages survive a timeout, which built-in pagination can’t give you. Let me know how this goes.