- Call this tool ONLY ONCE per campaign
- Creates REAL campaigns that cost REAL money
- Do NOT retry automatically if errors occur
- Report errors to user instead of retrying
OpenAPI
/api-reference/openapi.json post /api/v1/tools/create_search_campaign/execute
openapi: 3.1.0
info:
contact:
email: [email protected]
name: ADSMCP Support
description: >-
REST endpoints for every ADSMCP tool. Same surface as the MCP server, over
plain HTTP for consumers that can't speak SSE (n8n, Zapier, Make, curl, any
language's HTTP client).
## Envelope
Every request wraps tool-specific input in an `arguments` object:
```json
{ "arguments": { <tool-specific fields> } }
```
Every response wraps the result in either a success envelope (`success:
true`, `data: {...}`) or an error envelope (`success: false`, `error:
"..."`, `is_error: true`).
## Authentication
Pass your API key as `Authorization: Bearer sk_live_...` on every request.
Generate keys at https://adsmcp.si/dashboard/api-tokens.
## Quota & billing
Every successful billable call decrements your monthly tool-call allowance.
The current counter is attached to every 200 response under `data.quota`:
```json
"quota": { "used": 42, "limit": 150, "tier": "plus", "period_end":
"2026-05-01" }
```
When the limit is hit, the API returns HTTP 402 with a full `quota` block
including `upgrade_url`. Read-only diagnostic tools (`get_usage_status`,
`list_connected_accounts`, `get_connections_status`) are exempt and never
consume quota.
## Idempotency
Write operations accept an `Idempotency-Key: <uuid>` header. A repeated call
with the same key returns the cached result rather than executing twice.
**Strongly recommended for n8n, Zapier, and any retry-prone client** — it
prevents duplicate campaigns when networks misbehave. Generate a fresh UUID
per logical operation (not per retry).
## Multi-account users
Customers with multiple connected accounts on the same platform (e.g. an
agency with 10 Meta ad accounts) must specify which account to use via
`ad_account_id`, `customer_id`, `advertiser_id`, or `account_id` depending
on the platform. Omitting it returns HTTP 400 with a list of valid account
IDs. See `list_connected_accounts` to discover available IDs.
## HTTP status codes
- `200` — success (parse `data`)
- `400` — tool-level error (surface `error` to users)
- `401` — bad/missing API key
- `402` — ADSMCP quota exhausted
- `404` — unknown tool name
- `429` — upstream ad platform rate-limited us (Meta/Google/etc.) — retry
with backoff
- `500` — server error, report to support
## Streaming
This endpoint is plain request-response JSON. There is **no SSE, no chunked
streaming**. Safe to use from n8n Cloud's HTTP Request node, Zapier
Webhooks, Make HTTP module, curl, and every mainstream HTTP library.
title: ADSMCP REST API
version: 1.0.0
servers:
- description: Production
url: https://api.adsmcp.si
security: []
tags:
- description: Audit tools
name: audit
- description: General (Account Management) tools
name: general
- description: Google Ads tools
name: google-ads
- description: LinkedIn Ads tools
name: linkedin-ads
- description: Meta Ads tools
name: meta-ads
- description: Monitoring & Reporting tools
name: monitoring
- description: TikTok Ads tools
name: tiktok-ads
paths:
/api/v1/tools/create_search_campaign/execute:
post:
tags:
- google-ads
summary: Create Search Campaign
description: >
🔄 LONG-RUNNING TOOL: Creates a Google Ads Search campaign with full
structure.
Emits MCP progress updates during authentication and campaign creation
(typically 5-10 seconds).
Progress stages: validate → commit.
⚠️ CRITICAL WARNING ⚠️
- Call this tool ONLY ONCE per campaign
- Creates REAL campaigns that cost REAL money
- Do NOT retry automatically if errors occur
- Report errors to user instead of retrying
⚠️ GOOGLE ADS POLICY NOTE:
Avoid keywords related to health conditions, medical treatments,
financial hardship, or political topics. These may trigger policy
violations. Use general service terms instead.
Example: Use "senior care services" not "nursing care", "home services"
not "medical services"
operationId: execute_create_search_campaign
parameters:
- description: >-
Client-generated UUID to make writes idempotent. Strongly
recommended for write tools. A repeat call with the same key returns
the cached result instead of re-executing. Example:
550e8400-e29b-41d4-a716-446655440000
example: 550e8400-e29b-41d4-a716-446655440000
in: header
name: Idempotency-Key
required: false
schema:
format: uuid
type: string
requestBody:
content:
application/json:
example:
arguments:
ad_groups:
- audiences:
affinity_audiences: null
custom_audiences: null
demographics: null
in_market_audiences: null
descriptions:
- string
final_url: https://example.com
headlines:
- string
keywords:
- string
name: string
sitelinks:
- description1: null
description2: null
final_url: null
link_text: null
bidding_strategy: MAXIMIZE_CONVERSIONS
budget_daily: 1
business_description: string
campaign_name: string
customer_id: string
negative_keywords:
- string
objective: CONVERSIONS
target_locations:
- string
target_roas: 1
website_url: https://example.com
schema:
properties:
arguments:
properties:
ad_groups:
description: >-
1-4 themed ad groups, each with keywords, headlines, and
descriptions. Each ad group represents a theme/audience
segment.
items:
$ref: >-
#/components/schemas/execute_create_search_campaign_AdGroupInput
title: Ad Groups
type: array
bidding_strategy:
default: MAXIMIZE_CONVERSIONS
description: >-
Bidding strategy for the campaign. Options:
'MAXIMIZE_CONVERSIONS' (default) - Get the most
conversions within budget. 'MAXIMIZE_CONVERSION_VALUE' -
Optimize for highest value conversions (requires
target_roas). Recommended: MAXIMIZE_CONVERSIONS for new
campaigns.
title: Bidding Strategy
type: string
budget_daily:
description: >-
Daily budget in the account's native currency.
IMPORTANT: Do NOT convert currencies — pass the user's
amount as-is. Example: if user says '₹1000/day', pass
1000 (not a USD conversion). The Google Ads API
interprets this in the account's currency automatically.
Google recommends minimum ~$10/day USD equivalent for
Search campaigns.
minimum: 1
title: Budget Daily
type: number
business_description:
description: >-
What does your business sell? (e.g., 'Luxury watch
retailer specializing in Rolex')
title: Business Description
type: string
campaign_name:
description: Campaign name (e.g., 'Luxury Watches Q4 2025')
title: Campaign Name
type: string
customer_id:
anyOf:
- type: string
- type: 'null'
default: null
description: >-
Google Ads customer ID. Required for multi-account
users. Get from list_connected_accounts.
title: Customer Id
negative_keywords:
anyOf:
- items:
type: string
type: array
- type: 'null'
default: null
description: >-
Campaign-level negative keywords (optional, e.g.,
['free', 'cheap', 'used'])
title: Negative Keywords
objective:
default: CONVERSIONS
description: >-
Campaign objective/goal. Options: 'CONVERSIONS'
(default) - Track and optimize for sales, leads, or
sign-ups. 'CLICKS' - Maximize website traffic.
Recommended: CONVERSIONS for performance-focused
campaigns.
title: Objective
type: string
target_locations:
description: >-
Geographic targets — supports countries, states, cities,
and regions globally. Examples: ['India'], ['Mumbai,
India'], ['New York, NY'], ['United States', 'Canada']
items:
type: string
title: Target Locations
type: array
target_roas:
anyOf:
- type: number
- type: 'null'
default: null
description: >-
Target ROAS (Return on Ad Spend) for
MAXIMIZE_CONVERSION_VALUE bidding. Example: 3.0 means
you want $3 revenue for every $1 spent. Only required
when bidding_strategy='MAXIMIZE_CONVERSION_VALUE'.
title: Target Roas
website_url:
description: Your business website URL (landing page for ads)
title: Website Url
type: string
required:
- campaign_name
- business_description
- website_url
- budget_daily
- target_locations
- ad_groups
type: object
required:
- arguments
type: object
description: >-
All tool arguments are wrapped in an `arguments` object. The fields
accepted inside `arguments` are listed below — required fields are
marked with a red asterisk.
required: true
responses:
'200':
content:
application/json:
example:
data:
quota:
limit: 150
period_end: '2026-05-01'
tier: plus
used: 42
text: (tool-specific textual output for create_search_campaign)
success: true
tool: create_search_campaign
schema:
$ref: '#/components/schemas/SuccessResponse'
description: >-
Tool executed successfully. `data.text` carries the human-readable
result (markdown-friendly). `data.quota` shows your current usage
against the plan limit. `data.structured` appears when the tool
emits machine-parseable structured content. `data.content` appears
for tools that return non-text blocks (images, resources).
'400':
content:
application/json:
example:
error: >-
You have 25 meta_ads accounts connected. Please specify which
account to use by passing the ad_account_id parameter:
- Acme Holdings (ad_account_id="act_123456789")
- Acme EU (ad_account_id="act_987654321")
is_error: true
success: false
tool: create_search_campaign
schema:
$ref: '#/components/schemas/ErrorResponse'
description: >-
Tool-level error. The `error` string is safe to surface to end
users. Common causes: missing required argument, multi-account user
didn't specify which account, upstream platform validation failure.
'401':
content:
application/json:
example:
error: >-
Not authenticated. Please connect your ADSMCP account first
at https://adsmcp.si
is_error: true
success: false
tool: create_search_campaign
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Invalid, expired, or revoked API key.
'402':
content:
application/json:
example:
error: |-
🚨 Monthly limit reached (150/150 tool calls on Plus tier).
Upgrade to Pro at https://adsmcp.si to keep building.
is_error: true
quota:
limit: 150
period_end: '2026-05-01'
tier: plus
upgrade_url: https://adsmcp.si
used: 150
success: false
tool: create_search_campaign
schema:
$ref: '#/components/schemas/QuotaErrorResponse'
description: >-
Quota exhausted for the current billing period. The response
includes a `quota` block with the current used/limit/tier values and
an `upgrade_url` to move up a tier.
'404':
content:
application/json:
example:
error: 'Tool not found: create_search_campaign'
is_error: true
success: false
tool: create_search_campaign
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Unknown tool_name. Check /openapi.json for the full catalog.
'429':
content:
application/json:
example:
error: >-
Upstream platform rate limit hit (Meta Business Use Case
throttle at 95%). Retry after 60 seconds.
is_error: true
success: false
tool: create_search_campaign
schema:
$ref: '#/components/schemas/ErrorResponse'
description: >-
Rate-limited by the upstream ad platform (Meta, Google, LinkedIn,
TikTok). Retry with exponential backoff. Not the same as ADSMCP's
own quota — that returns 402.
'500':
content:
application/json:
example:
error: 'Internal error: RuntimeError'
is_error: true
success: false
tool: create_search_campaign
schema:
$ref: '#/components/schemas/ErrorResponse'
description: >-
Unexpected server error. Report to [email protected] with
request_id.
security:
- ApiKeyAuth: []
components:
schemas:
execute_create_search_campaign_AdGroupInput:
properties:
audiences:
anyOf:
- $ref: >-
#/components/schemas/execute_create_search_campaign_AudienceTargeting
- type: 'null'
default: null
description: >-
Optional: Audience targeting to reach specific user segments. Leave
empty for broad reach. Use if you have specific audience IDs from
Google Ads.
descriptions:
description: >-
EXACTLY 4 descriptions required (max 80 chars each - STRICT LIMIT).
Google Ads requires 4 descriptions for Responsive Search Ads. ANY
description over 80 chars will be REJECTED. Commas ARE allowed
within descriptions. Example: ["Discover handcrafted timepieces for
collectors."] (50 chars). MUST BE JSON ARRAY. COUNT CHARACTERS
BEFORE CALLING!
items:
type: string
maxItems: 4
minItems: 4
title: Descriptions
type: array
final_url:
description: >-
Landing page URL for this ad group (usually the website URL or a
specific page)
title: Final Url
type: string
headlines:
description: >-
EXACTLY 15 headlines required (max 30 chars each). Google Ads
requires 15 headlines for Responsive Search Ads. Commas ARE allowed
within headlines. Example: ["Premium Watches, Since 1950", "Luxury
Timepieces", ...]. MUST BE JSON ARRAY - do NOT send comma-delimited
strings.
items:
type: string
maxItems: 15
minItems: 15
title: Headlines
type: array
keywords:
description: >-
Keywords for this ad group (10-20 keywords recommended, minimum 5).
Should come from Google Keyword Planner research. Will use BROAD
match.
items:
type: string
minItems: 5
title: Keywords
type: array
name:
description: Ad group name (e.g., 'Premium Watches', 'Luxury Timepieces')
title: Name
type: string
sitelinks:
anyOf:
- items:
$ref: >-
#/components/schemas/execute_create_search_campaign_SitelinkExtension
maxItems: 4
type: array
- type: 'null'
default: null
description: >-
Optional: 2-4 sitelink extensions (additional links below ad).
IMPORTANT: All sitelink URLs MUST be same domain as final_url.
Example: [{'link_text': 'Free Shipping', 'final_url':
'https://example.com/shipping', 'description1': 'Fast delivery',
'description2': 'Order by 5pm'}]
title: Sitelinks
required:
- name
- keywords
- headlines
- descriptions
- final_url
title: AdGroupInput
type: object
SuccessResponse:
description: >-
Returned on HTTP 200. `data.text` is the primary human-readable output.
`data.quota` is always present for billable calls. `data.structured` is
set only when the tool emits machine-parseable structured content.
`data.content` is set only when the tool emits non-text content blocks.
properties:
data:
properties:
content:
description: Non-text content blocks (images, resources).
items:
$ref: '#/components/schemas/ContentBlock'
type: array
quota:
$ref: '#/components/schemas/QuotaBlock'
structured:
additionalProperties: true
description: >-
Machine-parseable structured content when the tool provides it
(e.g. account selection prompts, widget payloads).
type: object
text:
description: >-
Human-readable output. Markdown-friendly for tools that emit
formatted lists, tables, or recommendations.
type: string
required:
- text
type: object
success:
const: true
type: boolean
tool:
description: Echoed tool_name from the request URL.
type: string
required:
- success
- data
- tool
type: object
ErrorResponse:
description: >-
Returned on HTTP 4xx / 5xx (except 402 which uses `QuotaErrorResponse`).
`error` is always a human-readable string safe to surface to end users.
properties:
error:
description: Human-readable error message.
type: string
is_error:
const: true
type: boolean
structured_content:
additionalProperties: true
description: >-
Present when the underlying tool attached structured metadata to the
error (e.g. account-selection lists).
type: object
success:
const: false
type: boolean
tool:
type: string
required:
- success
- error
- tool
type: object
QuotaErrorResponse:
description: >-
Returned on HTTP 402 when the monthly quota is exhausted. Identical to
`ErrorResponse` but with an additional `quota` block.
properties:
error:
type: string
is_error:
const: true
type: boolean
quota:
$ref: '#/components/schemas/QuotaBlock'
success:
const: false
type: boolean
tool:
type: string
required:
- success
- error
- tool
- quota
type: object
execute_create_search_campaign_AudienceTargeting:
description: Audience targeting for ad groups - reach specific user segments
properties:
affinity_audiences:
anyOf:
- items:
type: string
type: array
- type: 'null'
default: null
description: >-
Affinity audience IDs. Users with demonstrated interest in related
topics. Leave empty to skip audience targeting.
title: Affinity Audiences
custom_audiences:
anyOf:
- items:
type: string
type: array
- type: 'null'
default: null
description: >-
Custom audience IDs from your Google Ads account. Leave empty if you
don't have pre-created custom audiences.
title: Custom Audiences
demographics:
anyOf:
- $ref: >-
#/components/schemas/execute_create_search_campaign_DemographicTargeting
- type: 'null'
default: null
description: >-
Optional: Target specific demographics (age, gender, income).
Analyze the business to determine ideal customer profile. Leave
empty for broad demographic reach.
in_market_audiences:
anyOf:
- items:
type: string
type: array
- type: 'null'
default: null
description: >-
In-market audience IDs. Users actively researching/comparing
products. Leave empty to skip audience targeting.
title: In Market Audiences
title: AudienceTargeting
type: object
execute_create_search_campaign_SitelinkExtension:
description: >-
Sitelink extension - additional links that appear below your ad
CRITICAL SITELINK RULES:
- URL MUST be same domain as main ad (e.g., if ad goes to example.com,
sitelinks must go to example.com/*)
- Link text must be DESCRIPTIVE and RELEVANT to the page it links to
- Each sitelink must go to a UNIQUE URL (no duplicates)
- URLs must be HTTPS and WORKING pages (Google will reject broken links)
- Don't use generic terms like "Click Here" - be specific
- Common violations: wrong domain, broken links, duplicate URLs,
misleading text
properties:
description1:
anyOf:
- maxLength: 35
type: string
- type: 'null'
default: null
description: >-
First description line (max 35 chars, optional). Adds context to the
link. Example: 'Fast nationwide delivery'
title: Description1
description2:
anyOf:
- maxLength: 35
type: string
- type: 'null'
default: null
description: >-
Second description line (max 35 chars, required if description1
provided). Example: 'Orders ship within 24 hours'
title: Description2
final_url:
description: >-
HTTPS URL for this sitelink. MUST be same domain as main ad URL.
Must be a working page (Google validates). Each sitelink needs
UNIQUE URL. Example: If ad goes to 'https://example.com', sitelink
could be 'https://example.com/shipping'
title: Final Url
type: string
link_text:
description: >-
Link text displayed (max 25 chars). MUST be descriptive and match
the page content. Good: 'Free Shipping Info', 'Pricing Plans',
'Contact Support'. Bad: 'Click Here', 'Learn More', 'Website'
maxLength: 25
title: Link Text
type: string
required:
- link_text
- final_url
title: SitelinkExtension
type: object
ContentBlock:
additionalProperties: true
description: >-
A non-text content block. Rare today; tools that attach images,
resources, or widget payloads populate `data.content` with blocks of
this shape.
properties:
type:
example: image
type: string
required:
- type
type: object
QuotaBlock:
description: >-
Current quota state for the API key owner. Attached to every successful
billable response under `data.quota`, and to 402 errors under `quota`
(plus `upgrade_url`).
properties:
limit:
description: Tool call allowance for the current tier.
example: 150
type: integer
period_end:
description: When the monthly counter resets (ISO date).
example: '2026-05-01'
format: date
type: string
tier:
description: Subscription tier of the account that owns the API key.
enum:
- free
- plus
- pro
- max
example: plus
type: string
upgrade_url:
description: Upgrade link (present only on 402 quota errors).
format: uri
type: string
used:
description: Tool calls consumed this billing period.
example: 42
type: integer
required:
- used
- limit
- tier
- period_end
type: object
execute_create_search_campaign_DemographicTargeting:
description: >-
Demographic targeting - target specific age groups, genders, and income
levels
properties:
age_ranges:
anyOf:
- items:
type: string
type: array
- type: 'null'
default: null
description: >-
Target specific age groups. Options: 'AGE_RANGE_18_24',
'AGE_RANGE_25_34', 'AGE_RANGE_35_44', 'AGE_RANGE_45_54',
'AGE_RANGE_55_64', 'AGE_RANGE_65_UP'. Leave empty to target all
ages. Example: ['AGE_RANGE_25_34', 'AGE_RANGE_35_44'] for 25-44 year
olds.
title: Age Ranges
genders:
anyOf:
- items:
type: string
type: array
- type: 'null'
default: null
description: >-
Target specific genders. Options: 'MALE', 'FEMALE'. Leave empty to
target all genders. Example: ['MALE'] for male-only targeting.
title: Genders
income_ranges:
anyOf:
- items:
type: string
type: array
- type: 'null'
default: null
description: >-
Target household income levels. Options: 'INCOME_RANGE_0_50' (Lower
50%), 'INCOME_RANGE_50_60' (50-60%), 'INCOME_RANGE_60_70' (60-70%),
'INCOME_RANGE_70_80' (70-80%), 'INCOME_RANGE_80_90' (80-90%),
'INCOME_RANGE_TOP_10' (Top 10%). Leave empty to target all income
levels. Example: ['INCOME_RANGE_TOP_10', 'INCOME_RANGE_80_90'] for
top 20% earners.
title: Income Ranges
title: DemographicTargeting
type: object
securitySchemes:
ApiKeyAuth:
bearerFormat: API Key (sk_live_...)
description: >-
API key from https://adsmcp.si/dashboard/api-tokens. Prefix `sk_live_`. Treat as a
secret — never commit.
scheme: bearer
type: http