Retrieving the emails of all contacts in a list

Hello,
I am trying to find a way to retrieve the email addresses of all contacts in a list.
From what I can see from the documentation I need to first call the list endpoint below:

https://api.hubapi.com/crm/v3/lists/LIST_ID_HERE/memberships”
and then the only way I can get the email addresses (not the contactIDs) of all my contacts (which are almost 4 digits) is through this endpoint:

https://api.hubapi.com/crm/v3/objects/contacts/CONTACT_ID_HERE"
This is fine when you are dealing with a few contacts within a list but when you have a list as big as mine (almost 1000 contacts) then you get a timeout, understandable, when you try to retrieve the email address of each contact.
So my question is.. Is there a better way to retrieve the email addresses of all contacts in a list where the contacts reach almost 4 digits?

Hi @AntreasPapadopo

We use the Contacts API and the Read Batch Endpoint to get the details of all the Contacts on a List.

You can specify the Properties you want returned, Email is returned by default.

Have fun

Mike

Here to learn more about HubSpot and share my HubSpot Knowledge. I’m the founder of Webalite a Gold HubSpot Partner Agency based in Wellington, New Zealand and the founder of Portal-iQ the world’s first automated HubSpot Portal Audit that helps you work smarter with HubSpot.

I am just replying to this to help anyone who comes up with this in the future. Although Mike’s answer does not answer my question exactly it did point me in the right direction hence why I am marking it as the correct answer.
I was not able to find a way to pass a ListID as a parameter in the Read Batch endpoint but what I did was the following:
Get all contacts from a particular list I need using the below endpoint

$listEndpoint = "https://api.hubapi.com/crm/v3/lists/" . $listID . "/memberships";

This gave me a list of all contacts on said list. Then I used the Read Batch endpoint (

https://api.hubapi.com/crm/v3/objects/contacts/batch/read) and I passed as parameters chunk by chunk (because it only accepts 100 IDs as parameters per call) a list of all my contacts and got from it their first name, surname and email. (Come by default)
Below is a more complete code (PHP)

 // Since we can only pass 100 inputs at a time we chunk the array
 $contactList = array_chunk($contactList, 100);

 // Loop through the chunks to generate our email list
 foreach ($contactList as $chunk) {
 $postfields = [
 "inputs" => $chunk,
 ];

 curl_setopt($this->ch, CURLOPT_URL, "https://api.hubapi.com/crm/v3/objects/contacts/batch/read");
 curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, "POST");
 curl_setopt($this->ch, CURLOPT_POSTFIELDS, json_encode($postfields));

 //Get the result and turn it into an array we can use
 $result = json_decode(curl_exec($this->ch), true);

 if (!empty($result['results'])) {
 foreach ($result['results'] as $contact) {
 $displayName = $contact['properties']['email'];
 // Sometimes we do not have first and last name so we will use the email as the displayName (which cannot be null)
 if (!is_null($contact['properties']['firstname']) && !is_null($contact['properties']['lastname'])) {
 $displayName = implode(' ', array($contact['properties']['firstname'], $contact['properties']['lastname']));
 }
 //Add it to our final list
 $emailList[] = 
 [
 "emailAddress" => $contact['properties']['email'],
 "displayableName" => $displayName
 ];
 }
 }
 }
 return $emailList;
 /**
 * Reccursive function to get all contact IDs from a list
 */
 private function retriveContacts($endpoint, $contactList)
 {
 curl_setopt($this->ch, CURLOPT_URL, $endpoint);

 //Get the result and turn it into an array we cam use
 $result = json_decode(curl_exec($this->ch), true);

 // Get the list of all contacts in an array
 foreach ($result['results'] as $contact) {
 $contactList[] = [
 'id' => $contact['recordId']
 ];
 }

 // If we have not collected all contacts we call the new endpoint
 if (isset($result['paging']['next']['link']) && !empty($result['paging']['next']['link'])) {
 $contactList = $this->retriveContacts($result['paging']['next']['link'], $contactList);
 }

 curl_close($this->ch);
 return $contactList;
 }

@Mike_Eastwood
Thank you for your prompt response but what I fail to realise is how you specify the List when using said endpoint (POST /crm/v3/objects/contacts/batch/read).
Unless you call the List API endpoint first (GET /crm/v3/lists/{listId}/memberships), retrieve all contact IDs and add them as post fields on the first endpoint.
Feel free to correct me if I am wrong.

next.js implementation
import { NextApiRequest, NextApiResponse } from ‘next’;

const accessToken: string = process.env.HUBSPOT_AUTHORIZATION_TOKEN ?? ‘’;

const fetchContactsFromList = async (listId: number): Promise<string[]> => {

let contactList: string[] = [];

let endpoint = `https://api.hubapi.com/crm/v3/lists/${listId}/memberships\`;

while (endpoint) {

const response = await fetch(endpoint, {

method: ‘GET’,

headers: {

Authorization: `Bearer ${accessToken}`,

‘Content-Type’: ‘application/json’,

},

});

if (!response.ok) {

throw new Error(

`Failed to fetch contacts from list: ${response.statusText}`,

);

}

const data = await response.json();

const contacts = data.results.map((contact: any) => contact.recordId);

contactList = contactList.concat(contacts);

endpoint = data.paging?.next?.link || ‘’;

}

return contactList;

};

const fetchContactDetails = async (contactIds: string[]): Promise<any[]> => {

const chunks = chunkArray(contactIds, 100);

let detailedContacts: any[] = [];

for (const chunk of chunks) {

const response = await fetch(

https://api.hubapi.com/crm/v3/objects/contacts/batch/read’,

{

method: ‘POST’,

headers: {

Authorization: `Bearer ${accessToken}`,

‘Content-Type’: ‘application/json’,

},

body: JSON.stringify({

inputs: chunk.map((id) => ({ id })),

}),

},

);

if (!response.ok) {

throw new Error(

`Failed to fetch contact details: ${response.statusText}`,

);

}

const data = await response.json();

detailedContacts = detailedContacts.concat(data.results);

}

return detailedContacts;

};

const chunkArray = (array: any[], size: number) => {

return Array.from({ length: Math.ceil(array.length / size) }, (v, i) =>

array.slice(i * size, i * size + size),

);

};

export default async function handler(

req: NextApiRequest,

res: NextApiResponse<any | { error: string }>,

): Promise<void> {

const listId: number = 9;

try {

const contactIds = await fetchContactsFromList(listId);

const detailedContacts = await fetchContactDetails(contactIds);

const emailList = detailedContacts.map((contact) => {

const { email, firstname, lastname } = contact.properties;

const displayName =

firstname && lastname ? `${firstname} ${lastname}` : email;

return {

emailAddress: email,

displayableName: displayName,

};

});

res.status(200).json(emailList);

} catch (e: any) {

res.status(500).json({ error: e.message });

}

}

respnse:

[

***{***

    ***"emailAddress": "bh@hubspot.com",***

    ***"displayableName": "Brian Halligan"***

***},***

***{***

    ***"emailAddress": "emailmaria@hubspot.com",***

    ***"displayableName": "Maria Johnson"***

***}***

]