{
  "openapi": "3.0.1",
  "info": {
    "title": "Google Ads Transparency Monitor: New Competitor Ads",
    "description": "Get only your competitors' NEW ads from Google Ads Transparency Center as a daily Slack/webhook digest per competitor and client. Flags launches and new countries. $0.01 per run + $0.01 per competitor check + $0.005 per new ad. First check: free 30-day baseline summary. Not affiliated with Google.",
    "version": "0.3",
    "x-build-id": "MCult6MMJraPyl2fD"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/domestic_buffalograss~competitor-ad-alerts/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-domestic_buffalograss-competitor-ad-alerts",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/domestic_buffalograss~competitor-ad-alerts/runs": {
      "post": {
        "operationId": "runs-sync-domestic_buffalograss-competitor-ad-alerts",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/domestic_buffalograss~competitor-ad-alerts/run-sync": {
      "post": {
        "operationId": "run-sync-domestic_buffalograss-competitor-ad-alerts",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "required": [
          "watchlist"
        ],
        "properties": {
          "watchlist": {
            "title": "Competitors to watch",
            "type": "array",
            "description": "One object per competitor. Fields: <code>label</code> (your name for it), optional <code>client</code> (groups the digest), <code>googleDomain</code> (e.g. patagonia.com; a Transparency Center advertiser URL here is used as the advertiser ID) or <code>googleAdvertiserId</code> (AR... or a Transparency Center advertiser URL), <code>regions</code> (ISO country codes such as US, GB, JP, or \"anywhere\"; a list or a comma-separated text; default US). A competitor listed twice (e.g. for two clients) is checked and billed once, and read in the union of the countries of both entries (at most 20 countries per competitor)."
          },
          "slackWebhookUrl": {
            "title": "Slack Incoming Webhook URL",
            "type": "string",
            "description": "Optional. One digest per run, grouped by client and competitor. Create one in Slack: Apps > Incoming Webhooks (https://hooks.slack.com/services/...). Slack workflow and trigger URLs, and other URLs that cannot receive the digest as written, are refused before anything is read or charged (the run ends as aborted, Invalid input). Stored as a secret; not written to logs or outputs. If a delivery fails, the run still ends as succeeded after the checks and charges (the new ads are in the dataset; the status message starts with the failed notification), and a summary of it (new-ad counts, signals and the top 5 ads per competitor) is sent to this same URL with the next run (a digest with no new ads or signals has nothing to send again). If you change this URL, what was still undelivered for the old one goes to the new one for the competitors still watched under the same label and client (the rest is said in the status message); a summary too long for one Slack message is spread over the following messages. Temporary failures (timeouts, HTTP 408, 429, 5xx) do not stop the run; if Slack refuses the digest 3 runs in a row with a permanent answer (such as 404 or 410), that run ends as aborted and the status message asks to check this URL (one successful delivery resets the count). One Slack message holds at most 50 blocks and about 40,000 characters; competitors beyond that are counted as +N more in the dataset."
          },
          "webhookUrl": {
            "title": "Webhook URL (JSON)",
            "type": "string",
            "description": "Optional. Receives the same digest as JSON (POST, https only, public addresses only, redirects are not followed) - for n8n, Make, Zapier or your own endpoint. Other URLs are refused before anything is read or charged (the run ends as aborted, Invalid input). Each delivery carries an idempotencyKey (also as the Idempotency-Key header). The body is kept under 1 MB: above that each competitor lists its top 5 new ads (with the full count) and what still does not fit is counted in truncated (every row is in the dataset). Stored as a secret. If a delivery fails, the run still ends as succeeded after the checks and charges (the new ads are in the dataset; the status message starts with the failed notification), and a summary of it is sent to this same URL with the next run (a digest with no new ads or signals has nothing to send again). If you change this URL, what was still undelivered for the old one goes to the new one for the competitors still watched under the same label and client (the rest is said in the status message). Temporary failures (timeouts, HTTP 408, 429, 5xx) do not stop the run; if the URL refuses the digest 3 runs in a row with a permanent answer (HTTP 3xx/4xx other than 408/429, or an internal address at connect time), that run ends as aborted and the status message asks to check this URL (one successful delivery resets the count)."
          },
          "language": {
            "title": "Digest language",
            "enum": [
              "en",
              "ja"
            ],
            "type": "string",
            "description": "Language of the Slack digest. English digests show dates in UTC, Japanese digests in Japan time.",
            "default": "en"
          },
          "sendQuietDigest": {
            "title": "Send a digest even when nothing is new",
            "type": "boolean",
            "description": "Off: quiet runs (no new ads, no signals, no failures) send nothing. On: every run sends a short \"no new ads\" status - useful on a weekly schedule.",
            "default": false
          },
          "maxNewAdsPerAdvertiser": {
            "title": "Max new ads per competitor per run",
            "minimum": 1,
            "maximum": 500,
            "type": "integer",
            "description": "New ads beyond this are counted in the digest but not listed or billed, and are not reported again.",
            "default": 50
          },
          "baselineDays": {
            "title": "Baseline window (days)",
            "minimum": 1,
            "maximum": 90,
            "type": "integer",
            "description": "The first check of a competitor (or country) summarises ads shown in this many days, free. Ads first shown before this window (minus the overlap), or before the window of the country's first check, are not billed as new. A competitor or country not checked for clearly longer than this window (the window plus the larger of the overlap and a quarter of the window) gets a fresh free baseline instead of a bill for everything that appeared meanwhile.",
            "default": 30
          },
          "includeBaselineRows": {
            "title": "Write baseline ads as dataset rows",
            "type": "boolean",
            "description": "Off: the first check returns a free summary (counts, formats, latest 10) and the 5 latest baseline ads as free baseline_sample rows. On: every baseline ad is written as a row, billed per row (baseline-row).",
            "default": false
          },
          "stateKey": {
            "title": "State key",
            "type": "string",
            "description": "Optional name of the memory of already-seen ads. Leave empty in a task: each task gets its own memory automatically (named after the task). Outside a task (API calls, a schedule that starts the Actor itself), an empty state key uses the shared memory `main`. The memory is kept per competitor, so editing the watchlist keeps it: an added competitor starts with a free baseline, a removed one is simply no longer checked. To run a different watchlist outside a task, give it its own state key (two watchlists on one key share one memory; the run log warns when one key is used with different Slack/webhook destinations). \"default\" counts as empty. Two runs with the same key at the same time: the second waits up to 5 minutes, then stops without checking or charging (run ends as aborted)."
          },
          "launchThreshold": {
            "title": "Campaign launch signal threshold",
            "minimum": 2,
            "maximum": 100,
            "type": "integer",
            "description": "Flag a \"campaign launch\" when a competitor starts at least this many new ads within 7 days.",
            "default": 5
          },
          "maxChargeUsd": {
            "title": "Maximum charge per run (USD)",
            "minimum": 0,
            "maximum": 1000,
            "type": "number",
            "description": "The run stops between competitors before its charges would exceed this amount; the rest are listed as \"not checked\", go first next run, and unpaid new ads are kept for the next run. A competitor that alone does not fit in what is left (a very large inventory) is listed as \"not checked\" and the run goes on with the others. A competitor that was read but could not be paid for goes after the others next run (and is not read again for up to 7 days while what is left stays below its last read; that estimate is at most what the current read limit allows and is dropped when its countries change). The run reports the amount that would cover every competitor. The lower of this and the platform's \"Maximum cost per run\" applies. 0 checks nothing.",
            "default": 1
          },
          "minHoursBetweenChecks": {
            "title": "Minimum hours between checks of a competitor",
            "minimum": 0,
            "maximum": 168,
            "type": "integer",
            "description": "A competitor whose last check was in a run that started less than about this many hours ago (10% slack for schedule jitter) is skipped (not read, not billed). Keeps a too-frequent schedule from re-reading the Transparency Center. 0 turns it off.",
            "default": 1
          },
          "maxRunMinutes": {
            "title": "Maximum run time (minutes)",
            "minimum": 1,
            "maximum": 120,
            "type": "integer",
            "description": "No new competitor is started after this many minutes (at most 120; also 2 minutes before the platform timeout); the rest are listed as \"not checked\" and go first next run. The digest is still sent.",
            "default": 30
          },
          "maxAdsReadPerAdvertiser": {
            "title": "Max ads read per competitor",
            "minimum": 40,
            "maximum": 5000,
            "type": "integer",
            "description": "Safety limit on how deep one competitor's ad list is read per run. Reads beyond 200 ads (the ads read in each country, added up over its countries) are billed per 40 (google-large-inventory-page). If the limit is reached before every country of a competitor was read, the check is billed for what was read and the unread countries are read first next run.",
            "default": 1000
          },
          "overlapHours": {
            "title": "Overlap with the previous check (hours)",
            "minimum": 1,
            "maximum": 168,
            "type": "integer",
            "description": "Re-reads this much before the previous check so ads that appear late in the Transparency Center are not missed. Already-seen ads are not reported or billed twice. A longer overlap reads a little deeper each run (for very large advertisers this can add large-inventory pages).",
            "default": 48
          },
          "proxyConfiguration": {
            "title": "Proxy",
            "type": "object",
            "description": "Residential Apify Proxy (the default) is required in practice: Google Ads Transparency Center answers datacenter proxy IPs with HTTP 429 on every request, so Apify Proxy without a group is switched to Residential. Requests are sent one at a time; after a rate limit, server or network error the client slows down and retries a request at most twice. A country that still fails ends that competitor's check (its other countries are not read; nothing is billed, it is retried next run), and 3 such competitors in a row stop the run. A competitor that failed goes after the others next run. A competitor is read at most once per minHoursBetweenChecks.",
            "default": {
              "useApifyProxy": true,
              "apifyProxyGroups": [
                "RESIDENTIAL"
              ]
            }
          },
          "spreadsheetSafeText": {
            "title": "Spreadsheet-safe text",
            "type": "boolean",
            "description": "Turn on if you open the dataset in Excel, Google Sheets or another spreadsheet (CSV/XLSX export). Text that starts with =, +, -, @ (or a tab or line break) gets a leading apostrophe, so the spreadsheet shows it as text instead of running it as a formula. Advertiser names are written by the advertiser. Off by default because it changes those values for JSON consumers.",
            "default": false
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}