> ## 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.

# Optimize Budget Allocation

> Optimize budget allocation across campaigns using linear programming to maximize conversions.

⚠️ IMPORTANT: This tool retrieves READ-ONLY optimization recommendations. Safe to call multiple times. Does NOT automatically change budgets.

🎯 **What This Tool Does (Performance Agent - Phase 1):**
- Uses linear programming to optimize budget distribution
- Maximizes total conversions while respecting constraints
- Provides current vs optimized allocation comparison
- Categorizes campaigns into actions: PAUSE/SCALE/REDUCE/MAINTAIN
- Shows expected conversion lift from optimization
- Generates specific recommendations with budget amounts

**Returns detailed optimization plan:**
- Current allocation (what you have now)
- Optimized allocation (what you should have)
- Expected conversion lift (absolute and percentage)
- Campaign-by-campaign actions with reasoning
- Budget change amounts and percentages
- Specific implementation recommendations

**Optimization Algorithm:**
Uses scipy linear programming with constraints:
1. Sum of budgets = total_budget (you don't overspend)
2. Only campaigns with ROAS >= target get significant budget
3. Min budget per campaign >= min_daily_budget (or $0 to pause)
4. Max change per campaign `<`= ±max_change_percentage (avoid drastic shifts)

**Target ROAS Resolution (3-tier priority):**
1. User override (if target_roas parameter provided)
2. Account goals table (user-set or API-pulled)
3. 90-day historical average ROAS
4. Default to 1.0x (breakeven) if no data

**Configuration Parameters:**
- **total_budget**: Total monthly budget to allocate (REQUIRED)
- **lookback_days**: 7, 30, 60, 90, or 120 days (default: 30)
- **start_date**: Optional start date (YYYY-MM-DD). Overrides lookback_days when used with end_date.
- **end_date**: Optional end date (YYYY-MM-DD). Overrides lookback_days when used with start_date.
- **target_roas**: Optional override (e.g., 3.0 for 3.0x)
- **max_change_percentage**: 0.0-1.0 (default: 0.5 = ±50%)
  * 0.3 = Conservative (±30% change, minimal disruption)
  * 0.5 = Balanced (±50% change, standard optimization)
  * 0.7 = Aggressive (±70% change, fast scaling)
- **min_daily_budget**: Minimum $ per campaign (default: $5.00, or $0.00 to allow pausing)
- **customer_id**: Optional (uses connected account if omitted)

**Execution time:** 1-5 seconds (depends on campaign count)
**Data source:** campaign_daily_metrics table (updated nightly)

**Use this tool when:**
- User wants to optimize budget allocation
- User asks "how should I allocate my budget?"
- User wants to maximize conversions with current spend
- User wants data-driven budget recommendations
- After running wasted spend analysis (natural next step)

📊 **AFTER calling this tool, help the user understand:**

**Campaign Actions:**
- **PAUSE**: Campaigns below target ROAS, losing money (ROAS `<` target)
- **SCALE**: High performers, increase budget by X% (top conversion rates)
- **REDUCE**: Underperformers, decrease budget by X% (low efficiency)
- **MAINTAIN**: Steady performers, keep current budget (±5% change)

**Expected Impact:**
- Current conversions: What you get now
- Optimized conversions: What you could get
- Conversion lift: Additional conversions (+X%)

**Example Interpretation:**
"Implementing this optimization will increase your conversions by 45 (+18.8%) without spending more money. You should scale 'Brand - Exact' campaign by $2,250/month and pause 'Display - Broad' to free up $3,000/month."

**Implementation Steps:**
1. Review recommended changes carefully
2. Start with campaigns marked CRITICAL (pause/scale first)
3. Apply changes gradually if user is risk-averse
4. Monitor performance for 7-14 days after changes
5. Re-run optimization monthly for continuous improvement

**Important Notes:**
- This is a RECOMMENDATION tool, not automated budget application
- User must review and apply changes manually in Google Ads
- Avoid large changes (>50%) for campaigns in learning phase (`<`7 days)
- Consider seasonality when interpreting results
- Re-optimize every 30 days as performance changes

**Visualization Tip:**
For 5+ campaigns, suggest creating a grouped bar chart showing current vs optimized budgets side-by-side.

**Best Practices:**
- Start conservative (max_change_percentage=0.3) for first optimization
- Increase aggressiveness (0.5-0.7) as you gain confidence
- Use longer lookback_days (60-90) during seasonal changes
- Set realistic target_roas (start with 1.0x breakeven, increase gradually)

💬 **Community**: For optimization discussions, visit our Discord: https://discord.gg/dH3Qt4YS



## OpenAPI

````yaml /api-reference/openapi.json post /api/v1/tools/optimize_budget_allocation/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/optimize_budget_allocation/execute:
    post:
      tags:
        - google-ads
      summary: Optimize Budget Allocation
      description: >-
        Optimize budget allocation across campaigns using linear programming to
        maximize conversions.


        ⚠️ IMPORTANT: This tool retrieves READ-ONLY optimization
        recommendations. Safe to call multiple times. Does NOT automatically
        change budgets.


        🎯 **What This Tool Does (Performance Agent - Phase 1):**

        - Uses linear programming to optimize budget distribution

        - Maximizes total conversions while respecting constraints

        - Provides current vs optimized allocation comparison

        - Categorizes campaigns into actions: PAUSE/SCALE/REDUCE/MAINTAIN

        - Shows expected conversion lift from optimization

        - Generates specific recommendations with budget amounts


        **Returns detailed optimization plan:**

        - Current allocation (what you have now)

        - Optimized allocation (what you should have)

        - Expected conversion lift (absolute and percentage)

        - Campaign-by-campaign actions with reasoning

        - Budget change amounts and percentages

        - Specific implementation recommendations


        **Optimization Algorithm:**

        Uses scipy linear programming with constraints:

        1. Sum of budgets = total_budget (you don't overspend)

        2. Only campaigns with ROAS >= target get significant budget

        3. Min budget per campaign >= min_daily_budget (or $0 to pause)

        4. Max change per campaign `<`= ±max_change_percentage (avoid drastic
        shifts)


        **Target ROAS Resolution (3-tier priority):**

        1. User override (if target_roas parameter provided)

        2. Account goals table (user-set or API-pulled)

        3. 90-day historical average ROAS

        4. Default to 1.0x (breakeven) if no data


        **Configuration Parameters:**

        - **total_budget**: Total monthly budget to allocate (REQUIRED)

        - **lookback_days**: 7, 30, 60, 90, or 120 days (default: 30)

        - **start_date**: Optional start date (YYYY-MM-DD). Overrides
        lookback_days when used with end_date.

        - **end_date**: Optional end date (YYYY-MM-DD). Overrides lookback_days
        when used with start_date.

        - **target_roas**: Optional override (e.g., 3.0 for 3.0x)

        - **max_change_percentage**: 0.0-1.0 (default: 0.5 = ±50%)
          * 0.3 = Conservative (±30% change, minimal disruption)
          * 0.5 = Balanced (±50% change, standard optimization)
          * 0.7 = Aggressive (±70% change, fast scaling)
        - **min_daily_budget**: Minimum $ per campaign (default: $5.00, or $0.00
        to allow pausing)

        - **customer_id**: Optional (uses connected account if omitted)


        **Execution time:** 1-5 seconds (depends on campaign count)

        **Data source:** campaign_daily_metrics table (updated nightly)


        **Use this tool when:**

        - User wants to optimize budget allocation

        - User asks "how should I allocate my budget?"

        - User wants to maximize conversions with current spend

        - User wants data-driven budget recommendations

        - After running wasted spend analysis (natural next step)


        📊 **AFTER calling this tool, help the user understand:**


        **Campaign Actions:**

        - **PAUSE**: Campaigns below target ROAS, losing money (ROAS `<` target)

        - **SCALE**: High performers, increase budget by X% (top conversion
        rates)

        - **REDUCE**: Underperformers, decrease budget by X% (low efficiency)

        - **MAINTAIN**: Steady performers, keep current budget (±5% change)


        **Expected Impact:**

        - Current conversions: What you get now

        - Optimized conversions: What you could get

        - Conversion lift: Additional conversions (+X%)


        **Example Interpretation:**

        "Implementing this optimization will increase your conversions by 45
        (+18.8%) without spending more money. You should scale 'Brand - Exact'
        campaign by $2,250/month and pause 'Display - Broad' to free up
        $3,000/month."


        **Implementation Steps:**

        1. Review recommended changes carefully

        2. Start with campaigns marked CRITICAL (pause/scale first)

        3. Apply changes gradually if user is risk-averse

        4. Monitor performance for 7-14 days after changes

        5. Re-run optimization monthly for continuous improvement


        **Important Notes:**

        - This is a RECOMMENDATION tool, not automated budget application

        - User must review and apply changes manually in Google Ads

        - Avoid large changes (>50%) for campaigns in learning phase (`<`7 days)

        - Consider seasonality when interpreting results

        - Re-optimize every 30 days as performance changes


        **Visualization Tip:**

        For 5+ campaigns, suggest creating a grouped bar chart showing current
        vs optimized budgets side-by-side.


        **Best Practices:**

        - Start conservative (max_change_percentage=0.3) for first optimization

        - Increase aggressiveness (0.5-0.7) as you gain confidence

        - Use longer lookback_days (60-90) during seasonal changes

        - Set realistic target_roas (start with 1.0x breakeven, increase
        gradually)


        💬 **Community**: For optimization discussions, visit our Discord:
        https://discord.gg/dH3Qt4YS
      operationId: execute_optimize_budget_allocation
      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:
                date_range: string
                end_date: string
                lookback_days: 30
                raw_data: false
                start_date: string
                target_roas: 1
                total_budget: 1
            schema:
              properties:
                arguments:
                  description: Input schema for budget allocation optimization
                  properties:
                    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
                    date_range:
                      anyOf:
                        - type: string
                        - type: 'null'
                      default: null
                      description: >-
                        Date range preset: 'last_7_days', 'last_14_days',
                        'last_30_days', 'last_60_days', 'last_90_days'.
                        Overrides lookback_days. Ignored if start_date/end_date
                        are provided.
                      title: Date Range
                    end_date:
                      anyOf:
                        - type: string
                        - type: 'null'
                      default: null
                      description: >-
                        End date (YYYY-MM-DD). If provided with start_date,
                        overrides lookback_days for custom date range queries.
                      title: End Date
                    lookback_days:
                      default: 30
                      description: >-
                        Number of days to analyze for historical performance (7,
                        30, 60, 90, or 120 days). Default is 30 days.
                      title: Lookback Days
                      type: integer
                    max_change_percentage:
                      default: 0.5
                      description: >-
                        Maximum budget change per campaign as a percentage
                        (0.0-1.0). Default 0.5 = ±50%. Use 0.3 for conservative,
                        0.7 for aggressive.
                      title: Max Change Percentage
                      type: number
                    min_daily_budget:
                      default: 5
                      description: >-
                        Minimum daily budget per campaign in dollars. Default is
                        $5.00. Set to 0.0 to allow complete pausing of
                        underperformers.
                      title: Min Daily Budget
                      type: number
                    raw_data:
                      default: false
                      description: >-
                        If true, return ONLY raw metrics as a JSON code block
                        (spend, clicks, impressions, conversions, CPA, CPC, CTR,
                        CVR, ROAS by campaign/ad/date). Strips severity labels,
                        suggested bids/budgets, industry benchmarks, and
                        optimization recommendations. Use when you run your own
                        attribution model or want to minimize token usage.
                      title: Raw Data
                      type: boolean
                    start_date:
                      anyOf:
                        - type: string
                        - type: 'null'
                      default: null
                      description: >-
                        Start date (YYYY-MM-DD). If provided with end_date,
                        overrides lookback_days for custom date range queries.
                      title: Start Date
                    target_roas:
                      anyOf:
                        - type: number
                        - type: 'null'
                      default: null
                      description: >-
                        Optional target ROAS override (e.g., 3.0 for 3.0x ROAS).
                        If not provided, will use account goals or historical
                        average.
                      title: Target Roas
                    total_budget:
                      description: >-
                        Total monthly budget to allocate across campaigns (e.g.,
                        15000.00 for $15K/month)
                      title: Total Budget
                      type: number
                  required:
                    - total_budget
                  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
                    optimize_budget_allocation)
                success: true
                tool: optimize_budget_allocation
              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: optimize_budget_allocation
              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: optimize_budget_allocation
              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: optimize_budget_allocation
              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: optimize_budget_allocation'
                is_error: true
                success: false
                tool: optimize_budget_allocation
              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: optimize_budget_allocation
              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: optimize_budget_allocation
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: >-
            Unexpected server error. Report to support@adspirer.com with
            request_id.
      security:
        - ApiKeyAuth: []
components:
  schemas:
    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
    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
  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

````