1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

blastengineのEDIT数上限の仕組みと対処法

Posted at

blastengine APIを利用した一斉配信を行う際、「配信登録ができなくなった」というトラブルに遭遇したことはありませんか?これは多くの場合、EDIT状態の配信が上限に達していることが原因です。本記事では、この配信登録上限の仕組みを解説し、APIを活用した具体的な対処法を紹介します。

blastengineの配信ステータス管理の概要

ステータス一覧(EDIT / RESERVE / SENT / FAILED)

blastengineでは、配信の状態を以下のステータスで管理しています。

ステータス 説明
EDIT 編集中
IMPORTING 配信アドレス一括インポート中
RESERVE 配信予約済
WAIT 配信待ち
SENDING 配信中
SENT 配信成功
FAILED 配信失敗

これらのステータスはAPIレスポンスおよびダッシュボードで確認でき、配信一覧API(GET /deliveries)のstatusパラメータでフィルタリングすることも可能です。

EDITステータスとは?

EDITは「編集中」を表すステータスで、一斉配信(BULK)の配信登録を開始した直後の状態です。この段階では、件名・本文・送信先アドレスなどの設定途中であり、配信予約が確定していません。

一斉配信の基本的な流れは以下のようになります

  1. POST /deliveries/bulk/begin で配信登録を開始 → EDIT状態
  2. PUT /deliveries/bulk/update/{delivery_id} で内容を更新(オプション)
  3. POST /deliveries/{delivery_id}/emails で配信先アドレスを登録
  4. PATCH /deliveries/bulk/commit/{delivery_id} で配信予約を確定 → RESERVE状態

重要なポイント:EDITステータスの配信は、配信登録上限のカウント対象となります。

上限の仕様

blastengine APIでは、配信ステータスがEDITの配信が30件に達すると、それ以上配信登録ができなくなります

この制限の意図は、未完了の配信が大量に滞留することを防ぎ、システムリソースを適切に管理することにあります。追加で配信登録を行う場合は、EDITの配信を完了(RESERVE状態へ遷移)させるか、削除する必要があります。

制限回避の実装例

1. EDIT配信の自動削除バッチ

一定期間以上経過したEDIT配信を自動削除するバッチ処理を実装することで、上限問題を予防できます。

Python実装例

import requests
from datetime import datetime, timedelta, timezone

def cleanup_old_edit_deliveries(bearer_token, days_threshold=7):
    """
    指定日数以上前に作成されたEDIT配信を削除

    Args:
        bearer_token: API認証トークン
        days_threshold: 削除対象とする経過日数(デフォルト7日)
    """
    headers = {
        "Authorization": f"Bearer {bearer_token}",
        "Content-Type": "application/json"
    }

    # EDIT状態の配信を取得
    response = requests.get(
        "https://app.engn.jp/api/v1/deliveries",
        headers=headers,
        params={"status[]": "EDIT", "size": 100}
    )

    if response.status_code != 200:
        print(f"配信一覧の取得に失敗: {response.status_code}")
        return

    deliveries = response.json().get("data", [])
    threshold_date = datetime.now(timezone.utc) - timedelta(days=days_threshold)
    deleted_count = 0

    for delivery in deliveries:
        created_time = datetime.fromisoformat(
            delivery["created_time"].replace("+09:00", "+09:00")
        )

        # 閾値より古い配信を削除
        if created_time < threshold_date:
            delivery_id = delivery["delivery_id"]
            delete_response = requests.delete(
                f"https://app.engn.jp/api/v1/deliveries/{delivery_id}",
                headers=headers
            )

            if delete_response.status_code == 200:
                print(f"削除成功: ID={delivery_id}, 作成日={delivery['created_time']}")
                deleted_count += 1
            else:
                print(f"削除失敗: ID={delivery_id}, エラー={delete_response.text}")

    print(f"クリーンアップ完了: {deleted_count}件削除")
    return deleted_count

# 実行例(7日以上前のEDIT配信を削除)
cleanup_old_edit_deliveries(bearer_token, days_threshold=7)

2. EDIT配信が既に30件近い場合の自動完了処理

上限に近づいた場合、必要な配信を自動的に完了(RESERVE状態へ遷移)させる処理も有効です。

def auto_commit_if_near_limit(bearer_token, threshold=25):
    """
    EDIT配信数が閾値を超えた場合、
    配信先アドレスが登録済みの配信を自動的に即時配信する
    """
    headers = {
        "Authorization": f"Bearer {bearer_token}",
        "Content-Type": "application/json"
    }

    # EDIT状態の配信を取得
    response = requests.get(
        "https://app.engn.jp/api/v1/deliveries",
        headers=headers,
        params={"status[]": "EDIT", "size": 100}
    )

    deliveries = response.json().get("data", [])

    if len(deliveries) < threshold:
        print(f"EDIT配信数: {len(deliveries)} - 閾値未満のため処理スキップ")
        return

    print(f"[WARNING] EDIT配信数が閾値を超えました: {len(deliveries)}/{threshold}")

    # 配信先アドレスが登録済みの配信を即時配信
    for delivery in deliveries:
        delivery_id = delivery["delivery_id"]

        # 配信詳細を取得して配信先件数を確認
        detail_response = requests.get(
            f"https://app.engn.jp/api/v1/deliveries/{delivery_id}",
            headers=headers
        )

        if detail_response.status_code == 200:
            detail = detail_response.json()
            total_count = detail.get("total_count", 0)

            if total_count > 0:
                # 即時配信を実行
                commit_response = requests.patch(
                    f"https://app.engn.jp/api/v1/deliveries/bulk/commit/{delivery_id}/immediate",
                    headers=headers
                )

                if commit_response.status_code == 200:
                    print(f"即時配信開始: ID={delivery_id}, 宛先数={total_count}")
                    # アラート通知(Slack等への連携を想定)
                    send_alert(f"配信ID {delivery_id} を自動的に即時配信しました")

この処理を実行する際は、意図しない配信を防ぐため、アラート通知を組み合わせることを強く推奨します。

3. 配信テンプレート再利用によるEDIT肥大化防止策

毎回新規に配信登録するのではなく、テンプレート的なアプローチで運用することで、EDIT配信の肥大化を防げます。

運用パターン例

class DeliveryManager:
    def __init__(self, bearer_token):
        self.bearer_token = bearer_token
        self.headers = {
            "Authorization": f"Bearer {bearer_token}",
            "Content-Type": "application/json"
        }

    def create_and_send_immediately(self, from_email, from_name, subject,
                                     text_part, recipients):
        """
        配信を作成して即座に配信開始する
        (EDIT状態を最小限に抑える)
        """
        # 1. 配信登録開始
        begin_response = requests.post(
            "https://app.engn.jp/api/v1/deliveries/bulk/begin",
            headers=self.headers,
            json={
                "from": {"email": from_email, "name": from_name},
                "subject": subject,
                "text_part": text_part
            }
        )

        if begin_response.status_code != 201:
            raise Exception(f"配信登録失敗: {begin_response.text}")

        delivery_id = begin_response.json()["delivery_id"]

        try:
            # 2. 配信先アドレスを登録
            for recipient in recipients:
                requests.post(
                    f"https://app.engn.jp/api/v1/deliveries/{delivery_id}/emails",
                    headers=self.headers,
                    json={"email": recipient["email"],
                          "insert_code": recipient.get("insert_code", [])}
                )

            # 3. 即時配信開始
            commit_response = requests.patch(
                f"https://app.engn.jp/api/v1/deliveries/bulk/commit/{delivery_id}/immediate",
                headers=self.headers
            )

            if commit_response.status_code == 200:
                print(f"配信開始: ID={delivery_id}")
                return delivery_id
            else:
                raise Exception(f"配信確定失敗: {commit_response.text}")

        except Exception as e:
            # エラー時は配信を削除してEDIT状態を解消
            requests.delete(
                f"https://app.engn.jp/api/v1/deliveries/{delivery_id}",
                headers=self.headers
            )
            raise e

このパターンでは、配信の作成から送信開始までを一連の処理として実行し、途中でエラーが発生した場合は配信を削除することで、EDIT状態の配信が残らないようにしています。

バッチ運用の注意点

自動削除バッチを運用する際の誤操作防止策を紹介します。

1. 削除対象のフィルタリング

# 削除してはいけない配信を除外するフラグ管理
PROTECTED_SUBJECTS = ["重要", "保護", "[DO NOT DELETE]"]

def should_delete(delivery):
    """削除対象かどうか判定"""
    subject = delivery.get("subject", "")

    # 保護対象の件名パターンにマッチする場合は削除しない
    for pattern in PROTECTED_SUBJECTS:
        if pattern in subject:
            return False

    # 作成から7日以上経過している場合のみ削除対象
    created_time = datetime.fromisoformat(delivery["created_time"])
    if datetime.now(timezone.utc) - created_time < timedelta(days=7):
        return False

    return True

2. ドライランモードの実装

def cleanup_with_dryrun(bearer_token, dry_run=True):
    """
    dry_run=True: 削除対象を表示するのみ(実際には削除しない)
    dry_run=False: 実際に削除を実行
    """
    deliveries = get_edit_deliveries(bearer_token)
    targets = [d for d in deliveries if should_delete(d)]

    print(f"削除対象: {len(targets)}")
    for d in targets:
        print(f"  ID={d['delivery_id']}, 件名={d.get('subject', '(未設定)')}")

    if dry_run:
        print("[DRY RUN] 実際の削除は行いません")
        return

    # 確認プロンプト
    confirm = input("削除を実行しますか? (yes/no): ")
    if confirm.lower() != "yes":
        print("キャンセルしました")
        return

    # 実際の削除処理
    for d in targets:
        delete_delivery(bearer_token, d["delivery_id"])

3. 実行ログの保存

削除したdelivery_idと削除日時をログファイルやデータベースに記録しておくことで、万が一の際にトラブルシューティングが可能になります。

まとめ

blastengineのEDIT配信30件制限について、要点を整理します。

制限の仕組み

  • 配信ステータスがEDITの配信が30件に達すると新規登録不可
  • 対処法は「完了(RESERVE状態へ遷移)」または「削除」

予防策

  1. EDIT配信数を定期監視し、閾値でアラート通知
  2. 配信登録から完了までを一連の処理として実装
  3. 古いEDIT配信を自動削除するバッチ処理の導入

適切なAPI利用と自動化により、配信登録上限の問題を未然に防ぎ、安定したメール配信運用を実現できます。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?