How to read group info in Python with the Wapito WhatsApp API

List the groups, read one in detail, rename or re-describe it when your own data changes, and follow edits on the 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.

Keeping a directory of groups from Python is mostly reading: page through the list with requests, fetch the ones you care about, and PATCH a subject or description when your own data changes. Because the list can be long, the listing step wants a generator, and because every edit is announced in the group, the writing step wants a comparison first.

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. List the groups this number is in

    Page through the list and store the ids as strings. This list is the ground truth for which groups your automation can act on, and it changes whenever someone adds or removes the linked number.

    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 page through /groups with a generator that calls requests.get with the cursor parameter and yields each group until no cursor comes back; store each id as a str in your table. The generator hides the paging from the sync code, which then reads the whole set with an ordinary for loop.

    API reference for this step
  2. Read one group in detail

    The group object carries the subject, description, creation time, current settings and the participant roll with roles. Fetch it before a write so you are acting on the present state rather than on a cached copy.

    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 detail read is requests.get on /groups/{id} and the response dict carries subject, description, settings and the participant roll; keep the dict in memory for the rest of the run so the write step can compare against it. A 404 raised by raise_for_status() means the number was removed since the list was read.

    API reference for this step
  3. Rename or re-describe the group

    Subject and description changes are visible to every member as a system message, so make them deliberately. A description that carries the rules and an opt-out route does more for your ban risk than any clever pacing.

    import requests
    
    url = "https://api.wapito.com/v1/groups/120363041234567890@g.us"
    
    payload = {
        "subject": "Acme Launch Team",
        "description": "Launch week: daily standup at 09:30."
    }
    headers = {
        "Authorization": "Bearer wpt_YOUR_TOKEN",
        "Content-Type": "application/json"
    }
    
    response = requests.patch(url, json=payload, headers=headers)
    
    print(response.json())

    In Python rename or re-describe with requests.patch(url, json={"subject": ...}) sending only the fields that differ from what you just read. That comparison in Python is what stops a nightly job from posting the same rename as a system message every night; if the dict already matches, skip the call.

    API reference for this step
  4. Follow changes over the webhook

    Renames, description edits and setting changes made by any admin arrive as group events, which is how a cached directory of groups stays accurate without polling the list endpoint.

    Arrives on your webhook as groups.

    In Python the FastAPI handler receives groups events for renames, description edits and setting changes; update the cached row from payload["data"] in a background task and return the acknowledgement at once. With the event stream applied, the list endpoint only needs to be polled occasionally as a safety net.

The whole script

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

"""Group Info with the Wapito WhatsApp API.

List the groups, read one in detail, rename or re-describe it when your own data changes, and follow edits on the webhook.

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

import requests

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

# --- List the groups this number is in ---
url = BASE_URL + "/groups"

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

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

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

print(response.json())

# --- Read one group in detail ---
url = BASE_URL + "/groups/120363041234567890@g.us"

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

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

print(response.json())

# --- Rename or re-describe the group ---
url = BASE_URL + "/groups/120363041234567890@g.us"

payload = {
    "subject": "Acme Launch Team",
    "description": "Launch week: daily standup at 09:30."
}
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

  • A pandas DataFrame with the ids in a numeric column rounds them; read the list into a column of dtype str, or better, keep the ids in a plain Python list of strings.
  • A PATCH sent on every run without comparing first produces a system message in the group each time, and members mute the group; compare the subject and description to the read-back and skip when equal.
  • requests.get with no timeout can hang the generator on one page forever; pass timeout=(5, 30) and let a Timeout exception end the run so cron retries it later.

Frequently asked questions

Why is a group missing from the list?

Either the linked number is not in it, or the session has not finished syncing. A newly paired number receives its groups over a short period rather than instantly, so a directory built in the first minutes after pairing will be incomplete. Re-read it once the session reports itself healthy.

Can I read a group my number is not in?

Only its public metadata, and only if you hold an invite code for it - resolving a code returns the name, size and owner without joining. Beyond that, a group is invisible to a number that is not a member, which is a deliberate part of how WhatsApp works.

Does the group object include every participant?

It includes the participant roll with each member's role, which is what most automations need. For very large groups, prefer the dedicated participants endpoint so you can page through the membership instead of pulling one enormous object on every read.

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.