POST/campaigns/:campaignId/leads/batch-update
Credits: 0
LinkedIn risk: None
Rate limit: 6 / minute (max 100 items)
Optional Idempotency-Key (TTL 24h).
Purpose
Update tag and/or excluded for up to 100 leads in one call. Prefer this over many PATCH …/leads/:leadId calls.
Exclude is workspace-global (exclusion list + matching leads across campaigns) via one bulk exclude when a LinkedIn URL is available. If there is no LinkedIn URL, only the campaign lead’s excluded flag is set locally (same as single PATCH). Auto-exclude tags (Not interested, Wrong person) apply immediately — no pending acknowledgement / polite-close deferral (differs from single PATCH / inbox).
Request
POST /campaigns/search_abc/leads/batch-update
X-API-Key: sk_live_...
Idempotency-Key: optional-key
Content-Type: application/json
{
"updates": [
{ "leadId": "lead_xyz", "excluded": true },
{ "leadId": "lead_abc", "tag": "Interested" }
]
}
| Field | Notes |
|---|---|
updates | Yes — non-empty array, length 1…100 (hard max) |
updates[].leadId | Required; lead_ prefix normalized |
updates[].tag | Optional; allowlisted labels or null to clear |
updates[].excluded | Optional boolean |
At least one of tag / excluded per item (otherwise whole-request 400). Unknown top-level keys → 400. Soft per-item: bad id, duplicate leadId (first wins), auto-exclude tag + excluded: false.
Idempotency: array order matters (fingerprint). Persist 200 only when summary.updated > 0.
Response 200
{
"results": [
{
"index": 0,
"leadId": "lead_xyz",
"status": "updated",
"tag": null,
"excluded": true
}
],
"summary": {
"total": 1,
"updated": 1,
"not_found": 0,
"validation_error": 0,
"failed": 0
},
"creditsCharged": 0
}
Statuses: updated · not_found · validation_error · failed.
If the request fails mid-batch after durable writes, response is still 200 with completed rows as updated (only fields that actually landed — e.g. a tagged auto-exclude row does not claim excluded: true until the bulk/local exclude step succeeds) and remaining as failed. Idempotency-Key is persisted when summary.updated > 0.
Per-item soft failures may include an error object (code, message).
Errors
Whole-request failures (not partial 200):
error.code | When |
|---|---|
unauthorized | Missing/invalid API key |
validation_error | Bad envelope / empty updates / item missing both tag and excluded / unknown keys / over-length |
not_found | Campaign missing before any update |
conflict | Idempotency conflict |
rate_limited | Batch-update RPM (6/min) or global RPM |
upstream_unavailable | Idempotency reserve fail — no updates |
vs single PATCH
| Behavior | PATCH | Batch update |
|---|---|---|
| Auto-exclude deferral | Possible | No — immediate |
| Global exclude (with LinkedIn URL) | Yes (per lead) | Yes (one bulk) |
| No LinkedIn URL + exclude | Lead flag only | Same (local mark) |
excluded: false | Lead flag only | Same |
lead.tagged webhook | 1 | Up to 100 |
MCP
Tool: update_leads_batch. Outbound REST timeout 300s (large exclude scans).
See also
- Patch lead — single-lead tag / exclude
- Batch delete — permanent remove
- List leads —
excluded=trueafter exclude