> ## Documentation Index
> Fetch the complete documentation index at: https://www.adspirer.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Search Campaign

> 🔄 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"




## OpenAPI

````yaml /api-reference/openapi.json post /api/v1/tools/create_search_campaign/execute
openapi: 3.1.0
info:
  contact:
    email: support@adspirer.com
    name: Adspirer Support
  description: >-
    REST endpoints for every Adspirer 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://adspirer.ai/keys.


    ## 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` — Adspirer 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: Adspirer REST API
  version: 1.0.0
servers:
  - description: Production
    url: https://api.adspirer.ai
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 Adspirer account first
                  at https://adspirer.ai
                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://adspirer.ai to keep building.
                is_error: true
                quota:
                  limit: 150
                  period_end: '2026-05-01'
                  tier: plus
                  upgrade_url: https://adspirer.ai
                  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 Adspirer'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 support@adspirer.com 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://adspirer.ai/keys. Prefix `sk_live_`. Treat as a
        secret — never commit.
      scheme: bearer
      type: http

````