DEV Community

Omni Genz
Omni Genz

Posted on Originally published at omnigenz.site

Find your Brevo list ID and add contacts to it with plain Node.js fetch

A code-first version of a guide first published on Omni Genz.

Almost every Brevo (formerly Sendinblue) integration asks for a list ID sooner or later: a WordPress form plugin, a Zapier step, or your own sign-up endpoint. This post shows how to find it in the dashboard, how to look it up with the API, and how to put new and existing contacts into that list from Node 18+ using only the built-in fetch.

1. Find the list ID in the dashboard

  1. Log in to Brevo.
  2. Go to Contacts > Lists.
  3. The ID is the number shown next to (or under) each list name.

You can also open the list and read the number at the end of the page's web address. Brevo moves menus around now and then, so if the column isn't there, the URL trick still works.

A list ID is not a segment ID. If a tool asks for a list, give it a list.

2. Keep your API key out of the code

Create a key in Settings > SMTP & API and put it in an environment variable:

export BREVO_API_KEY="xkeysib-..."
Enter fullscreen mode Exit fullscreen mode

Every request below sends it in an api-key header against https://api.brevo.com/v3.

A tiny helper keeps the rest of the code short:

// brevo.js
const BASE = "https://api.brevo.com/v3";

export async function brevo(path, { method = "GET", body } = {}) {
  const res = await fetch(BASE + path, {
    method,
    headers: {
      "api-key": process.env.BREVO_API_KEY,
      "accept": "application/json",
      "content-type": "application/json",
    },
    body: body ? JSON.stringify(body) : undefined,
  });

  // Some successful calls return an empty body, so only parse when there is one.
  const text = await res.text();
  const data = text ? JSON.parse(text) : null;

  if (!res.ok) {
    const msg = data?.message || res.statusText;
    throw new Error(`Brevo ${method} ${path} failed (${res.status}): ${msg}`);
  }
  return data;
}
Enter fullscreen mode Exit fullscreen mode

3. List your lists (and their IDs) with the API

GET /contacts/lists returns up to 50 lists per page (limit goes from 0 to 50, default 10), so page through with offset:

// lists.js
import { brevo } from "./brevo.js";

async function getAllLists() {
  const all = [];
  for (let offset = 0; ; offset += 50) {
    const { lists = [], count = 0 } =
      await brevo(`/contacts/lists?limit=50&offset=${offset}`);
    all.push(...lists);
    if (all.length >= count || lists.length === 0) break;
  }
  return all;
}

const lists = await getAllLists();
for (const l of lists) console.log(`${l.id}\t${l.name}`);
Enter fullscreen mode Exit fullscreen mode

Run it with node lists.js and you get a simple ID-to-name table. Handy to paste into your team notes so nobody wires a form to the wrong list.

One heads-up: Brevo's docs say the totalSubscribers and totalBlacklisted fields on this endpoint are being dropped (they'll default to 0), so don't build anything on those counts.

4. Add a new sign-up straight into a list

POST /contacts creates a contact, and listIds drops it into one or more lists in the same call. Set updateEnabled: true so a repeat sign-up updates the existing contact instead of failing:

// subscribe.js
import { brevo } from "./brevo.js";

const NEWSLETTER_LIST_ID = Number(process.env.BREVO_LIST_ID); // e.g. 7

export async function subscribe(email, firstName) {
  await brevo("/contacts", {
    method: "POST",
    body: {
      email,
      attributes: { FIRSTNAME: firstName }, // must match an attribute in your account
      listIds: [NEWSLETTER_LIST_ID],
      updateEnabled: true,
    },
  });
}

await subscribe("reader@example.com", "Sam");
console.log("Subscribed");
Enter fullscreen mode Exit fullscreen mode

Two things that trip people up:

  • Attribute names must already exist in your Brevo account and are written in capitals. Brevo ignores values that don't match the attribute's type.
  • List IDs are numbers, not strings. Reading one from an env var gives you a string, hence the Number(...).

5. Add contacts that already exist to another list

If the contacts are already in Brevo, use POST /contacts/lists/{listId}/contacts/add with an emails array. The response tells you which ones worked and which failed:

// add-to-list.js
import { brevo } from "./brevo.js";

const listId = 12;
const result = await brevo(`/contacts/lists/${listId}/contacts/add`, {
  method: "POST",
  body: { emails: ["jeff@example.com", "jim@example.com"] },
});

console.log("added:", result.contacts?.success ?? result.success);
console.log("failed:", result.contacts?.failure ?? result.failure);
Enter fullscreen mode Exit fullscreen mode

(The docs' example shows success and failure arrays; checking both shapes keeps the script working whichever way the response is nested.)

6. Respect consent

Adding someone to a marketing list means they'll get your campaigns, so only do it with their permission. For public sign-up forms, Brevo's double opt-in flow is the safer default, and it also keeps typo addresses off your list.

Wrap-up

  • Dashboard: Contacts > Lists, or the number at the end of the list's URL.
  • API: GET /contacts/lists, paged 50 at a time.
  • New contacts: POST /contacts with listIds and updateEnabled: true.
  • Existing contacts: POST /contacts/lists/{id}/contacts/add.

The full, non-code guide (including where plugins and Zapier ask for the ID) is on Omni Genz. Omni Genz is an independent guide and isn't affiliated with Brevo.

Top comments (0)