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

# Analyze Meta Wasted Spend

> User asks about Meta/Facebook/Instagram ad spend efficiency, wasted money, underperforming campaigns, placement optimization, or creative fatigue.

This tool identifies Meta Ads campaigns and placements that are wasting money by performing below target ROAS or below breakeven (ROAS `<` 1.0). It extends the base wasted spend detector with Meta-specific features:

Returns:
- Campaign-level wasted spend analysis (ROAS `<` 1.0 = actual losses)
- Underperforming spend analysis (1.0 `<`= ROAS `<` target = opportunity cost)
- Placement-level breakdown (which placements are wasting money: Feed, Stories, Reels, etc.)
- Creative fatigue detection (high frequency + declining CTR)
- Actionable recommendations for campaigns, placements, and creatives
- Quick action items

When to use this tool:
- "Where am I wasting money on Meta/Facebook ads?"
- "Which Meta placements should I exclude?"
- "Are any of my Facebook creatives fatigued?"
- "How can I reduce wasted ad spend on Instagram?"
- "Which Meta campaigns are losing money?"
- "What's my Meta ROAS by placement?"

Parameters:
- lookback_days: 7, 30 (default), 60, 90, or 120 days
- 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.
⚠️ DATE CLARIFICATION: If the user's date request is vague or ambiguous (e.g., "March to June" without a year, "last quarter", "recently", "a few months ago"), ask the user to specify exact dates before calling this tool. Do not assume or guess dates.
- target_roas: Optional override (e.g., 3.0 for 3.0x ROAS)
- include_placements: true (default) - Include placement-level analysis
- include_fatigue: true (default) - Include creative fatigue analysis
- ad_account_id: Required for multi-account users. Get from list_connected_accounts

Execution time: 2-5 seconds (cached database query with analysis)
Data source: campaign_daily_metrics + meta_placement_daily_metrics + meta_ad_creative_metrics tables

Key definitions:
- Wasted Spend (ROAS `<` 1.0): Actual money lost - for every $1 spent, getting back `<`$1
- Underperforming (1.0 `<`= ROAS `<` target): Profitable but below target - opportunity cost
- Creative Fatigue: Ads with frequency >4x (cold traffic) or >7x (retargeting) showing declining CTR

Meta-specific placement optimization:
- Audience Network often has lowest ROAS - consider excluding
- Instagram Stories/Reels typically perform differently than Feed
- Facebook Marketplace can waste spend if not relevant

**Quick Actions (IMPORTANT — read severity context first):**
- ⏳ LEARNING campaigns → Do NOT pause. Monitor for 14+ days before judging.
- ❓ INSUFFICIENT_DATA campaigns → Need more spend before analysis is meaningful.
- 🚨 CRITICAL campaigns (established, 14+ days, ZERO conversions) → Consider pausing
- 🚨 CRITICAL campaigns (established, 14+ days, HAS conversions) → Review performance, verify revenue in ad platform before reducing budget
- 🔴 HIGH severity (established, 14+ days) → Consider reducing budget by 50-70%
- 🟡 MEDIUM → Optimize targeting, ad copy, landing pages

⚠️ **NEVER say "pause" for a campaign that has conversions.** Say "review" or "reduce budget" instead.
⚠️ **NEVER recommend pausing a campaign in LEARNING phase.**
⚠️ **If ALL campaigns are LEARNING or INSUFFICIENT_DATA, tell the user their account is too new for waste analysis and recommend checking back in 2 weeks.**
⚠️ **Consider campaign objective: brand awareness campaigns will not have ROAS data. This is normal.**
⚠️ **When data confidence is MEDIUM or LOW, soften all recommendations and add verification prompts.**



## OpenAPI

````yaml /api-reference/openapi.json post /api/v1/tools/analyze_meta_wasted_spend/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/analyze_meta_wasted_spend/execute:
    post:
      tags:
        - meta-ads
      summary: Analyze Meta Wasted Spend
      description: >-
        User asks about Meta/Facebook/Instagram ad spend efficiency, wasted
        money, underperforming campaigns, placement optimization, or creative
        fatigue.


        This tool identifies Meta Ads campaigns and placements that are wasting
        money by performing below target ROAS or below breakeven (ROAS `<` 1.0).
        It extends the base wasted spend detector with Meta-specific features:


        Returns:

        - Campaign-level wasted spend analysis (ROAS `<` 1.0 = actual losses)

        - Underperforming spend analysis (1.0 `<`= ROAS `<` target = opportunity
        cost)

        - Placement-level breakdown (which placements are wasting money: Feed,
        Stories, Reels, etc.)

        - Creative fatigue detection (high frequency + declining CTR)

        - Actionable recommendations for campaigns, placements, and creatives

        - Quick action items


        When to use this tool:

        - "Where am I wasting money on Meta/Facebook ads?"

        - "Which Meta placements should I exclude?"

        - "Are any of my Facebook creatives fatigued?"

        - "How can I reduce wasted ad spend on Instagram?"

        - "Which Meta campaigns are losing money?"

        - "What's my Meta ROAS by placement?"


        Parameters:

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

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

        ⚠️ DATE CLARIFICATION: If the user's date request is vague or ambiguous
        (e.g., "March to June" without a year, "last quarter", "recently", "a
        few months ago"), ask the user to specify exact dates before calling
        this tool. Do not assume or guess dates.

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

        - include_placements: true (default) - Include placement-level analysis

        - include_fatigue: true (default) - Include creative fatigue analysis

        - ad_account_id: Required for multi-account users. Get from
        list_connected_accounts


        Execution time: 2-5 seconds (cached database query with analysis)

        Data source: campaign_daily_metrics + meta_placement_daily_metrics +
        meta_ad_creative_metrics tables


        Key definitions:

        - Wasted Spend (ROAS `<` 1.0): Actual money lost - for every $1 spent,
        getting back `<`$1

        - Underperforming (1.0 `<`= ROAS `<` target): Profitable but below
        target - opportunity cost

        - Creative Fatigue: Ads with frequency >4x (cold traffic) or >7x
        (retargeting) showing declining CTR


        Meta-specific placement optimization:

        - Audience Network often has lowest ROAS - consider excluding

        - Instagram Stories/Reels typically perform differently than Feed

        - Facebook Marketplace can waste spend if not relevant


        **Quick Actions (IMPORTANT — read severity context first):**

        - ⏳ LEARNING campaigns → Do NOT pause. Monitor for 14+ days before
        judging.

        - ❓ INSUFFICIENT_DATA campaigns → Need more spend before analysis is
        meaningful.

        - 🚨 CRITICAL campaigns (established, 14+ days, ZERO conversions) →
        Consider pausing

        - 🚨 CRITICAL campaigns (established, 14+ days, HAS conversions) →
        Review performance, verify revenue in ad platform before reducing budget

        - 🔴 HIGH severity (established, 14+ days) → Consider reducing budget by
        50-70%

        - 🟡 MEDIUM → Optimize targeting, ad copy, landing pages


        ⚠️ **NEVER say "pause" for a campaign that has conversions.** Say
        "review" or "reduce budget" instead.

        ⚠️ **NEVER recommend pausing a campaign in LEARNING phase.**

        ⚠️ **If ALL campaigns are LEARNING or INSUFFICIENT_DATA, tell the user
        their account is too new for waste analysis and recommend checking back
        in 2 weeks.**

        ⚠️ **Consider campaign objective: brand awareness campaigns will not
        have ROAS data. This is normal.**

        ⚠️ **When data confidence is MEDIUM or LOW, soften all recommendations
        and add verification prompts.**
      operationId: execute_analyze_meta_wasted_spend
      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
            schema:
              properties:
                arguments:
                  description: >-
                    Input schema for Meta wasted spend analysis with placement
                    and fatigue detection
                  properties:
                    ad_account_id:
                      anyOf:
                        - type: string
                        - type: 'null'
                      default: null
                      description: >-
                        Meta Ad Account ID. Required for multi-account users.
                        Get from list_connected_accounts.
                      title: Ad Account 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
                    include_fatigue:
                      default: true
                      description: Include creative fatigue analysis. Default is true.
                      title: Include Fatigue
                      type: boolean
                    include_placements:
                      default: true
                      description: >-
                        Include placement-level analysis (Feed, Stories, Reels,
                        etc.). Default is true.
                      title: Include Placements
                      type: boolean
                    lookback_days:
                      default: 30
                      description: >-
                        Number of days to analyze (7, 30, 60, 90, or 120 days).
                        Default is 30 days.
                      title: Lookback Days
                      type: integer
                    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
                  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 analyze_meta_wasted_spend)
                success: true
                tool: analyze_meta_wasted_spend
              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: analyze_meta_wasted_spend
              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: analyze_meta_wasted_spend
              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: analyze_meta_wasted_spend
              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: analyze_meta_wasted_spend'
                is_error: true
                success: false
                tool: analyze_meta_wasted_spend
              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: analyze_meta_wasted_spend
              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: analyze_meta_wasted_spend
              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

````