{
  "openapi": "3.0.1",
  "info": {
    "title": "📣 SERP Share of Voice - Keyword Visibility Tracker",
    "description": "📣 Track your and competitors' position-weighted share of search visibility across a keyword set, over time. Works from SERP results you supply — the reliable path, since Google returns a JS shell to server-side requests. Optional Bing/DuckDuckGo live fetch.",
    "version": "1.0",
    "x-build-id": "gsQInFQ9hH4J6emOJ"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/that_red_bird~serp-share-of-voice/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-that_red_bird-serp-share-of-voice",
        "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/that_red_bird~serp-share-of-voice/runs": {
      "post": {
        "operationId": "runs-sync-that_red_bird-serp-share-of-voice",
        "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/that_red_bird~serp-share-of-voice/run-sync": {
      "post": {
        "operationId": "run-sync-that_red_bird-serp-share-of-voice",
        "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",
        "properties": {
          "results": {
            "title": "SERP results (supplied by you)",
            "type": "array",
            "description": "The primary input path. An array of {\"keyword\": \"...\", \"rank\": 1, \"url\": \"https://...\", \"title\": \"...\"} rows from ANY SERP source or actor you already use. Google is deliberately not scraped here (it returns a JS shell to server-side requests) — bring your own results and this actor does the analytics.",
            "default": [
              {
                "keyword": "best running shoes",
                "rank": 1,
                "url": "https://nike.com/running-shoes",
                "title": "Best Running Shoes | Nike"
              },
              {
                "keyword": "best running shoes",
                "rank": 2,
                "url": "https://adidas.com/running",
                "title": "Running Shoes | adidas"
              },
              {
                "keyword": "best running shoes",
                "rank": 3,
                "url": "https://runnersworld.com/gear/best-running-shoes",
                "title": "The Best Running Shoes, Tested - Runner's World"
              },
              {
                "keyword": "marathon training plan",
                "rank": 1,
                "url": "https://adidas.com/training/marathon",
                "title": "Marathon Training Plan | adidas"
              },
              {
                "keyword": "marathon training plan",
                "rank": 2,
                "url": "https://runnersworld.com/training/marathon-plan",
                "title": "Marathon Training Plan - Runner's World"
              },
              {
                "keyword": "marathon training plan",
                "rank": 3,
                "url": "https://nike.com/marathon",
                "title": "Marathon Guide | Nike"
              }
            ]
          },
          "brandMap": {
            "title": "Brand -> domains map",
            "type": "object",
            "description": "Maps each result URL to a brand. Format: {\"Your Brand\": [\"yourdomain.com\"], \"Competitor A\": [\"competitor-a.com\", \"blog.competitor-a.com\"]}. Subdomains are matched to the domain you list (e.g. shop.yourdomain.com matches yourdomain.com automatically) — you only need to list a subdomain explicitly if it belongs to a DIFFERENT brand than its parent domain.",
            "default": {
              "Nike": [
                "nike.com"
              ],
              "adidas": [
                "adidas.com"
              ],
              "Runner's World": [
                "runnersworld.com"
              ]
            }
          },
          "yourBrand": {
            "title": "Your brand name",
            "type": "string",
            "description": "The exact brand name (a key in brandMap) that head-to-head gaps and the summary's \"yourSoV\" are computed against. Leave blank to skip head-to-head rows.",
            "default": "Nike"
          },
          "ctrCurve": {
            "title": "Position-weight curve override (basis points)",
            "type": "array",
            "description": "Optional override for the default position-weighting (CTR-style decay) curve. Array of integers in basis points (10000 = 100%), index 0 = rank 1. Apify input schemas have no float type, so this is expressed in basis points rather than a 0-1 fraction. Leave empty to use the documented default curve (rank 1 worth roughly 10-15x rank 10).",
            "default": []
          },
          "keywordVolumes": {
            "title": "Keyword search volumes (optional)",
            "type": "object",
            "description": "Optional. {\"keyword\": 1000, ...} monthly search volume per keyword. When supplied, each result's position weight is multiplied by its keyword's volume, so visibility on a high-volume term counts for more than the same rank on a rarely-searched one. Omit to weight every keyword equally.",
            "default": {}
          },
          "fetchMode": {
            "title": "Live fetch mode",
            "enum": [
              "supplied",
              "bing",
              "duckduckgo",
              "bing+duckduckgo"
            ],
            "type": "string",
            "description": "Secondary, optional path. \"supplied\" (default) uses only the results you pass in above — the reliable option. \"bing\"/\"duckduckgo\"/\"bing+duckduckgo\" additionally fetch live results server-side for the keywords you list below. Google is NOT an option: it serves a JavaScript shell (no organic results, no AI Overview) to server-side requests and cannot be scraped this way.",
            "default": "supplied"
          },
          "keywords": {
            "title": "Keywords to fetch live",
            "type": "array",
            "description": "Only used when fetchMode is not \"supplied\". The keyword list to fetch live results for from Bing and/or DuckDuckGo.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "maxResultsPerKeyword": {
            "title": "Max results per keyword (live fetch)",
            "minimum": 1,
            "maximum": 50,
            "type": "integer",
            "description": "Upper bound on organic results parsed per keyword when fetchMode is not \"supplied\".",
            "default": 10
          },
          "country": {
            "title": "Country / market code (live fetch, optional)",
            "type": "string",
            "description": "Optional market hint passed to Bing (e.g. \"US\", \"GB\") when fetchMode includes bing. Ignored otherwise.",
            "default": ""
          },
          "concurrency": {
            "title": "Concurrency (live fetch)",
            "minimum": 1,
            "maximum": 10,
            "type": "integer",
            "description": "How many keyword/source fetches to run in parallel when fetchMode is not \"supplied\".",
            "default": 3
          },
          "timeoutMs": {
            "title": "Fetch timeout (ms)",
            "minimum": 1000,
            "maximum": 60000,
            "type": "integer",
            "description": "Per-request timeout in milliseconds for live Bing/DuckDuckGo fetches.",
            "default": 15000
          },
          "onlyChanges": {
            "title": "Only output brands that moved",
            "type": "boolean",
            "description": "When true (and this isn't the first run), brand rows whose share-of-voice trend is \"flat\" are omitted from the dataset. Keyword, head-to-head and mover rows are always included.",
            "default": false
          },
          "maxItems": {
            "title": "Max dataset rows",
            "minimum": 0,
            "type": "integer",
            "description": "Hard cap on rows pushed to the dataset this run. 0 = no cap.",
            "default": 0
          },
          "resetBaseline": {
            "title": "Reset baseline",
            "type": "boolean",
            "description": "Ignore any previously stored share-of-voice state and treat this run as a fresh first run (no movement, no dropout/new-entrant detection).",
            "default": false
          },
          "keepHistory": {
            "title": "History length per brand",
            "minimum": 1,
            "maximum": 500,
            "type": "integer",
            "description": "How many past runs to keep in each brand's rolling share-of-voice history, so you can chart it over time without your own database.",
            "default": 60
          },
          "moversLimit": {
            "title": "Biggest movers to report",
            "minimum": 1,
            "maximum": 100,
            "type": "integer",
            "description": "How many keyword-level biggest-gain and biggest-loss rows to emit per run.",
            "default": 10
          },
          "stateStoreName": {
            "title": "State store name",
            "type": "string",
            "description": "Name of the named key-value store used to persist share-of-voice history between runs. Use a different name to run independent, non-interfering trackers (e.g. per client or per keyword set).",
            "default": "serp-share-of-voice-state"
          },
          "proxyConfiguration": {
            "title": "Proxy",
            "type": "object",
            "description": "Optional Apify proxy configuration, used only for live Bing/DuckDuckGo fetches when fetchMode is not \"supplied\"."
          }
        }
      },
      "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
                  }
                }
              },
              "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}