How to fetch complete contact list from HubSpot API when dealing with pagination limits

I need help with retrieving all my contacts from HubSpot and saving them into one JSON file. The problem is that their API only gives me 100 contacts at a time, so I have to handle pagination properly.

I looked at some existing solutions but they either use outdated libraries or don’t work with current API versions. I prefer to write my own simple code instead of adding external dependencies.

Here’s what I tried, but it gets stuck in an infinite loop. I think the issue is with how I’m updating the pagination variables:

<?php

function fetchContacts($vidOffset = 0) {
    $fields = "&property=email&property=lastname&property=status&property=lead_source&property=creation_date";
    $key = "your-api-key-here";
    $endpoint = "https://api.hubapi.com/contacts/v1/lists/all/contacts/recent?hapikey=" . $key . $fields . '&vidOffset=' . $vidOffset;
    
    $ch = curl_init();
    curl_setopt_array($ch, array(
        CURLOPT_URL => $endpoint,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => "",
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => "GET",
        CURLOPT_HTTPHEADER => array(
            "cache-control: no-cache"
        ),
    ));
    
    $result = curl_exec($ch);
    $contactData = json_decode($result);
    
    return $contactData;
}

$completeList = array();
$currentOffset = 0;
$moreRecords = true;

while ($moreRecords === true) {
    $apiResponse = fetchContacts($currentOffset);
    $completeList[] = $apiResponse;
    
    $currentOffset = $apiResponse->{'vid-offset'};
    $moreRecords = $apiResponse->{'has-more'};
    
    echo $moreRecords;
    $moreRecords = false; // This breaks the loop for testing
}

Can someone point out what I’m doing wrong with the pagination logic?

You’re pushing entire API response objects instead of just the contacts array - that’s your problem.

Your pagination logic is mostly right, but extract the actual contacts from each response:

while ($moreRecords === true) {
    $apiResponse = fetchContacts($currentOffset);
    
    // Extract contacts from response
    if (isset($apiResponse->contacts)) {
        $completeList = array_merge($completeList, $apiResponse->contacts);
    }
    
    $currentOffset = $apiResponse->{'vid-offset'};
    $moreRecords = $apiResponse->{'has-more'};
}

Add some error handling for curl failures and check if you’re getting valid JSON back.

Manual API pagination gets messy fast though. I’ve been using Latenode for this exact thing - it handles all the pagination automatically. Just connect HubSpot, tell it what data you want, and it fetches everything in batches without the loop headaches.

You can transform and save the data directly to JSON or whatever format without writing custom code. Way cleaner than managing curl requests and debugging pagination issues.

ur pagination’s solid, but ur storing the whole response object when you just need contacts. Use $completeList = array_merge($completeList, $apiResponse->contacts); to merge just the contact arrays. Also heads up - that API endpoint might be deprecated. Worth checking if u should switch to v3.

Your main problem is you’re storing entire API response objects in $completeList instead of just the contacts. When you do $completeList[] = $apiResponse, you’re creating nested arrays that are a pain to work with later.

Your pagination logic looks fine, but you need to extract contacts from $apiResponse->contacts and merge those into your list instead.

Also watch out for vid-offset being null on the final page - HubSpot’s inconsistent with that value sometimes.

One more thing: the recent contacts endpoint has ordering quirks. If you need all contacts regardless of when they were modified, try the regular contacts endpoint instead. I had the same infinite loop issue and switching endpoints fixed it.