How to leave a group in Python with the Wapito WhatsApp API

List what you are in, hand over the creator role if you hold it, leave, and archive your record when the webhook confirms it.

A script, a token and an HTTP client is the whole stack. Everything below runs from a file you can execute today, and the same code moves to a worker or a container without changing shape.

The exit flow in Python is a cleanup script with a safety check in the middle: list with requests, read each group's roles, promote a human if the linked number is the creator, and only then post to /leave. Export anything you still want before that last call, because once it returns the group is gone from the number's view. Add a dry-run flag that prints instead of posting.

Before you start

  • pip install requests
  • os.environ["WAPITO_TOKEN"]
  • A channel with a WhatsApp number linked to it, and its API token. Create one in the dashboard — the authentication guide shows where the token goes.

How it works

  1. Find the groups to leave

    Work from the live list rather than from your own records, so an automation cleaning up after itself does not try to leave groups it was already removed from and log a pile of harmless 404s.

    import requests
    
    url = "https://api.wapito.com/v1/groups"
    
    querystring = {"count":"50","offset":"0"}
    
    headers = {"Authorization": "Bearer wpt_YOUR_TOKEN"}
    
    response = requests.get(url, headers=headers, params=querystring)
    
    print(response.json())

    In Python read the live list through a paging generator over requests.get that advances offset by count until total is reached, and filter it in code against the ids you intend to leave; a set intersection between the two gives exactly the groups that still contain the number. Anything in your list but absent from the API's already removed you.

    API reference for this step
  2. Hand over first if you are the creator

    Read the roles. If the linked number is the group's creator, promote a human admin before leaving, otherwise the group is stranded with nobody able to change its settings or membership.

    import requests
    
    url = "https://api.wapito.com/v1/groups/120363041234567890@g.us"
    
    headers = {"Authorization": "Bearer wpt_YOUR_TOKEN"}
    
    response = requests.get(url, headers=headers)
    
    print(response.json())

    In Python fetch each candidate with requests.get and inspect the participants list in the dict: if the entry whose id is the linked number carries the superadmin role, promote a named human first with a separate POST, and only proceed once that call has succeeded. Encode that rule as an if before the leave, and log the promoted admin's id.

    API reference for this step
  3. Leave the group

    Leaving is immediate and announced to the group. The group becomes invisible to the linked number afterwards, so store anything you still need - the id, the membership, the transcript - before you call this.

    import requests
    
    url = "https://api.wapito.com/v1/groups/120363041234567890@g.us/leave"
    
    headers = {"Authorization": "Bearer wpt_YOUR_TOKEN"}
    
    response = requests.post(url, headers=headers)
    
    print(response.status_code)

    In Python the departure is requests.post on /groups/{id}/leave with no body, answered with a 204, wrapped in try/except requests.HTTPError so a 404 for a group that dropped you in the meantime is logged and skipped. Write your export of the membership and the transcript to disk before this line executes, not after, and flush the file handle.

    API reference for this step
  4. Confirm on the webhook

    The departure arrives as a participant event, which is the signal to archive your own record rather than assuming the call succeeded and moving on.

    Arrives on your webhook as groups.participants.

    In Python the FastAPI receiver sees the departure as a participants event naming the linked number; that event, not the 2xx from the POST, is what should flip the row in your database to archived. Do it in a background task and answer immediately, and make the update idempotent so a redelivered event archives nothing twice.

The whole script

Every step above in one runnable file. Save it as leave-group.py, put your token in the environment, and run it.

"""Leave Group with the Wapito WhatsApp API.

List what you are in, hand over the creator role if you hold it, leave, and archive your record when the webhook confirms it.

Run it with:
    export WAPITO_TOKEN="wpt_..."
    python leave-group.py
"""
import os

import requests

BASE_URL = "https://api.wapito.com/v1"
TOKEN = os.environ["WAPITO_TOKEN"]

# --- Find the groups to leave ---
url = BASE_URL + "/groups"

querystring = {"count":"50","offset":"0"}

headers = {"Authorization": "Bearer " + TOKEN}

response = requests.get(url, headers=headers, params=querystring)

print(response.json())

# --- Hand over first if you are the creator ---
url = BASE_URL + "/groups/120363041234567890@g.us"

headers = {"Authorization": "Bearer " + TOKEN}

response = requests.get(url, headers=headers)

print(response.json())

# --- Leave the group ---
url = BASE_URL + "/groups/120363041234567890@g.us/leave"

headers = {"Authorization": "Bearer " + TOKEN}

response = requests.post(url, headers=headers)

print(response.status_code)

Receive the webhook

FastAPI receiver for groups, groups.participants — it verifies the signature, answers immediately and does the work after. Install it with pip install fastapi uvicorn and save it as webhook.py.

import hashlib
import hmac
import os
import time

from fastapi import FastAPI, Header, HTTPException, Request

app = FastAPI()
SECRET = os.environ["WAPITO_WEBHOOK_SECRET"].encode()
EVENTS = ["groups", "groups.participants"]


def verify(raw: bytes, header: str | None) -> bool:
    """Checks the X-Wapito-Signature header: t=<ms>,v1=<hex hmac-sha256>."""
    if not header:
        return False
    parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
    ts, sig = parts.get("t"), parts.get("v1")
    if not ts or not sig:
        return False
    if abs(time.time() * 1000 - int(ts)) > 300_000:  # 5 minute clock skew
        return False
    expected = hmac.new(SECRET, ts.encode() + b"." + raw, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig)


@app.post("/wapito")
async def wapito(request: Request, signature: str | None = Header(default=None, alias="X-Wapito-Signature")):
    raw = await request.body()
    if not verify(raw, signature):
        raise HTTPException(status_code=401, detail="bad signature")
    payload = await request.json()
    if payload["event"] in EVENTS:
        print(payload["event"], payload["data"])
    return {"ok": True}  # answer 2xx fast; do the real work in a queue
Every event, its payload and the retry rules

Errors you may hit

Gotchas

  • requests does not raise on a 4xx by itself. Call response.raise_for_status() or check response.ok, otherwise a 429 send_rate_limited silently looks like a successful send and your loop keeps hammering the queue.
  • json= and data= are not interchangeable. Pass json={...} so requests sets Content-Type: application/json and encodes UTF-8 for you; data= with a dict sends form encoding and the API answers 400 invalid_request.
  • The default requests timeout is None, which means a stalled connection hangs your worker forever. Always pass timeout=(5, 30) and treat a read timeout as "maybe delivered" - re-check with the message id before you send again.
  • A group id such as 120363000000000000@g.us is a string, not a number. Building it with an f-string from an int drops the precision and you get 404 not_found.
  • Emoji and accented text need no extra work in Python 3, but reading a CSV of numbers with the default encoding on Windows will mangle them - open files with encoding="utf-8".

Where to run it

  • Google Cloud Run (container, scale to zero, one revision per deploy)
  • Render background worker for the sender, Render web service for the webhook receiver
  • A 1 GB VPS with systemd for the worker and Caddy in front of the FastAPI receiver
  • AWS Lambda + API Gateway for the webhook receiver only (the sender needs a long-lived process for queue spacing)

Pitfalls in Python

  • shutil.rmtree of an export folder in a finally block runs even when the leave call raised, so a failed run deletes the only copy of the transcript; move cleanup after a successful archive check instead, and keep the export until the webhook has confirmed the departure.
  • Iterating groups while calling /leave inside the same generator that pages the list can skip entries as the list shifts under you; materialise the ids into a list first, then leave, and re-read the list once at the end to report what remains.
  • A script that reads the group ids from a CSV with the csv module gets them as strings, but a quick fix through pandas turns a column of ids into floats and the path /groups/1.2e17/leave answers 404 not_found; keep dtype=str for that column or skip pandas entirely for a list this small.

Frequently asked questions

Can I rejoin a group I left?

Only with a fresh invite link or by being added by an admin. There is no undo, and the group's history from before you left does not come back with you. If a bot might need to return, keep the group id and make sure a human admin can re-invite it.

Do other members see that I left?

Yes. Departures appear as a system message in the group, the same as joins. If your automation leaves a customer-facing group, consider posting a short handover note first so people know where to direct follow-up questions rather than replying into a thread nobody is watching.

What happens to messages I sent before leaving?

They stay in the group for everyone else, exactly as they were - leaving retracts nothing at all. If a message needs removing, delete it before you leave, and remember that WhatsApp only allows deletion for everyone within a limited window after the message was sent. After that window, and after you have left, the message is simply part of the group history.

Related

Try it on your own number

Create a channel, link a WhatsApp number by QR or pairing code, and call the API in a couple of minutes. The Sandbox plan is free and needs no card.