{
  "openapi": "3.0.1",
  "info": {
    "title": "City Council & Public Meeting Transcripts",
    "description": "Timestamped city council and county board transcripts tied to agenda items and official votes. Granicus, Swagit, Legistar and YouTube; type a city or paste URLs. $0.008 per audio minute; list meetings first for $0.002 each. Keyword alerts and new-meeting monitoring.",
    "version": "1.0",
    "x-build-id": "c1Q46softMkYpEef5"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/catalyst-labs~city-council-meeting-transcripts/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-catalyst-labs-city-council-meeting-transcripts",
        "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/catalyst-labs~city-council-meeting-transcripts/runs": {
      "post": {
        "operationId": "runs-sync-catalyst-labs-city-council-meeting-transcripts",
        "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/catalyst-labs~city-council-meeting-transcripts/run-sync": {
      "post": {
        "operationId": "run-sync-catalyst-labs-city-council-meeting-transcripts",
        "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": {
          "city": {
            "title": "City or county",
            "type": "string",
            "description": "City, county or other public body, e.g. \"King County, WA\", \"San Jose\" or \"Polk County, IA\". Built-in registry first (with state, county, FIPS and OCD IDs), then Legistar lookup. Leave empty if you paste URLs in \"Meeting URLs\". For several places in one run use \"Cities\" below (this field still works on its own)."
          },
          "cities": {
            "title": "Cities (several places in one run)",
            "type": "array",
            "description": "List of cities, counties or bodies, e.g. [\"Des Moines, IA\", \"Polk County, IA\", \"King County, WA\"]. Each is resolved like \"City or county\"; results are tagged with their input and summarized per place in the OUTPUT record (byInput). Can be combined with \"City or county\" and \"Meeting URLs\".",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "title": "Meeting URLs",
            "type": "array",
            "description": "Any mix of: Granicus list pages (https://CITY.granicus.com/ViewPublisher.php?view_id=N) or meeting pages (MediaPlayer.php?clip_id=N); Swagit list pages (https://CITY.new.swagit.com/views/N) or videos (/videos/N); Legistar calendars (https://CITY.legistar.com or legistar:CLIENT); YouTube channels, playlists or videos; direct .mp4/.m3u8 recordings; or a meeting web page that embeds one. Unsupported sites fail with a clear message and are never charged.",
            "items": {
              "type": "string"
            }
          },
          "mode": {
            "title": "Mode",
            "enum": [
              "transcribe",
              "listOnly"
            ],
            "type": "string",
            "description": "listOnly returns meeting metadata (title, date, length, video and agenda links) without transcribing. Use it to discover meetings cheaply, then transcribe the ones you want.",
            "default": "transcribe"
          },
          "daysBack": {
            "title": "Days back",
            "minimum": 1,
            "type": "integer",
            "description": "Look for meetings from the last N days (ignored when dateFrom is set).",
            "default": 30
          },
          "dateFrom": {
            "title": "From date",
            "type": "string",
            "description": "Earliest meeting date (YYYY-MM-DD). Overrides daysBack."
          },
          "dateTo": {
            "title": "To date",
            "type": "string",
            "description": "Latest meeting date (YYYY-MM-DD). Default: today."
          },
          "maxMeetingsPerSource": {
            "title": "Max meetings per source",
            "minimum": 1,
            "type": "integer",
            "description": "Newest N meetings taken from each list page, channel, Legistar calendar or city.",
            "default": 1
          },
          "bodyFilter": {
            "title": "Body filter",
            "type": "string",
            "description": "Only meetings whose body or title contains this text, e.g. \"City Council\" or \"Planning Commission\"."
          },
          "titleFilter": {
            "title": "Title filter",
            "type": "string",
            "description": "Only meetings whose title contains this text (case-insensitive)."
          },
          "alignAgenda": {
            "title": "Agenda-item timestamps (+$0.05 per meeting)",
            "type": "boolean",
            "description": "Adds agenda items with start/end times and video links. Uses the platform's own index when it has one (Swagit, some Granicus), otherwise aligns the Legistar agenda or agenda document to the transcript. Charged only when at least one item is located; no agenda found = no charge.",
            "default": false
          },
          "generateDigest": {
            "title": "News digest (+$0.05 per meeting)",
            "type": "boolean",
            "description": "Adds a neutral plain-language digest: headline, the top decisions (see Digest points, default 5) with 'watch the moment' links, upcoming items, Markdown and ~280-character social versions. Vote results come only from official records (minutes or Legistar). When the official votes are available you get the verified record digest ($0.05, charged once per meeting). Before the minutes are posted you get a free PRELIMINARY digest from the agenda (see 'Digest when minutes are not posted yet'). Names of private individuals are left out by default.",
            "default": false
          },
          "digestMode": {
            "title": "Digest method",
            "enum": [
              "auto",
              "record",
              "llm"
            ],
            "type": "string",
            "description": "Record-first digests are built only from the official minutes/Legistar (no AI text), ranked by fixed rules, page-cited and identical on every run. They are always \"verified\".",
            "default": "auto"
          },
          "digestPoints": {
            "title": "Digest points",
            "minimum": 1,
            "maximum": 10,
            "type": "integer",
            "description": "Exactly this many top decisions (fewer only if the meeting has fewer eligible official votes).",
            "default": 5
          },
          "includeRecordNames": {
            "title": "Include names of private individuals as printed in the record",
            "type": "boolean",
            "description": "Off (default): digest headlines and text omit names of private individuals (public commenters, applicants, company officers); officials, staff, companies and places stay. On: names are kept exactly as printed in the official record and digest.names.namedPeople lists each person with role (speaker / applicant / applicant_representative / official / named_in_record) and source page. The raw transcript is never changed either way.",
            "default": false
          },
          "preliminaryDigest": {
            "title": "Digest when minutes are not posted yet",
            "enum": [
              "agenda",
              "ai",
              "off"
            ],
            "type": "string",
            "description": "Used only when the meeting has no official vote record yet. Every such digest is labeled PRELIMINARY and the meeting is put on the re-check list, so a later run with recheckPending upgrades it to the verified record digest. At most one digest is ever charged per meeting.",
            "default": "agenda"
          },
          "keywords": {
            "title": "Keywords to find",
            "type": "array",
            "description": "Words or phrases to find in each transcript, e.g. [\"rezoning\", \"data center\", \"Main Street\"]. Each hit includes the timestamp, surrounding text, agenda item and a link to that moment. Free.",
            "items": {
              "type": "string"
            }
          },
          "onlyNewMeetings": {
            "title": "Only new meetings (monitoring)",
            "type": "boolean",
            "description": "Skip meetings this tool already delivered in earlier runs with the same sources (or monitorId). Schedule the run daily or weekly to get just new meetings.",
            "default": false
          },
          "monitorId": {
            "title": "Monitor ID",
            "type": "string",
            "description": "Name for the onlyNewMeetings memory, so several schedules can track separately. Optional."
          },
          "recheckPending": {
            "title": "Re-check pending meetings",
            "type": "boolean",
            "description": "Re-run meetings that got a PRELIMINARY digest (minutes not posted) or a retry_later result (captions not posted) in earlier runs with the same monitorId. The transcript is reused, so audio minutes are never charged twice; a meeting whose minutes are now posted gets the verified record digest (free if its preliminary digest was already charged). Schedule this daily. Works with or without new sources.",
            "default": false
          },
          "recheckDatasetId": {
            "title": "Re-check meetings from a dataset",
            "type": "string",
            "description": "Optional: ID of an earlier run's dataset. Items with a recheck block are re-checked (same rules as recheckPending)."
          },
          "recheckIntervalHours": {
            "title": "Hours between re-checks",
            "minimum": 1,
            "maximum": 720,
            "type": "integer",
            "description": "A pending meeting is re-checked at most this often (its retryAfter).",
            "default": 24
          },
          "pendingMaxDays": {
            "title": "Stop re-checking after (days)",
            "minimum": 1,
            "maximum": 365,
            "type": "integer",
            "description": "Pending meetings whose minutes or recording never appear are dropped after this many days (reported as recheck_expired, not charged).",
            "default": 45
          },
          "maxAudioMinutes": {
            "title": "Max audio minutes per run",
            "minimum": 0,
            "type": "integer",
            "description": "Spending cap: the run stops transcribing once this many audio minutes are used. 240 min = $1.92.",
            "default": 240
          },
          "maxMinutesPerMeeting": {
            "title": "Max minutes per meeting",
            "minimum": 1,
            "type": "integer",
            "description": "Meetings longer than this fail uncharged, unless truncateLongMeetings is on.",
            "default": 480
          },
          "truncateLongMeetings": {
            "title": "Truncate long meetings",
            "type": "boolean",
            "description": "Transcribe only the first maxMinutesPerMeeting minutes of longer meetings instead of skipping them.",
            "default": false
          },
          "outputFormats": {
            "title": "Output formats",
            "type": "array",
            "description": "Formats included in each record.",
            "items": {
              "type": "string",
              "enum": [
                "text",
                "segments",
                "markdown",
                "srt",
                "vtt",
                "llmText"
              ],
              "enumTitles": [
                "Plain text",
                "Timestamped segments (JSON)",
                "Markdown with timestamp links",
                "SRT subtitles",
                "WebVTT subtitles",
                "LLM-ready text"
              ]
            },
            "default": [
              "text",
              "segments",
              "markdown"
            ]
          },
          "language": {
            "title": "Language",
            "type": "string",
            "description": "Spoken language code (en, es...). Empty = auto-detect."
          },
          "vocabulary": {
            "title": "Vocabulary hints",
            "type": "array",
            "description": "Names and terms to spell correctly, e.g. council members' names or street names.",
            "items": {
              "type": "string"
            }
          },
          "granicusHost": {
            "title": "Granicus host (Legistar cities)",
            "type": "string",
            "description": "Only needed when a Legistar city's Granicus site isn't CLIENT.granicus.com, e.g. \"cityname.granicus.com\"."
          },
          "youtubeCaptionsFallback": {
            "title": "Use YouTube captions",
            "type": "boolean",
            "description": "For YouTube meetings, use the video's own captions (human-made first, auto-generated after the city's official recording). Marked transcriptSource youtubeCaptions or youtubeAutoCaptions; same per-minute price.",
            "default": true
          },
          "youtubeResidentialFallback": {
            "title": "Last resort: YouTube audio via residential proxy",
            "type": "boolean",
            "description": "Off by default. When captions and the city's official recording are both unavailable, try downloading the YouTube audio through Apify's US residential proxy. Works only some of the time and is slower. Failed attempts are never charged.",
            "default": false
          },
          "maxConcurrency": {
            "title": "Parallel meetings",
            "minimum": 1,
            "maximum": 6,
            "type": "integer",
            "description": "Meetings transcribed at the same time.",
            "default": 3
          },
          "jurisdiction": {
            "title": "Jurisdiction for direct URLs (optional)",
            "type": "string",
            "description": "Registry id or name (e.g. 'us-ia-des-moines' or 'Des Moines, IA') used to tag meetings from pasted video URLs so the official agenda and minutes can be found. It does not add that jurisdiction's own sources."
          },
          "captionWaitMinutes": {
            "title": "Wait for late captions (minutes)",
            "minimum": 0,
            "maximum": 120,
            "type": "integer",
            "description": "If a YouTube meeting has no captions yet and no official recording is posted, wait up to this many minutes inside the run and retry (nothing is charged while waiting; you pay only Apify compute time). 0 = return a free \"retry_later\" result with a suggested retryAfter time immediately.",
            "default": 0
          },
          "captionRetryIntervalMinutes": {
            "title": "Caption retry interval (minutes)",
            "minimum": 5,
            "maximum": 60,
            "type": "integer",
            "description": "How often to re-check during the caption wait window.",
            "default": 15
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}