Spaces:
Running on CPU Upgrade
Vendor meeting portal search
Script: vendor_meeting_portal_search.py
Writes: data/cache/vendorsearch/_manifest.json and data/cache/vendorsearch/hits.jsonl
This tool runs DuckDuckGo-style metasearch (ddgs, same as the jurisdiction website enrichment script) to collect raw URL candidates on CivicPlus, Legistar, Granicus, PrimeGov, and Swagit. It does not download official vendor client lists; hits are for manual or downstream triangulation.
The three --mode options
These control which search queries run, not a different API.
1. global (default)
What it does: Runs a fixed set of nationwide queries—mostly site:civicplus.com, site:legistar.com, site:granicus.com, etc.—plus a few vendor-agnostic phrases.
When to use: Quick pass to surface many example portals and common URL shapes in one short run (on the order of 16 search requests if --vendor all).
Tradeoff: Strong for “big” sites that rank well everywhere; weaker for a small county that only shows up when the query mentions that state or region.
2. per-state
What it does: For each USPS code (default: all 50 states + DC), runs 10 tailored queries that embed the full state name, for example:
- “Texas county … site:legistar.com”
- “Texas city council … site:civicplus.com”
- “Texas state legislature … site:legistar.com”
- …and similar rows for Granicus, PrimeGov, Swagit, and state-level CivicPlus.
So the search engine gets geographic context, which often surfaces different hits than the global pass.
When to use: Building coverage by state, or when global mode missed jurisdictions you care about.
Tradeoff: Many more requests. With --vendor all and all states, that is about 51 × 10 ≈ 510 searches before deduplication—use --states TX,CA while developing, and a comfortable --sleep (seconds between queries) to stay polite.
3. both
What it does: Runs global first, then all per-state tasks (for the states you selected—default still all 51).
When to use: One batch job where you want maximum recall: broad vendor-wide hits plus state-scoped hits.
Tradeoff: Highest runtime and the most metasearch traffic; prefer --states or --vendor to narrow scope.
Other useful flags (short)
| Flag | Purpose |
|---|---|
--vendor |
Limit to one family, e.g. legistar, civicplus, primegov (Legistar and Granicus share one query set in the script). |
--states |
Comma-separated USPS codes; only affects per-state and both (which states get the 10-query loop). |
--max-results |
Cap hits returned per search query. |
--sleep |
Seconds to wait after each query (reduces rate-limit / blocking risk). |
--out-dir |
Alternate output directory instead of data/cache/vendorsearch/. |
Examples
# Understand the tool: one cheap run
.venv/bin/python scripts/datasources/vendorsearch/vendor_meeting_portal_search.py --mode global
# Per-state, only a few states
.venv/bin/python scripts/datasources/vendorsearch/vendor_meeting_portal_search.py --mode per-state --states VT,IA --sleep 2.5
# Only Legistar/Granicus family, global queries
.venv/bin/python scripts/datasources/vendorsearch/vendor_meeting_portal_search.py --mode global --vendor legistar
Reading the output
hits.jsonl: Each line is JSON.kindis usuallyhit(a search result); sometimesquery_errorif a query failed.vendor_inferred_from_urlis a hostname guess, not a verified product classification._manifest.json: Run metadata: mode, vendor filter,task_count, and row counts.