TL;DR: For most stores the official MailerLite plugin is the right answer. Go to the API when MailerLite is one destination among several, or when the rules about who lands in which group are yours.
POST https://connect.mailerlite.com/api/subscribersis an upsert: 201 when it creates, 200 when it updates. Fields and groups you leave out are never removed.PUT /api/subscribers/:idis not the same call. It removes the subscriber from every group you do not list.- Consent is your job. A checkbox registered with WooCommerce's additional checkout fields API is stored on the order, and the code that reads it decides whether anything is sent at all.
- The limit is 120 requests a minute per account, with a
Retry-Afterheader on the 429.
/ Official plugin
Is there an official MailerLite plugin for WooCommerce?
Yes, and it is good. MailerLite – WooCommerce integration has 30,000+ active installs.¹ It adds a subscribe checkbox to checkout with a choice of position and label, a double opt-in switch, order data forwarded to MailerLite, product import, order-related custom fields and segments, sales tracking for campaign ROI, automations triggered by recent purchases, and abandoned cart emails.
That list covers what most shops want. If yours is one of them, install it and stop reading. Abandoned carts in particular are not something you should rebuild yourself.
The API is for the cases the plugin was not built around. The same customer also has to reach a CRM, a Slack channel or an n8n workflow, and you want one place where every delivery is logged. Group membership depends on rules that are yours: which product was bought, whether it was a renewal, which shipping country. Or the event is not a checkout at all, such as a membership activation or a course enrolment.
/ Authentication
How do you authenticate to the MailerLite API?
With an API key sent as a Bearer token. In MailerLite, open Integrations, choose MailerLite API and click Generate new token. MailerLite shows the key once and stores it hashed, so copy it straight into wherever your site keeps secrets.
Two details from the getting-started page matter in production. Every request sends Content-Type: application/json and Accept: application/json to the base URL https://connect.mailerlite.com/api. And the key is bound to the user who created it: remove that person from the account and the key stops authenticating, and every request answers 401 Unauthenticated. Create it from an account owner who is not about to leave, and name it after the site.
Requests use the latest API version unless you pin one. Send X-Version with the date you implemented the integration and a later change to the API cannot alter what your requests do.
/ Upsert
Which endpoint adds a WooCommerce customer without creating duplicates?
POST /api/subscribers. The subscribers reference calls it create/upsert: if the email exists the subscriber is updated, and it is a non-destructive operation, so omitting fields or groups does not remove them. Only email is required. The response code tells you which case happened: 201 Created for a new subscriber, 200 OK for an existing one.
JSON — body for POST /api/subscribers
{ "email": "[email protected]", "fields": { "name": "Ada", "last_name": "Lovelace" }, "groups": ["4243829086487936"] }
Three rules decide whether that body works:
groupsholds group ids, not names, and the groups must already exist. Read the ids fromGET /api/groupsonce and keep them in configuration.- Every key in
fieldsmust be a default or custom field.nameandlast_nameexist on every account. Anything else, such aslast_order_total, has to be created first withPOST /api/fields, which takes anameand atypeoftext,numberordate. - Do not switch to PUT for updates.
PUT /api/subscribers/:idlooks like the update half of the API, but the same reference says the subscriber "will be removed from unlisted groups". A PUT that sends only the customer group silently takes the person out of every newsletter group they joined from a form.
/ Consent
How do you record newsletter consent at WooCommerce checkout?
WooCommerce has no marketing checkbox of its own, and the MailerLite API accepts any address you send it. Whether the customer agreed is a fact only your checkout knows. Add the checkbox with the additional checkout fields API, which stores the answer on the order. Then fire your own action only when the box was ticked:
PHP — checkout checkbox and an opt-in action
add_action( 'woocommerce_init', function () { woocommerce_register_additional_checkout_field( array( 'id' => 'my-shop/newsletter', 'label' => 'Email me news and offers', 'location' => 'order', 'type' => 'checkbox', ) ); } ); add_action( 'woocommerce_order_status_processing', function ( $order_id, $order ) { // Order-location fields are saved under the _wc_other/ prefix; a ticked box is "1". if ( '1' === $order->get_meta( '_wc_other/my-shop/newsletter' ) ) { do_action( 'my_shop_newsletter_optin', $order ); } }, 10, 2 );
Three things to know about that code. The field API renders in the Checkout block only, not the old [woocommerce_checkout] shortcode. WooCommerce's docs recommend reading values through their CheckoutFields helper and document the _wc_other/ meta key as the direct route, with a ticked checkbox stored as "1". And woocommerce_order_status_processing fires again if an order goes back to processing after being on hold. That is harmless here, because the upsert is idempotent: the same email lands on the same subscriber.
Gating the event, rather than sending everyone and filtering later, means a customer who did not tick the box never reaches MailerLite at all. No log, no queue entry, no subscriber record to clean up.
/ Unsubscribed
What happens when a customer who unsubscribed orders again?
They stay unsubscribed. The upsert updates their fields and groups but does not change the status back to active. The endpoint does take a resubscribe flag, documented as "set to true to resubscribe previously unsubscribed subscribers", and that is the point where a careless integration does damage.
Ticking a box at checkout is fresh consent, so setting resubscribe for an order with the box ticked is defensible. Setting it on every order is not: a customer who unsubscribed in March and bought again in October without ticking anything would be back on the list. The PUT endpoint is stricter still. For an unsubscribed, bounced or junk subscriber, the reference says reactivation is only possible through the app, a form or a landing page.
/ Rate limits
What are the MailerLite API rate limits?
A global limit of 120 requests per minute. Past it you get 429 Too Many Attempts with X-RateLimit-Remaining: 0 and a Retry-After header in seconds. Imports have a separate limit of 5 requests per minute, and that includes batch requests made up entirely of subscriber upserts.
Checked against real store volume, with one upsert per opted-in order:
- A Black Friday hour of 600 orders is 600 ÷ 60 = 10 requests a minute, a twelfth of the limit.
- Backfilling 20,000 existing customers one by one is 20,000 ÷ 120 = 167 minutes at the very best. Use the import endpoint instead.
So the live integration will not hit the limit. A backfill sharing the same key with it will, and the live orders arriving during the backfill are the ones that get the 429s.
| Concern | Hand-rolled wp_remote_post | Webhook Actions |
|---|---|---|
| Abandoned carts, product sync, campaign ROI | Not this. Use the official plugin | Not this either. It delivers events; it has no MailerLite connector and no cart tracking |
| Which events | One add_action per event, written by you | Any WordPress action as a trigger, including your own my_shop_newsletter_optin |
| Where the call runs | Inside the status change unless you build a queue | Queued in its own table, delivered in the background |
| The API key | A constant in wp-config.php | A Bearer credential in the Credentials Vault, redacted in every log |
| email, name, last_name | Built in your array | Field mapping from args.0.billing into email, fields.name and fields.last_name |
| Group ids | Written in your array | A short pre-dispatch snippet; field mapping moves values and cannot invent one |
| A 429 or 5xx | Lost unless you wrote retry logic | Retried with exponential backoff, five attempts by default. Retry-After is not read |
| A 422 from an unknown field | Invisible | Marked permanently failed with MailerLite's errors object in the delivery log, replayable after the fix |
The group rules are a few lines of PHP. Field mapping has already produced email and the two name fields, and passed the order's line items through as items; the snippet turns products into group ids:
PHP — pre-dispatch snippet that sets the groups
// $payload arrives field-mapped: email, fields.name, fields.last_name, items. $groups = [ '4243829086487936' ]; // everyone who opted in $by_product = [ 412 => '31985378335392975' ]; // product id => group id foreach ( $payload['items'] ?? [] as $item ) { if ( isset( $by_product[ $item['product_id'] ] ) ) { $groups[] = $by_product[ $item['product_id'] ]; } } $payload['groups'] = array_values( array_unique( $groups ) ); unset( $payload['items'] ); // MailerLite does not need them return $payload;
Seeing it run beats reading about it. The live preview boots a throwaway WordPress with Webhook Actions already installed and demo deliveries sitting in the log — no signup, nothing left on your machine afterwards.
/ Failures
Which MailerLite errors should be retried?
Only 429 and 5xx. A 429 clears by itself. With retries 60 and then a further 120 seconds apart, the second retry lands 60 + 120 = 180 seconds after the first attempt, past even the 119-second Retry-After in MailerLite's own example, without the header ever being read. A 5xx is on MailerLite's side and is usually brief.
Everything in the 4xx range is a request that will fail the same way next time. A 401 means the key was revoked or its user was removed. A 422 carries an errors object naming the field, most often an invalid email or a fields key that does not exist on the account. A 403 means the action is denied for that account or token. Retrying those burns requests from the same 120-a-minute budget the healthy orders need. Log them where someone will look, which is the whole subject of why webhooks fail silently.
/ Exposure
What does MailerLite not protect you from?
Your own consent logic. MailerLite's terms put it directly: when you collect subscribers through your own API key, you are responsible for preventing abuse. A checkout that adds everyone is a checkout that sends campaigns to people who never asked for them, and spam complaints count against the account's sending reputation.
The bill. MailerLite plans are priced by subscriber count. Every guest checkout you push in moves you toward the next tier, whether or not that person ever opens an email. Opt-in gating keeps the list and the invoice the same size as your actual audience.
A key that dies with a person. Because the key is bound to its creator, an offboarding in the marketing team can stop the integration with no change on your side. The symptom is a run of 401s, which is why a 401 should alert rather than retry.
If the same store also feeds Mailchimp or Klaviyo, the Mailchimp and Klaviyo articles cover their APIs, which handle status and tags quite differently.