Documentation
Marketing
marketing.ad_campaigns
REST
ShippingPOST /api/v1/marketing/ad_campaignsMCP tool
Livemarketing.ad_campaignsExposed on: REST API · Shop MCP server · Claude connector · Dash (in-app copilot) · Zapier. Part of the Marketing domain.
Operating contract
The ad campaigns this shop has imported from its ad account, with what each one is set to spend and whether it is running.
status is Google's own — ENABLED, PAUSED or REMOVED — reported as the last import saw it, not as a live read. last_seen_at says how fresh that is; a campaign whose last_seen_at is a week old was not in the most recent sync, which usually means it was deleted at the ad account.
managed IS THE CONSENT FLAG AND IT DEFAULTS TO FALSE. It is the owner's explicit 'yes, the optimizer may work on this campaign'. Importing is reading, never consent to act — so a campaign with managed: false is one the shop's autopilot will not touch, whatever else is switched on.
daily_budget is a setting, not a spend. What was actually spent is marketing.ad_spend, and the two routinely differ.
schema_missing: true means this shop's database has not had the ads-import migration applied, so an empty list is not evidence of no campaigns.
READ ONLY. Every ads write spends the shop's money at a third party, and switching the optimizer on is an owner action by design.
TRUNCATION: omitted is how many further campaigns exist. When it is not 0, raise limit before telling a shop what it is running.
Who may call it
- Permission
marketing.accessThe caller must hold Reviews & market at the ACT level. A read-only dashboard grant on the same section is refused.- Plan
- Every planNo plan gate. Available on every Service VIN plan.
- Retries
naturalNaturally idempotent — running it twice leaves the same world as running it once. A retrying integration needs no key.- Rate class
readCounted against the read budget — the widest of the four.
Partial answers
omitted is how many matching records are not in the response, and 0 is a real answer meaning you have all of them. There is no cursor here — narrow the filter instead.
Do not summarise from a truncated response
Input
| Field | Type | Description |
|---|---|---|
| managed_only | boolean | True returns only campaigns the owner opted into the optimizer for. |
| limit | integer1–200 | How many campaigns to return, 1-200. Defaults to 50. Default:50 |
Output
| Field | Type | Description |
|---|---|---|
| schema_missing | boolean | — |
| items | object[] | — |
| items[].id | string | — |
| items[].campaign_id | string | — |
| items[].name | string | — |
| items[].channel | string | null | — |
| items[].status | string | null | — |
| items[].daily_budget | number | null | — |
| items[].bidding_strategy | string | null | — |
| items[].managed | boolean | — |
| items[].last_seen_at | string | null | — |
| omitted | number | — |
Examples
Built from this capability's own schema — required fields and the ones carrying a default, and nothing invented. Paste one and it validates.
export SERVICEVIN_API_KEY=svk_live_…
curl -X POST https://www.servicevin.com/api/v1/marketing/ad_campaigns \
-H "Authorization: Bearer $SERVICEVIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"limit":50}'const res = await fetch("https://www.servicevin.com/api/v1/marketing/ad_campaigns", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SERVICEVIN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"limit": 50
}),
});
// Success and failure are both envelopes. Switch on error.code, never
// on error.message — the codes are stable, the messages are for people.
const payload = await res.json();
if (!res.ok) throw new Error(payload.error.code);
const data = payload.data;import os, requests
res = requests.post(
"https://www.servicevin.com/api/v1/marketing/ad_campaigns",
headers={"Authorization": f"Bearer {os.environ['SERVICEVIN_API_KEY']}"},
json={
"limit": 50
},
timeout=30,
)
payload = res.json()
if not res.ok:
raise RuntimeError(payload["error"]["code"])
data = payload["data"]{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "marketing.ad_campaigns",
"arguments": {
"limit": 50
}
}
}Refusals
The four gates run in this order on every surface, and the order is not arbitrary — see Authentication.
| Status | Code | When |
|---|---|---|
| 404 | not_found | The id is unknown, or the feature is not enabled for this account. Deliberately the same answer for both. |
| 403 | forbidden | This login does not hold marketing.access. |
| 422 | validation_error | An argument was wrong. The message names the field. |
| 429 | rate_limited | Too many read calls. Back off and retry. |
| 500 | internal_error | Something failed on our side. Nothing was changed. |