How to change group settings in Python with the Wapito WhatsApp API

Read the settings, restrict posting when you are broadcasting, reopen on schedule, and keep an audit trail from the group webhook.

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.

Toggling group settings from Python is the classic scheduler job: one requests.get to read the current state, one requests.patch to change a single field, and the same PATCH with the opposite value at the end of the window. Because both writes hit the same endpoint, the code is one function called twice with a boolean, run from cron or APScheduler.

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. Read the current settings

    The group object carries who may send messages, who may edit the subject and icon, and whether new members need approval. Read before you write so a scheduled job does not flip a setting an admin changed deliberately an hour ago.

    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 the read is requests.get on /groups/{id}, and the settings live inside the returned dict; pull the current value of the announcement flag before you write so a human change made an hour ago is respected. A comparison in Python between what you intend and what is already set decides whether the PATCH runs at all.

    API reference for this step
  2. Restrict posting to admins

    Announcement mode is a single field. It is the right default for any group you use to broadcast, because it removes the whole class of accidental replies to hundreds of people.

    import requests
    
    url = "https://api.wapito.com/v1/groups/120363041234567890@g.us/settings"
    
    payload = {
        "messages_admin_only": False,
        "membership_approval": True
    }
    headers = {
        "Authorization": "Bearer wpt_YOUR_TOKEN",
        "Content-Type": "application/json"
    }
    
    response = requests.patch(url, json=payload, headers=headers)
    
    print(response.json())

    In Python restrict posting with requests.patch(url, json={...}) carrying only the field you mean to change; requests sends a proper JSON body for PATCH exactly as it does for POST. Call raise_for_status() and catch requests.HTTPError, since a 403 here tells you the linked number is no longer an admin of that group.

    API reference for this step
  3. Open it again on a schedule

    Flip the same field back when a discussion window opens. Running this from a scheduler is how a large group can hold a question hour without a moderator sitting on the mute button.

    import requests
    
    url = "https://api.wapito.com/v1/groups/120363041234567890@g.us/settings"
    
    payload = {
        "messages_admin_only": False,
        "membership_approval": True
    }
    headers = {
        "Authorization": "Bearer wpt_YOUR_TOKEN",
        "Content-Type": "application/json"
    }
    
    response = requests.patch(url, json=payload, headers=headers)
    
    print(response.json())

    In Python reopening is the same requests.patch with the flag set the other way, so write one function that takes the boolean and let the scheduler call it twice. Use datetime with a timezone from zoneinfo when you compute the window, because a naive datetime on a UTC server opens the discussion hour at the wrong time.

    API reference for this step
  4. Record the change from the webhook

    Settings changes arrive as group events, including ones made by other admins on their phones, so your audit trail reflects what actually happened rather than what your job intended.

    Arrives on your webhook as groups.

    In Python the FastAPI handler receives the groups event with the changed settings in payload["data"]; write who changed what and when into an audit table from a background task. Comparing the event against your last intended write is how you notice an admin overriding the schedule from their phone.

The whole script

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

"""Group Settings with the Wapito WhatsApp API.

Read the settings, restrict posting when you are broadcasting, reopen on schedule, and keep an audit trail from the group webhook.

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

import requests

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

# --- Read the current settings ---
url = BASE_URL + "/groups/120363041234567890@g.us"

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

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

print(response.json())

# --- Restrict posting to admins ---
url = BASE_URL + "/groups/120363041234567890@g.us/settings"

payload = {
    "messages_admin_only": False,
    "membership_approval": True
}
headers = {
    "Authorization": "Bearer " + TOKEN,
    "Content-Type": "application/json"
}

response = requests.patch(url, json=payload, headers=headers)

print(response.json())

# --- Open it again on a schedule ---
url = BASE_URL + "/groups/120363041234567890@g.us/settings"

payload = {
    "messages_admin_only": False,
    "membership_approval": True
}
headers = {
    "Authorization": "Bearer " + TOKEN,
    "Content-Type": "application/json"
}

response = requests.patch(url, json=payload, headers=headers)

print(response.json())

Receive the webhook

FastAPI receiver for groups — 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"]


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

  • APScheduler and cron both re-run a job that crashed, and a re-run of the restrict step produces a second system message in the group; make the function read the setting first and return early when it already matches.
  • requests.patch with data= instead of json= sends form encoding and the API answers 400 invalid_request; the two keyword arguments look interchangeable and are not.
  • A naive datetime.now() on a server set to UTC computes the reopen time in the wrong zone for every group outside it; use zoneinfo.ZoneInfo and store the group's timezone with its id.

Frequently asked questions

What settings can I change from the API?

The ones an admin sees on the phone: who may send messages, who may edit the group's subject, icon and description, and whether people joining by link need approval first. Read the group object to see the current values, because other admins can change them at any time.

Does announcement mode stop replies entirely?

It stops non-admins from posting in the group, which is what makes it suitable for broadcasts. People can still react to messages, and they can still message the linked number privately, so plan a path for the replies you do want rather than assuming nobody will try.

Can I make a group members-only invisible to search?

Groups are not searchable on WhatsApp in the first place - they are reachable only through an invite link or a direct add. The nearest control is to revoke the link and require approval, which together mean nobody joins without an admin acting.

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.