# US Government AI Policy Tracker (`morehavoc/us-gov-ai-policy-tracker`) Actor

State and local government AI-use policies, each read from the official source and coded on ~30 fields (generative-AI stance, approved tools, disclosure, human review, data limits, dates). Free sample, plus a monitor that flags edited, moved or removed policies.

- **URL**: https://apify.com/morehavoc/us-gov-ai-policy-tracker.md
- **Developed by:** [Christopher Moravec](https://apify.com/morehavoc) (community)
- **Categories:** News, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## US Government AI Policy Tracker

A curated dataset of US state and local government policies on the use of artificial intelligence: staff generative-AI
guidelines, administrative policies and directives, executive orders, standards and procurement terms. Each policy is
coded on about 30 fields, so you can filter and compare them instead of reading hundreds of PDFs. A monitor mode
re-checks every official source and tells you which policies were edited, moved or taken down.

**Coverage:** 56 policies (October 2026): 20 states, 1 state agency, 16 cities, 10 counties, 4 consolidated city-counties, 4 school districts and 1 regional council. Growing toward every state plus the largest cities, counties and school districts. 52 can be change-monitored automatically (6 of them scanned PDFs, compared file to file); the other 4 (New York State ITS and OGS, Salt Lake City, Haines Borough) refuse automated access from cloud servers and are re-checked by hand. They are skipped in monitor mode and never charged.

### Who it is for

- **Govtech vendors**: find which cities, counties and states allow generative AI, which require approved tools, and
  which name a product (Copilot, ChatGPT Enterprise, Gemini) before you pitch.
- **Consultants and policy advisors**: benchmark a client's draft against what peer jurisdictions adopted.
- **Journalists**: track who has rules, who bans what, and when a policy quietly changes.
- **Researchers**: a coded, citable table with one link per row to the official document.

### How the coding is done

Each policy is read and coded from the official government source. Summaries are original. Every record links to the
official document and carries a SHA-256 hash of its normalised text, so you can confirm the document you are reading is
the one that was coded, and the monitor can tell when it changes. A field the coding could not determine is `null`; `coding_confidence`
says how sure the coding is.

### Fields

Identity and jurisdiction

- `policy_id`: stable identifier.
- `jurisdiction_name`, `state` (USPS two-letter code), `issuing_body` (e.g. "Office of the Governor", "City IT").
- `level`: `state` | `state_agency` | `county` | `city` | `town_township` | `consolidated_city_county` | `school_district` | `special_district` | `regional_council`.

Document

- `title`, `summary` (original, plain English).
- `doc_type`: `policy` | `guideline` | `executive_order` | `administrative_directive` | `standard` | `procurement_terms` | `model_policy_template` | `ban_moratorium` | `strategy_plan` | `inventory_ordinance`.
- `scope_ai`: `generative_only` | `all_ai` | `includes_agentic`.
- `status`: `interim` | `final` | `draft` | `superseded` | `rescinded`.
- Dates (YYYY-MM-DD): `adopted_date`, `effective_date`, `last_updated`, `sunset_or_review_date`.
- `derived_from`: `govai_template` | `boston` | `san_jose` | `nist_ai_rmf` | `state_policy` | `other` | `unknown` (the model the policy was visibly based on).

Generative-AI rules

- `genai_stance`: `allowed` | `allowed_with_approval` | `approved_tools_only` | `restricted` | `banned` | `not_addressed`.
- `approved_tools_named`, `prohibited_tools_named`: product names as the policy states them (arrays).
- `training_required`: whether staff must complete training before using AI tools.
- `disclosure_required`: `always` | `substantial_use` | `public_facing_only` | `no` | `not_addressed`.
- `human_review_required`: `all_outputs` | `consequential_decisions` | `public_communications` | `not_addressed`.
- `data_restrictions` (array): `pii` | `confidential` | `regulated` (HIPAA, CJIS, FERPA and the like) | `student_data` | `copyrighted_input`.
- `public_records_addressed`: `explicit_records_act_reference` | `retention_required` | `not_addressed`.

Governance and procurement

- `risk_assessment_required`, `ai_inventory_required`, `incident_reporting`: true / false / null.
- `procurement_rules`: what the policy says about buying AI products (free text, or null).
- `governance_body`: the committee, officer or office responsible.

Provenance

- `source_url`: the official document. `format`: `html` | `pdf` | `docx` | `scan`.
- `fetched_at`, `content_sha256` (SHA-256 of the normalised text), `text_chars`.
- `hash_basis`: what the monitor compares. `text` (the normalised text) for most records; `bytes` for scanned PDFs with no
  text layer, whose `content_bytes_sha256` is the SHA-256 of the file as served (their `content_sha256` is of the coded
  OCR text). `monitorable`: false when the official source cannot be read automatically. `render_js`: true for a page
  whose text is written by scripts, so the monitor compares the page as rendered in a browser.
- `coding_confidence`, `notes`.

### Modes

#### Dataset (default)

Returns coded policy records, sorted by jurisdiction. Filters combine (all must match):

| Input | Meaning |
|---|---|
| `states` | USPS codes, e.g. `["WA", "CA"]`. Empty = all. |
| `levels` | Government levels from the list above. Empty = all. |
| `genaiStance` | Generative-AI stances from the list above. Empty = all. |
| `search` | Case-insensitive text in the jurisdiction name, title or summary. |
| `updatedSince` | YYYY-MM-DD. Keeps policies whose latest date (last updated, effective or adopted) is on or after it. Policies with no date are left out. |
| `maxItems` | Stop after this many records. |

Example: every county and city policy in Washington and Oregon that limits staff to approved tools.

```json
{ "mode": "dataset", "states": ["WA", "OR"], "levels": ["county", "city"], "genaiStance": ["approved_tools_only"] }
```

#### Free sample

Set `freeSample: true` to get the first 10 policies sorted by jurisdiction, **free of charge**, with every field, so you
can judge the coding before you buy. Filters and `maxItems` are ignored in the sample, and each row carries
`sample: true`.

```json
{ "mode": "dataset", "freeSample": true }
```

#### Monitor

Re-fetches the official source of each selected policy (the same filters apply), extracts its text (HTML page body or
PDF), normalises whitespace, hashes it, and compares it with the hash in the dataset. When a source answers a plain request
with a bot wall (403, 429, or a challenge page), it is loaded once more in a real headless Chromium, which waits for the
challenge to clear; nothing is logged into and no CAPTCHA is solved. Scanned PDFs have no text, so they are compared by
the hash of the file itself. One row per policy:

- `change_type`:
  - `unchanged`: same text.
  - `text_changed`: the text differs. `chars_added`, `chars_removed` and `changed_excerpt` (the first ~500 characters
    of the new or edited sentences) summarise what changed.
  - `moved`: the address now redirects to a different page (`final_url`). `text_changed` says whether the text also
    changed. A redirect from http to https, or to or from `www.`, is not counted as a move.
  - `file_changed`: a scanned PDF (`hash_basis: "bytes"`) is now a different file. There is no text to diff, so
    nothing says what changed; open the document.
  - `removed`: the source answers 404 or 410.
  - `unreachable`: any other failure (403 even in a browser, 5xx, timeout, DNS, unreadable file), with `http_status`
    and `error`. A 403 is usually a bot wall, not a deleted page, so it is never reported as `removed`.
- Also: `policy_id`, `jurisdiction_name`, `state`, `title`, `source_url`, `checked_at`, `final_url`, `http_status`,
  `hash_basis` (`text` or `bytes`), `fetched_via` (`fetch` or `browser`), `old_sha256`, `new_sha256` (file hashes when
  the basis is `bytes`).

`onlyChanges` (default `true`) leaves `unchanged` rows out of the output. Requests are polite: one at a time, 1.5 s
apart, a 30-second timeout, one retry on a 429 or 5xx, at most one browser attempt per policy. The comparison is against the dataset version this Actor
ships, so a change stays reported until the dataset is refreshed with the new text.

```json
{ "mode": "monitor", "states": ["CA"], "onlyChanges": true }
```

Schedule the monitor weekly to get a change feed for the jurisdictions you follow.

### Output views

- **Policies** (`overview`): jurisdiction, state, level, title, document type, generative-AI stance, disclosure, human
  review, adopted date, official source.
- **Changes** (`changes`): jurisdiction, title, change type, compared by (text or file), HTTP status, final URL, checked at.

### Pricing

Pay per event:

- **Policy record**: $0.10 per policy returned in dataset mode.
- **Policy change check**: $0.02 per policy checked in monitor mode (charged for every policy checked, including
  unchanged ones you chose not to output).
- The free sample costs nothing.

Set a maximum charge on the run to cap spend; the Actor stops when it is reached.

# Actor input Schema

## `mode` (type: `string`):

Dataset: return coded policy records ($0.10 each). Monitor: re-fetch each selected policy's official source and report whether it changed ($0.02 per policy checked).

## `freeSample` (type: `boolean`):

Dataset mode only. Return the first 10 policies sorted by jurisdiction, free of charge, so you can see the fields before buying. Filters and Max items are ignored; rows carry sample: true.

## `states` (type: `array`):

Two-letter USPS codes (e.g. WA, CA, NY). Empty = all states.

## `levels` (type: `array`):

Empty = all levels.

## `genaiStance` (type: `array`):

Keep only policies with these stances on generative AI. Empty = all.

## `search` (type: `string`):

Case-insensitive text match on jurisdiction name, title or summary.

## `updatedSince` (type: `string`):

Keep policies whose most recent date (last updated, effective or adopted) is on or after this date.

## `maxItems` (type: `integer`):

Stop after this many policies (each returned or checked policy is one charge).

## `onlyChanges` (type: `boolean`):

Monitor mode only. Leave out policies whose text is unchanged. Every policy checked is still charged.

## Actor input object example

```json
{
  "mode": "dataset",
  "freeSample": false,
  "maxItems": 10000,
  "onlyChanges": true
}
```

# Actor output Schema

## `policies` (type: `string`):

One row per policy: jurisdiction, document type, generative-AI stance, disclosure and human-review rules, adoption date and the official source link. In monitor mode the same dataset holds one row per checked policy (use the Changes view).

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "dataset"
};

// Run the Actor and wait for it to finish
const run = await client.actor("morehavoc/us-gov-ai-policy-tracker").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "mode": "dataset" }

# Run the Actor and wait for it to finish
run = client.actor("morehavoc/us-gov-ai-policy-tracker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "dataset"
}' |
apify call morehavoc/us-gov-ai-policy-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,morehavoc/us-gov-ai-policy-tracker"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/DtbmVuh3bwbn7Uc8M/builds/6DPFn4bfYBDeuWD9a/openapi.json
