做公众号运营,把所有粉丝当成同一批人来发消息,效果往往越来越差。微信公众平台提供了用户标签能力,可以把粉丝按性别偏好、活跃程度、消费行为等维度分组,再针对不同分组推送不同的内容。这篇文章从接口层面讲清楚标签的增删改查、批量打标,以及如何按标签圈人做群发,并补充几个实际开发中容易踩的坑。

一、标签体系设计与基础接口
微信公众平台允许一个公众号最多创建100个标签,每个粉丝身上最多可以挂20个标签。在动手写代码之前,建议先规划好标签体系,而不是想到一个建一个。常见的做法是分层设计:一层是来源标签(如线下活动、广告投放、自然增长),一层是行为标签(如月活用户、付费用户),一层是偏好标签(如科技数码、母婴育儿)。分层的好处是后续组合圈人时维度清晰,不会出现标签爆炸的情况。
标签相关的接口都在公众号管理接口组里,调用前需要拿到access_token。创建标签的接口是https://api.weixin.qq.com/cgi-bin/tags/create,请求体里传一个tag对象,包含name字段。下面用PHP演示创建、查询、修改、删除四个基本操作。
<?php
function httpPost($url, $json) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
$res = curl_exec($ch);
curl_close($ch);
return json_decode($res, true);
}
$token = '你的access_token';
// 创建标签
$result = httpPost(
"https://api.weixin.qq.com/cgi-bin/tags/create?access_token={$token}",
json_encode(['tag' => ['name' => '付费用户']], JSON_UNESCAPED_UNICODE)
);
// 获取公众号已有标签列表
$tags = json_decode(file_get_contents(
"https://api.weixin.qq.com/cgi-bin/tags/get?access_token={$token}"
), true);
// 修改标签名称
httpPost(
"https://api.weixin.qq.com/cgi-bin/tags/update?access_token={$token}",
json_encode(['tag' => ['id' => 101, 'name' => '高价值付费用户']], JSON_UNESCAPED_UNICODE)
);
// 删除标签
httpPost(
"https://api.weixin.qq.com/cgi-bin/tags/delete?access_token={$token}",
json_encode(['tag' => ['id' => 101]], JSON_UNESCAPED_UNICODE)
);<?php需要注意两点:一是标签名不能重复,重复创建会返回错误码45186;二是删除标签时要谨慎,删除后挂在用户身上的该标签会一并消失,且标签id不会被复用,频繁增删会浪费100个标签的额度。修改接口只能改名字,不能改id。
二、批量给用户打标签与取消标签
有了标签之后,下一步就是把用户和标签关联起来。单个打标的接口一次只能操作一个用户一个标签,如果粉丝量达到几万甚至几十万,逐个调用速度会非常慢,还容易触发接口频率限制。微信提供了批量接口cgi-bin/tags/members/batchtagging,一次最多可以对50个openid打同一个标签,取消标签则用batchuntagging。
实际项目中,批量打标的数据来源通常有几种:一是用户关注时通过带参二维码或关注来源渠道自动打标;二是用户在小程序或H5里提交了表单、完成了购买,业务系统异步回调打标;三是定期跑批,根据用户行为数据计算规则后批量更新。下面的例子演示了分批处理一段openid列表的写法,重点在于按50一组切分数组,并对失败的用户做重试记录。
<?php
function batchTagging($token, $tagId, array $openids) {
$failed = [];
// 每50个openid为一组,接口单次上限是50
foreach (array_chunk($openids, 50) as $group) {
$res = httpPost(
"https://api.weixin.qq.com/cgi-bin/tags/members/batchtagging?access_token={$token}",
json_encode([
'openid_list' => $group,
'tagid' => $tagId
])
);
if ($res['errcode'] != 0) {
// 记录失败的组,交给后续任务重试
$failed[] = ['group' => $group, 'errcode' => $res['errcode']];
}
}
return $failed;
}
// 获取某个标签下的用户列表(可用于核对打标结果)
function getTagUsers($token, $tagId, $nextOpenid = '') {
$url = "https://api.weixin.qq.com/cgi-bin/user/tag/get?access_token={$token}"
. "&tagid={$tagId}&next_openid={$nextOpenid}";
return json_decode(file_get_contents($url), true);
}<?php这里有几个细节值得展开。第一,打标接口的频率限制比较严格,如果粉丝量大,建议用消息队列把任务拆碎,控制调用速率,比如每秒不超过10次。第二,错误码40003表示openid不合法,通常是因为该用户已经取消关注,取关用户的openid无法打标,所以业务上要先过滤掉取关用户。第三,查询标签下用户列表时,一次最多返回10000个openid,粉丝多的时候要利用next_openid做游标翻页,循环取完为止,不能只取第一页就认为拿到了全量数据。
还有一个反直觉的点:给用户打标不需要用户授权,因为openid本身就属于公众号维度的身份标识。但如果你的业务用的是UnionID做跨平台用户打通,就要维护好openid和UnionID的映射关系,打标时先把UnionID翻译回当前公众号的openid,否则会一直报40003错误。
三、按标签群发消息的完整实现
标签圈好了,接下来就是按标签推送。群发接口cgi-bin/message/mass/sendall支持按标签id过滤,把filter字段里的is_to_all设为false并指定tag_id即可。订阅号每天1次、服务号每月4次的群发额度是按次计算的,按标签发也占这个额度,所以标签群发更适合做精准触达,而不是当成无限次推送的通道来用。
消息体支持图文、文本、图片、语音、视频、卡券等多种类型。以文本消息为例,请求结构如下。
<?php
$data = [
'filter' => [
'is_to_all' => false,
'tag_id' => 102 // 只发给挂了这个标签的用户
],
'text' => [
'content' => '尊敬的付费会员,您专属的权益日来了!'
],
'msgtype' => 'text'
];
$res = httpPost(
"https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token={$token}",
json_encode($data, JSON_UNESCAPED_UNICODE)
);
// 返回结果里包含msg_id和msg_data_id,可用于后续查询发送状态
// 查询状态接口:cgi-bin/message/mass/get
$check = httpPost(
"https://api.weixin.qq.com/cgi-bin/message/mass/get?access_token={$token}",
json_encode(['msg_id' => $res['msg_id']])
);<?php群发接口调用成功只代表任务提交成功,不代表用户一定收到。微信侧会经历审核和异步下发的过程,所以一定要保存返回的msg_id,通过状态查询接口确认最终的发送结果。如果出现错误码45009,说明触发了接口调用频率限制,需要检查重试逻辑;如果返回40125或40001,则是access_token失效或不是该公众号的token,排查时优先检查token的刷新机制,建议统一由中控服务维护token,多个业务点共用,避免各自刷新导致互相顶掉。
除了正式群发,还有两种补充触达方式值得了解。一种是预览接口cgi-bin/message/mass/preview,可以先把消息发给指定openid验证内容和排版,正式群发前务必预览一次。另一种是客服消息接口,它不占群发次数,但要求用户在最近48小时内与公众号有过互动(发消息、点击菜单等),适合做活跃用户的即时精准提醒。两者的关系可以简单理解为:标签群发覆盖面广但有次数限制,客服消息无次数压力但受48小时窗口约束,实际运营中常常组合使用。
最后提醒一个容易忽略的问题:群发内容的合规性。按标签发送虽然精准,但营销类内容如果包含诱导分享、夸大宣传等违规表述,依然会被平台拦截甚至处罚。建议在业务系统里加一层内容审核流程,群发前人工确认一遍文案,比出问题后申诉省事得多。