Contents
1. 全体像と事前準備
| 項目 | 必要条件 | 主なポイント |
|---|---|---|
| Mailchimp | API キー取得は Essentials プラン以上(2024 年時点)※プランは随時変更されます。 | UI から API キーとサーバープレフィックスが自動付与される。 |
| Shopify | カスタムアプリ作成は Basic プラン以降で利用可能(Shopify のプラン構成は変わり得ます)。 | プライベートアプリは非推奨、カスタムアプリのみ公式サポート。 |
| 共通要件 | HTTPS が必須のサーバー環境、環境変数で機密情報を管理できること。 | Docker, Vercel, Railway など好きなデプロイ先で可。 |
結論
Mailchimp と Shopify の API を直接組み合わせれば、公式アプリが提供する「一括同期」以上に、顧客属性や注文イベントをリアルタイムで柔軟に扱えます。
2. Mailchimp API キーとサーバープレフィックスの取得手順
2‑1. API キー発行(公式 UI)
- Mailchimp にログイン → 右上メニューの Account を選択。
- 左サイドバーの Extras → API keys を開く。
- Create A Key ボタンをクリックすると、
xxxxxxxxxxxx-usXの形式でキーが生成されます。
ポイント
- キー文字列末尾の‑usXがサーバープレフィックスです(例:abcd1234efgh5678-us2 → us2)。
- 取得したキーは 環境変数 (MAILCHIMP_API_KEY) に保存し、コードにハードコーディングしないでください。
2‑2. サーバープレフィックス確認(自動的に付与)
- キーの末尾がプレフィックスなので別途取得作業は不要です。
- エンドポイント例:
https://us2.api.mailchimp.com/3.0/
注意:Mailchimp が UI を改修した場合、キー生成フローが変わる可能性があります。その際は公式ドキュメントの「Getting started」セクションを参照してください。
3. Shopify カスタムアプリ作成とアクセストークン取得
3‑1. カスタムアプリ vs. プライベートアプリ
| 種類 | 現在のステータス(2024 年) | 主な特徴 |
|---|---|---|
| プライベートアプリ | 新規作成は非推奨・将来的に廃止予定 | 従来の UI で作成できたが、OAuth が未サポート。 |
| カスタムアプリ | 推奨(2024‑10 API バージョン以降) | 管理画面から作成し、スコープごとの Access token を取得可能。 |
3‑2. カスタムアプリ作成手順
- Shopify 管理画面 → Apps > Develop apps for your store
- Create an app → アプリ名(例:
MailchimpSync)を入力し Create app をクリック。 - 左メニューの Configuration で必要なスコープを追加
read_customers,write_customersread_orders,write_orders- API credentials タブへ移動し Install app → Generate token を実行。
- 表示されたトークンは一度しか見れないので、直ちに環境変数 (
SHOPIFY_ACCESS_TOKEN) に保存。
3‑3. API バージョンとエンドポイント例
- 現在の最新バージョンは 2024‑10(2024 年 10 月リリース)。
- エンドポイント例:
https://{store-domain}.myshopify.com/admin/api/2024-10/customers.json
※ バージョン番号は半年ごとに更新されます。実装時には「Release notes」から最新バージョンを取得してください。
4. データマッピングと同期フロー設計
4‑1. フィールド対応表(Shopify → Mailchimp Audience)
| Shopify フィールド | Mailchimp フィールド (merge field) | 備考 |
|---|---|---|
email |
EMAIL(必須) |
同期の主キー |
first_name |
FNAME |
標準マージフィールド |
last_name |
LNAME |
標準マージフィールド |
phone |
PHONE |
任意 |
default_address.address1 |
ADDRESS |
カスタムフィールド例 |
orders_count |
ORDER_COUNT(数値) |
カスタムマージタグ |
total_spent |
TOTAL_SPENT(小数点) |
カスタムマージタグ |
実装ヒント
- Mailchimp のカスタムフィールドは 20 文字以内の英数字 が推奨です。事前に UI → Audiences → Settings → Audience fields and merge tags で作成しておくとエラーが減ります。
4‑2. リアルタイム同期:Shopify Webhook の活用
- Webhook 設定(管理画面 → Settings → Notifications → Webhooks)
- 対象イベント:
customers/create,customers/update,orders/create - ペイロード送信先 URL: 自前サーバー例
https://example.com/api/mailchimp-sync -
認証ヘッダーに Mailchimp API キー を付与(例:
Authorization: Bearer <key>) -
受信側実装ポイント
- リクエストボディは JSON。
topicフィールドでイベント種別を判定。 - 受信後 即座に Mailchimp の /lists/{list_id}/members エンドポイントへ PUT(上書き)し、ステータス
subscribedまたはpendingを設定。
レートリミット対策
- Shopify: 4 リクエスト/秒(2024‑10 バージョン)。
- Mailchimp (Standard プラン): 10 リクエスト/秒。
それぞれのRetry-Afterヘッダーを確認し、指数バックオフで再送するロジックを組み込むことが推奨されます。
4‑3. バッチ同期(過去データ・大量顧客)
| ステップ | 内容 |
|---|---|
| 1 | 毎日 02:00 に Shopify の customers.json をページング取得。 |
| 2 | 未同期レコードを抽出し、Mailchimp の /lists/{list_id}/members に 最大 500 件/リクエスト のバッチ POST(Batch Operations)で送信。 |
| 3 | 成功したレコードは DB(例: SQLite, DynamoDB)にフラグ保存し、次回実行時に除外。 |
注意点
- バッチ処理でも 1 秒あたりのリクエスト数を超えないように スロットリング を入れる。
- 大規模ストア(顧客 > 10 万件)では、バッチサイズと実行頻度を調整しつつ、Shopify の Bulk Operations API(2024‑04 以降提供)も検討してください。
5. サンプルコード(Node.js / Python)
5‑1. 共通設定(環境変数)
| 環境変数 | 説明 |
|---|---|
MAILCHIMP_API_KEY |
Mailchimp の API キー |
MAILCHIMP_SERVER |
サーバープレフィックス (us1, us2 など) |
MAILCHIMP_LIST_ID |
同期対象 Audience(リスト)の ID |
SHOPIFY_DOMAIN |
{store}.myshopify.com のサブドメイン部 |
SHOPIFY_ACCESS_TOKEN |
カスタムアプリで取得したアクセストークン |
セキュリティ
本番環境では Secret Manager(AWS Secrets Manager、Google Secret Manager 等)に保存し、コードからは直接参照しないようにしてください。
5‑2. Node.js (Axios + axios-retry)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// src/config.js require('dotenv').config(); export const mc = { base: `https://${process.env.MAILCHIMP_SERVER}.api.mailchimp.com/3.0`, headers: { Authorization: `apikey ${process.env.MAILCHIMP_API_KEY}`, 'Content-Type': 'application/json', }, }; export const shopify = { base: `https://${process.env.SHOPIFY_DOMAIN}.myshopify.com/admin/api/2024-10`, headers: { 'X-Shopify-Access-Token': process.env.SHOPIFY_ACCESS_TOKEN, 'Content-Type': 'application/json', }, }; |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
// src/syncCustomers.js import axios from 'axios'; import retry from 'axios-retry'; import { mc, shopify } from './config.js'; retry(axios, { retries: 3, retryDelay: retry.exponentialDelay }); export async function syncAllCustomers() { // 1️⃣ Shopify 顧客取得(ページングは簡易版) const shopRes = await axios.get(`${shopify.base}/customers.json`, { headers: shopify.headers, params: { limit: 250 }, }); const customers = shopRes.data.customers; for (const cust of customers) { const payload = { email_address: cust.email, status_if_new: 'subscribed', merge_fields: { FNAME: cust.first_name || '', LNAME: cust.last_name || '', PHONE: cust.phone || '', ADDRESS: cust.default_address?.address1 || '', ORDER_COUNT: cust.orders_count ?? 0, TOTAL_SPENT: parseFloat(cust.total_spent) || 0, }, }; try { await axios.put( `${mc.base}/lists/${process.env.MAILCHIMP_LIST_ID}/members/${encodeURIComponent(cust.email)}`, payload, { headers: mc.headers } ); console.log(`✅ ${cust.email} 同期完了`); } catch (err) { console.error(`❌ ${cust.email} エラー`, err.response?.data); } } } |
5‑3. Python(requests + tenacity)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# config.py import os from dotenv import load_dotenv load_dotenv() MAILCHIMP_SERVER = os.getenv("MAILCHIMP_SERVER") MAILCHIMP_API_KEY = os.getenv("MAILCHIMP_API_KEY") MAILCHIMP_LIST_ID = os.getenv("MAILCHIMP_LIST_ID") SHOPIFY_DOMAIN = os.getenv("SHOPIFY_DOMAIN") SHOPIFY_TOKEN = os.getenv("SHOPIFY_ACCESS_TOKEN") MC_BASE = f"https://{MAILCHIMP_SERVER}.api.mailchimp.com/3.0" SHOPIFY_BASE = f"https://{SHOPIFY_DOMAIN}.myshopify.com/admin/api/2024-10" MC_HEADERS = { "Authorization": f"apikey {MAILCHIMP_API_KEY}", "Content-Type": "application/json", } SHOPIFY_HEADERS = { "X-Shopify-Access-Token": SHOPIFY_TOKEN, "Content-Type": "application/json", } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# sync_customers.py import requests, json, logging from tenacity import retry, stop_after_attempt, wait_exponential from config import MC_BASE, SHOPIFY_BASE, MC_HEADERS, SHOPIFY_HEADERS, MAILCHIMP_LIST_ID logging.basicConfig(level=logging.INFO) @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10)) def put_mailchimp_member(email: str, payload: dict): url = f"{MC_BASE}/lists/{MAILCHIMP_LIST_ID}/members/{email}" resp = requests.put(url, headers=MC_HEADERS, json=payload) resp.raise_for_status() return resp def sync_customers(): # 1️⃣ Shopify 顧客取得 r = requests.get(f"{SHOPIFY_BASE}/customers.json", headers=SHOPIFY_HEADERS, params={"limit":250}) r.raise_for_status() customers = r.json()["customers"] for cust in customers: payload = { "email_address": cust["email"], "status_if_new": "subscribed", "merge_fields": { "FNAME": cust.get("first_name", ""), "LNAME": cust.get("last_name", ""), "PHONE": cust.get("phone", ""), "ADDRESS": cust.get("default_address", {}).get("address1", ""), "ORDER_COUNT": cust.get("orders_count", 0), "TOTAL_SPENT": float(cust.get("total_spent", 0)), }, } try: put_mailchimp_member(cust["email"], payload) logging.info(f"✅ {cust['email']} 同期完了") except requests.HTTPError as e: logging.error(f"❌ {cust['email']} エラー: {e.response.text}") if __name__ == "__main__": sync_customers() |
5‑4. Webhook 受信エンドポイント(Node.js Express)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
import express from 'express'; import bodyParser from 'body-parser'; import axios from 'axios'; import { mc } from './config.js'; const app = express(); app.use(bodyParser.json()); app.post('/api/mailchimp-sync', async (req, res) => { const { topic, payload } = req.body; // Shopify の webhook 形式 if (!topic || !payload) return res.status(400).send('Invalid payload'); try { if (topic === 'orders/create') { const email = payload.email; const mcPayload = { email_address: email, status_if_new: 'subscribed', merge_fields: { ORDER_ID: payload.id, TOTAL_PRICE: parseFloat(payload.total_price), CURRENCY: payload.currency, ORDER_DATE: payload.created_at, }, }; await axios.put( `${mc.base}/lists/${process.env.MAILCHIMP_LIST_ID}/members/${encodeURIComponent(email)}`, mcPayload, { headers: mc.headers } ); } // customers/create や update でも同様に処理可能 res.status(200).send('ok'); } catch (err) { console.error(err.response?.data); res.status(500).send('error'); } }); app.listen(3000, () => console.log('Webhook server listening on :3000')); |
6. エラーハンドリングとリトライ戦略
| HTTP ステータス | 主な原因 | 推奨対策 |
|---|---|---|
| 401 | API キー・アクセストークンが無効、サーバープレフィックス不一致 | キーを再取得し、MAILCHIMP_SERVER が正しいか確認 |
| 403 / 429 | レートリミット超過(Mailchimp: 10 req/s、Shopify: 4 req/s) | Retry-After ヘッダー待機 + 指数バックオフ (axios-retry, tenacity) |
| 404 | Audience ID、顧客 ID が存在しない | UI → Audiences → Settings で LIST_ID を再取得 |
| 422 | 必須フィールド欠如(例: email) | データ前処理で必ず email があるか検証 |
| 500 系 | サーバー内部エラー | 一時的な障害の可能性が高いので、数秒待機後再試行 |
実装ヒント
- Node.js ではaxios-retry、Python ではtenacityを利用するとコードがシンプルです。
- 失敗したリクエストは DB に保存し、手動で再処理 できるようにしておくと運用が楽になります。
7. テスト・デバッグ・コンプライアンス
7‑1. 同期テスト手順
| 手順 | 確認ポイント |
|---|---|
| ① Shopify 側でレコード作成 | Customers > Add customer または API で POST。 |
| ② Webhook ログ確認 | サーバーのコンソールに order synced / customer created が出力されているか。 |
| ③ Mailchimp UI 確認 | Audiences → All contacts に対象メールが Subscribed 状態で表示されるか。 |
7‑2. GDPR・二重オプトイン対応
- 二重オプトイン: API では
status_if_newを"pending"に設定すると、Mailchimp が自動的に確認メールを送ります。 - データ削除リクエスト: Shopify の
customer/deleteWebhook を受信したら、同時に Mailchimp の/lists/{list_id}/members/{subscriber_hash}DELETE エンドポイントで削除処理を行う。
法的留意点
- EU・UK などの居住者データは、保存期間やアクセス権限を厳格に管理してください(「Data Processing Addendum」参照)。
- 日本国内向けでも 個人情報保護法 に準拠し、利用目的外でのデータ活用は行わないようにしましょう。
7‑3. デバッグテクニック
axios/requestsのエラーレスポンスは JSON。
js
console.error(err.response?.data);- cURL で手軽にエンドポイントを叩くと、認証やヘッダーの抜け漏れがすぐ分かります。
8. FAQ(よくある質問)
| 質問 | 回答 |
|---|---|
| Mailchimp の Essentials プラン未契約でも API は使えますか? | 現行では Essentials 以上が必須です。ただし、2025 年以降のプラン変更やプロモーションで条件が緩和される可能性があります。公式ページを随時確認してください。 |
| Shopify の無料トライアルでもカスタムアプリは作れますか? | はい。開発ストア(Partner デベロッパー用)では無制限にカスタムアプリが作成可能です。本番ストアの場合は有料プランが必要です。 |
| レートリミット超過でデータが失われませんか? | 失われることはありません。API はエラーコードと共に処理対象外のリクエストを返すだけなので、再送ロジックさえ実装していれば必ず同期できます。 |
| Webhook が届かない場合の原因は? | - 公開 URL が HTTP ではなく HTTPS でない - SSL 証明書が自己署名(Shopify は信頼できる CA の証明書を要求) - IP 制限やファイアウォール設定 |
| バッチ処理の実行時間はどれくらい? | 顧客 10 万件程度なら、1 日あたり数千リクエストに分割して実行すれば、2〜3 時間以内で完了します(スロットリング考慮)。 |
9. まとめと次のステップ
- 公式情報を再確認:プラン・API バージョンは必ず最新ドキュメントでチェック。
- 環境変数に機密情報を格納し、コードにハードコーディングしない。
- カスタムアプリと Webhook を設定し、リアルタイム同期基盤を構築。
- バッチ処理で過去データも一括同期し、データロスのリスクを排除。
- エラーハンドリング・レートリミット対策は必須(axios-retry / tenacity)。
- GDPR/二重オプトイン を実装し、法令遵守を徹底。
以上の手順を踏めば、Mailchimp と Shopify のデータ連携が 自動化・拡張性・信頼性 のすべてを満たした形で実現できます。さらに深い内容やサンプルプロジェクトは、下記の無料 PDF ガイドからダウンロードしてください。
📥 無料 PDF ガイド ダウンロード
Mailchimp × Shopify 完全実装ガイド(PDF)
※ ダウンロードにはメールアドレスが必要です。個人情報は一切保存しません。
本記事の内容は 2024 年 10 月時点の情報を元に執筆しています。最新情報は必ず公式サイトをご確認ください。