How to integrate Microsoft Graph API with n8n for Exchange contact data

Hi everyone! I’m working on a project where I need to pull contact information from our Exchange server and store it in a database. I want to extract basic contact details like email addresses, first names, and last names.

I’ve been researching this and found that I should use the Microsoft Graph API endpoint to retrieve contact data. My plan is to set up an n8n workflow that connects to this API and then processes the contact information before saving it to our database.

const contactEndpoint = 'https://graph.microsoft.com/v1.0/me/contacts';

const fetchContacts = async () => {
  const response = await fetch(contactEndpoint, {
    headers: {
      'Authorization': 'Bearer ' + accessToken
    }
  });
  return response.json();
};

Has anyone successfully implemented something similar? I’m particularly interested in how to handle the authentication part and the best way to structure the n8n workflow for this type of data extraction.

Just dealt with this same pain. Switching to the Graph SDK instead of raw fetch calls was a game changer - it handles token renewal for you. Also make sure you’ve got Contacts.ReadWrite in your app registration scopes. I spent hours debugging weird errors because that was missing. The n8n cron trigger’s solid for scheduled pulls too.

Hit this same issue during a migration earlier this year. Watch out for the contact permissions scope - you might need Contacts.ReadWrite instead of Contacts.Read depending on your tenant setup. Also, Graph API returns different formats for personal vs shared contacts, which totally broke my database schema at first. I started using $select to grab only the fields I needed instead of the whole contact object. Way better for bandwidth and parsing. Another heads up: some Exchange contacts have busted email addresses that’ll crash your database constraints, so clean those up before inserting. Everything’s running smooth now but definitely took longer than I thought with all the edge cases.

Just finished a similar setup three weeks back. Your endpoint works, but try /users/{id}/contacts instead of /me/contacts if you’re hitting multiple mailboxes from a service account. I used client credentials for auth since we needed automated runs without user prompts. Token refresh in n8n caught me off guard - those access tokens die every hour, so get your token management sorted early. I split my workflow into separate nodes: auth, data fetch, transform, and database insert. Way easier to debug when things break. Graph API can return incomplete data when it’s busy, so validate everything before pushing to your database.

Hit a weird bug with this - some Exchange contacts have null values that’ll crash n8n’s JSON parsing. I had to add null checks everywhere before processing anything. Also heads up, your access token might need Mail.Read permissions depending on your Exchange setup, not just Contacts.Read.

Tenant ID config will bite you if you get it wrong - learned that the hard way six months ago building this same integration. Make sure you handle refresh tokens properly since Graph API sessions die constantly during long extractions. I store the refresh token in n8n’s credentials manager and validate tokens before each call. Saves tons of headaches.

That contact endpoint works fine, but if you’re pulling thousands of contacts, use batch requests. Graph API lets you batch up to 20 requests and it’s way faster.

Here’s something no one’s mentioned - field mapping is a pain. Exchange contact fields rarely match your database schema. I built a mapping node in n8n that fixes phone number formats and handles empty fields before inserting into the database.

One more thing - some Exchange setups have strict IP whitelisting. If your requests randomly start failing, check with your admin.

Watch out for exchange rate limits when pulling contacts - got burned by this last month. Your fetch looks good but add throttling between calls or Microsoft will block you fast. That endpoint might timeout on large contact lists too, so consider adding a $top parameter to limit results per request. Worked way better once I added proper error handling.

Built something almost identical last year. Authentication’s the biggest pain - register your app in Azure AD first and grab the Contacts.Read permission at minimum. Skip the pre-built Microsoft connectors in n8n and just use the HTTP Request node instead. Your code looks solid, but you’ll need to handle pagination since Graph API batches results. Watch for the @odata.nextLink property to grab remaining contacts. Learned this the hard way: add error handling and rate limiting or you’ll hit Microsoft’s throttling limits constantly. I throw in small delays between requests plus retry logic for temporary failures. If you’re scheduling this instead of running it for specific users, go with application permissions over delegated ones.

Hit this same issue during our office migration. The thing that saved my sanity was getting field mapping sorted out early. Exchange contacts are all over the place - some use displayName, others split into givenName and surname. Your database needs to handle this mess gracefully. I built a mapping table that standardizes everything before it hits the database. Also, use the $select parameter in your Graph API calls to grab only what you need. Makes a huge difference with large contact lists. If you’re syncing regularly, definitely implement deltaLink - it only pulls changed contacts instead of dumping the entire dataset every time.

Hit this same issue on our CRM integration. Here’s what saved me - add proper logging to your n8n workflow. Graph API fails silently, so you won’t know which contacts didn’t sync. I built a tracking table that logs successful imports with timestamps to catch gaps. Your auth looks good, but cache that access token in n8n’s global variables. No point making extra token requests. The tricky part was handling updates vs new contacts. I used the contact’s id field as the unique identifier and built upsert logic in the database step. Watch out - some Exchange contacts have multiple emails in an array. Make sure your extraction logic handles that or you’ll miss secondary addresses.