# eCFR: Search US Federal Regulations, Parts & Amendments (`yadroo/ecfr-regulations`) Actor

Search the Code of Federal Regulations by phrase, title, part or agency and get sections as rows: citation, headings, matched excerpt, effective window and change types. Further modes list the parts and sections inside a title, amendment dates per section, and the title and agency dictionaries.

- **URL**: https://apify.com/yadroo/ecfr-regulations.md
- **Developed by:** [Samat Makatov](https://apify.com/yadroo) (community)
- **Categories:** Business, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 regulation row returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## eCFR: Search US Federal Regulations, Parts & Amendments

Search the Code of Federal Regulations by phrase, title, part or agency and get sections as rows: citation, headings, matched excerpt, effective window and change types. Further modes list the parts and sections inside a title, amendment dates per section, and the title and agency dictionaries.

Every row comes from the government's own eCFR JSON API — no key, no proxy, no browser, no login. You get the
**citation** (`21 CFR 820.10`), the part and section **headings**, the **matched passage**, the dates a version
**started and ends**, whether it is **in force**, and the **link to the official page**; the amendment modes add the
**amendment and issue dates** of every recorded change. Built for compliance and quality teams, regulatory lawyers,
policy analysts and agents that need a citation with a link rather than a web page. It returns metadata, structure and
excerpts — **not the full text of a section**; see [Limits & FAQ](#limits--faq) for why.

### Use cases

- **Watch the parts your product is regulated under.** Run `search` with `lookbackDays: 7` and `onlyNew: true` on a
  schedule and the dataset holds only the sections that actually moved this week — with the date the new text took effect.
- **Prove when a rule last changed, for an audit.** `versions` on a title and part gives one row per recorded amendment
  with `amendmentDate`, `issueDate` and whether the change was substantive, so a finding gets a dated citation.
- **Build a table of contents for a RAG index.** `structure` on a title or part writes every subpart and section with
  its heading, the range it covers and `sizeBytes`, so you know how long each node is before you fetch anything.
- **Scope a topic before you read.** `counts` turns a phrase into a map of the code — how many matching sections sit in
  each title, chapter and part — for one request instead of thousands of paid rows.
- **Give an agent a citation tool.** One `run-sync-get-dataset-items` call answers "which section says this" with the
  citation, the heading and a link a human can open.
- **Keep a dated copy of the code for a contract.** `asOfDate` reads the code as it stood on a past day, and every row
  carries that day, so a quote in an agreement stays checkable later.

### Input

Nothing is required: with no input at all the actor searches the whole code with the prefilled phrase and writes 50 rows.

| Field | Type | Default | Allowed values / notes |
|---|---|---|---|
| `mode` | string | `search` | `search`, `structure`, `versions`, `counts`, `titles`, `agencies` — see [Modes](#modes) |
| `query` | string | — (prefill `medical device`) | Words to find in the regulation text; used by `search` and `counts`. Quote a phrase (`"design controls"`) to keep the words together. Empty in `search` returns whatever the other filters allow |
| `order` | string | `relevance` | `relevance`, `newest`, `oldest`, `hierarchy` — see [Sort orders](#sort-orders) |
| `asOfDate` | string | — | `YYYY-MM-DD`. Empty = the newest day the source has published (usually 1–3 days behind today). A future day is clamped down, not refused |
| `includeHistory` | boolean | `false` | `false` = one row per section, the version in force on `asOfDate`. `true` = also the superseded and not-yet-effective versions |
| `changedAfter` | string | — | `YYYY-MM-DD`. Keep only sections whose text was touched after this day |
| `changedBefore` | string | — | `YYYY-MM-DD`, the other end of the change window |
| `lookbackDays` | integer | — | 1–3650. Relative change window counted in UTC from the day the run starts. Ignored when `changedAfter` is set |
| `cfrTitle` | integer | — (prefill `21`) | 1–50, see [CFR titles](#cfr-titles). Optional in `search` and `counts`, **required** by `structure` and `versions`, ignored by `titles` and `agencies` |
| `cfrChapter` | string | — | Roman numeral as the code prints it (`I`, `III`, `XII`). Needs `cfrTitle` |
| `cfrPart` | string | — | Part number, e.g. `820` in title 21 or `141` in title 40. Needs `cfrTitle` |
| `cfrSubpart` | string | — | Subpart letter (`A`, `C`, `UUUUU`). Needs `cfrPart` |
| `cfrSection` | string | — | A single section written as the citation does: `820.30`, `141.23`. The part is taken from it when `cfrPart` is empty |
| `agencySlugs` | string\[] | — | Agency slugs, combined with OR — see [Agency slugs](#agency-slugs). A near miss is corrected, an unknown slug is refused |
| `nodeTypes` | string\[] | `["part","section"]` | `structure` only: which levels become rows — `chapter`, `subchapter`, `part`, `subpart`, `subjectGroup`, `section`, `appendix`, `title` |
| `substantiveOnly` | boolean | `false` | `versions` only: keep only amendments the source marks substantive |
| `includeRemoved` | boolean | `false` | `versions` only: also write sections an amendment deleted, with `removed: true` |
| `onlyNew` | boolean | `false` | Remember every row key in this actor's key-value store and write only keys no earlier run delivered |
| `maxItems` | integer | `50` | 1–5000. The search index serves at most 10 000 hits per query, however deep you page |
| `fields` | string\[] | all | Keep only these fields, in this order, e.g. `["citation","sectionName","startsOn","url"]` |

### Reference

#### Modes

| Mode | Question it answers | Needs | Endpoint behind it |
|---|---|---|---|
| `search` | Which sections of the code talk about this? | nothing | the search index over the codified text |
| `structure` | What is inside this title or part? | `cfrTitle` | the title's tree for a given day |
| `versions` | When was this last amended, and how often? | `cfrTitle` | the recorded amendment log |
| `counts` | Where in the code is this topic regulated? | nothing | hit counts per title, chapter and part |
| `titles` | Which titles exist and how current are they? | nothing | the title dictionary |
| `agencies` | Which agency owns which chapter, and what is its slug? | nothing | the agency dictionary |

`structure` and `versions` read one title, so they are the two modes that need `cfrTitle`. `titles` and `agencies` need
no input at all — run them once, keep the values, and use them in the filters of the other modes.

#### Sort orders

| Value | Effect |
|---|---|
| `relevance` | Best match for the phrase first (`search`) |
| `newest` | Newest version first in `search`, newest amendment first in `versions` — the setting for a change feed |
| `oldest` | Oldest first, for reading a section's history forward |
| `hierarchy` | The order the code prints, by title, part and section — the setting for exporting a part |

`structure` always follows the code's own order, `titles` its numbering and `agencies` its alphabet.

#### CFR titles

The 50 titles are the top level of the code. The ones people ask for most:

| Title | Name | Typical use |
|---|---|---|
| 7 | Agriculture | food and farm programs |
| 12 | Banks and Banking | bank and credit-union rules |
| 14 | Aeronautics and Space | aviation |
| 17 | Commodity and Securities Exchanges | securities and derivatives |
| 21 | Food and Drugs | food, drugs, medical devices |
| 26 | Internal Revenue | tax regulations |
| 29 | Labor | employment and workplace safety |
| 40 | Protection of Environment | air, water, waste, chemicals |
| 45 | Public Welfare | health privacy, research ethics |
| 47 | Telecommunication | spectrum and telecom |
| 48 | Federal Acquisition Regulations System | government contracting |
| 49 | Transportation | rail, road, pipeline, hazmat |

Run `{ "mode": "titles" }` for the full list with names and the day each title is current through — that is also the
honest answer to "how fresh is the text I am quoting".

#### Agency slugs

`agencySlugs` filters `search` by the chapters an agency owns, which is broader than one title and narrower than the
whole code. A few common ones:

| Slug | Agency | Chapters it owns include |
|---|---|---|
| `environmental-protection-agency` | EPA | 40 CFR I, IV, VII |
| `food-and-drug-administration` | FDA | 21 CFR I |
| `securities-and-exchange-commission` | SEC | 17 CFR II |
| `occupational-safety-and-health-administration` | OSHA | 29 CFR XVII |
| `federal-communications-commission` | FCC | 47 CFR I |
| `internal-revenue-service` | IRS | 26 CFR I |
| `federal-aviation-administration` | FAA | 14 CFR I |
| `agriculture-department` | USDA | 7 CFR XVI, XX, XXI |

Run `{ "mode": "agencies" }` for all of them with the chapters each one writes and the sub-agencies below it. A slug
with a typo is corrected to the closest one and the correction is logged; a slug that resembles nothing is refused
with a message rather than answered with an empty run.

#### How citations and links are built

`citation` is assembled from the hierarchy of the node: `21 CFR 820.10` for a section, `40 CFR part 141` for a part,
`21 CFR part 820 subpart A` for a subpart, `40 CFR part 58, Appendix E to Part 58` for an appendix, `21 CFR chapter I,
subchapter H` for a chapter level. `url` is the official page of the node, e.g.
`https://www.ecfr.gov/current/title-21/chapter-I/subchapter-H/part-820/subpart-A/section-820.10`. An appendix is linked
through its part and a subject group has no page of its own. Historical rows link the current page and are dated by
`startsOn` / `endsOn`.

### Examples

**Which codified FDA rules mention a topic**

```json
{ "mode": "search", "query": "quality management system", "cfrTitle": 21, "order": "relevance", "maxItems": 20 }
```

**One agency's sections across the whole code, in the order the code prints them**

```json
{ "mode": "search", "query": "pfas", "agencySlugs": ["environmental-protection-agency"], "order": "hierarchy", "maxItems": 20 }
```

**A weekly watch list of sections that changed (only what is new since the last run)**

```json
{ "mode": "search", "query": "safety", "lookbackDays": 7, "order": "newest", "onlyNew": true, "maxItems": 50 }
```

**The table of contents of one part, for a RAG index**

```json
{ "mode": "structure", "cfrTitle": 21, "cfrPart": "820", "nodeTypes": ["subpart", "section"], "maxItems": 40 }
```

**Every recorded amendment of a part, newest first**

```json
{ "mode": "versions", "cfrTitle": 40, "cfrPart": "141", "order": "newest", "substantiveOnly": true, "maxItems": 30 }
```

**Where a phrase is regulated, before exporting any sections**

```json
{ "mode": "counts", "query": "artificial intelligence", "maxItems": 30 }
```

**How a single section used to read**

```json
{ "mode": "search", "cfrTitle": 21, "cfrSection": "820.30", "includeHistory": true, "order": "oldest", "maxItems": 20 }
```

### Output

One row per section version, tree node, amendment or dictionary entry. The first row of a real run with the default
input (`mode: "search"`, `query: "medical device"`, `cfrTitle: 21`, 50 rows in six seconds):

```json
{
  "mode": "search",
  "nodeType": "section",
  "citation": "21 CFR 892.2020",
  "titleNumber": 21,
  "titleName": "Food and Drugs",
  "chapter": "I",
  "subchapter": "H",
  "part": "892",
  "subpart": "B",
  "subjectGroup": null,
  "section": "892.2020",
  "appendix": null,
  "identifier": "892.2020",
  "heading": "Medical image communications device.",
  "hierarchyHeading": "§ 892.2020",
  "chapterName": "Food and Drug Administration, Department of Health and Human Services",
  "subchapterName": "Medical Devices",
  "partName": "Radiology Devices",
  "subpartName": "Diagnostic Devices",
  "sectionName": "Medical image communications device.",
  "excerpt": "Identification. A medical image communications device provides electronic transfer of medical image… image data between medical devices. It may include a physical communications medium, modems… image review software functionality for medical image processing and manipulation, such",
  "score": 51.984,
  "startsOn": "2021-04-19",
  "endsOn": null,
  "inForce": true,
  "changeTypes": ["effective"],
  "structureIndex": 82397,
  "reserved": false,
  "removed": false,
  "asOfDate": "2026-09-24",
  "found": true,
  "url": "https://www.ecfr.gov/current/title-21/chapter-I/subchapter-H/part-892/subpart-B/section-892.2020",
  "fetchedAt": "2026-09-27T20:58:29.940Z"
}
```

Filled in every mode: `mode`, `nodeType`, `citation`, `titleNumber`, `url`, `asOfDate`, `fetchedAt`, `found`
(`citation`, `titleNumber` and `url` are null on an agency row that owns no chapter of the code, and on the single
`found: false` row a run writes when nothing matched).

| Field | Type | Meaning |
|---|---|---|
| `mode` | string | The mode that produced the row |
| `nodeType` | string | Level of the code: `section`, `appendix`, `part`, `subpart`, `chapter`, `subchapter`, `subtitle`, `subjectGroup`, `title`, `agency`, or `notFound` |
| `citation` | string | The citation as it is written in practice, e.g. `21 CFR 820.10` |
| `titleNumber` / `titleName` | number / string | CFR title of the row |
| `chapter`, `subchapter`, `part`, `subpart`, `subjectGroup`, `section`, `appendix` | string | The hierarchy, level by level; null where the node has no such level |
| `identifier` | string | The node's own identifier (`820.10`, `A`, `I`) |
| `heading` / `hierarchyHeading` | string | The node's heading and its printed label (`§ 820.10`) |
| `chapterName`, `subchapterName`, `partName`, `subpartName`, `sectionName` | string | Headings of the levels above and of the node itself |
| `excerpt` | string | `search`: the passage the index matched, with the markup removed |
| `score` | number | `search`: relevance of the match |
| `startsOn` / `endsOn` | string | `search`: the day this version took effect and the day it stopped (null = still current) |
| `inForce` | boolean | `search`: whether the version was in force on `asOfDate` (computed here, not read off `endsOn`) |
| `changeTypes` | string\[] | `search`: what kind of change produced this version, e.g. `effective`, `initial` |
| `structureIndex` | number | The source's position index of the node |
| `reserved` / `removed` | boolean | The node is reserved, or was removed by an amendment |
| `amendmentDate` / `issueDate` / `versionDate` | string | `versions`: when the amendment was made, the issue it was published under, and the version's own date |
| `name` / `substantive` | string / boolean | `versions`: the printed section name, and whether the change was substantive |
| `sizeBytes` | number | `structure`: size of that node's text in the source's own XML — how long a part is before you read it |
| `descendantRange` | string | `structure`: the sections a node covers, e.g. `141.1 – 141.905` |
| `volumes` / `receivedOn` / `childCount` | array / string / number | `structure`: printed volumes, when the source received the text (UTC), and how many children the node has |
| `latestAmendedOn`, `latestIssueDate`, `upToDateAsOf` | string | `titles`: last amendment, its issue date, and the day the title is current through |
| `slug`, `shortName`, `displayName`, `parentSlug`, `childSlugs` | string / string\[] | `agencies`: the slug the filter accepts, the names, the parent department and the sub-agencies |
| `cfrReferences` / `cfrTitles` | array | `agencies`: the `{title, chapter}` pairs the agency writes, and the title numbers |
| `hitCount` / `maxScore` / `level` | number / number / string | `counts`: matching sections under the node, best score, and the source's own level name |
| `asOfDate` | string | The day the code was read as of — the same for every row of a run |
| `found` | boolean | `false` on the one row a run writes when nothing matched |
| `url` | string | Official page of the node |
| `fetchedAt` | string | Fetch time, ISO 8601 UTC |

Dataset views: **Sections** (search), **Title structure**, **Amendments**, **Dictionaries & hit counts**. A `SUMMARY`
record in the run's key-value store repeats the filters, the resolved `asOfDate`, whether it was clamped, the total
number of matches, the request count and every warning.

### Use it from code / agents

```bash
curl -X POST "https://api.apify.com/v2/acts/yadroo~ecfr-regulations/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"search","query":"quality management system","cfrTitle":21,"maxItems":20}'
```

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('yadroo/ecfr-regulations').call({ mode: 'versions', cfrTitle: 40, cfrPart: '141', maxItems: 30 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

```python
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("yadroo/ecfr-regulations").call(run_input={"mode": "counts", "query": "artificial intelligence", "maxItems": 30})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

MCP: add `https://mcp.apify.com` to Claude / Cursor / any MCP client and call the `yadroo/ecfr-regulations` tool with the same JSON input.

**For agents: use `counts` first, `search` second.** A phrase like "artificial intelligence" matches sections in a dozen
titles. One `counts` run costs a few dozen rows and tells the agent which titles and parts to look in; the follow-up
`search` then carries `cfrTitle` and `cfrPart` and pays for the sections that matter instead of paging blindly. Pass
`fields` to trim the row to what the model actually reads — `["citation","sectionName","excerpt","url"]` is usually enough.

### Pricing

Pay per event: **$0.001 per run start + $0.002 per dataset row**. The start event is charged on every run, including a
run that matches nothing and writes only the `found: false` row. Apify plan discounts apply to the per-row price:
−10 % on Bronze, −20 % on Silver, −30 % on Gold and above.

| Run | Rows | Cost at list price |
|---|---|---|
| The default input (50 sections for a phrase in one title) | 50 | $0.001 + 50 × $0.002 = **$0.101** |
| A part's table of contents (`structure`, one part) | 12 | $0.001 + 12 × $0.002 = **$0.025** |
| A topic map before searching (`counts`) | 30 | $0.001 + 30 × $0.002 = **$0.061** |
| A weekly watch that finds two changed sections (`onlyNew`) | 2 | $0.001 + 2 × $0.002 = **$0.005** |

Compute is small next to that: no browser, and the runs behind this README finished in a few seconds each — a search is
two requests, a part's structure three. The actor asks for 512 MB because its heaviest job parses a whole title's tree:
title 40 is 9.4 MB of JSON and 30 311 nodes, and walking every one of them needs well under half that memory. Passing
`cfrPart` or `cfrChapter` cuts the subtree before it is walked, which is why exporting one part is the cheap case.

### Limits & FAQ

- **No full section text, and that is the source's rule, not a shortcut.** `robots.txt` of the eCFR disallows the two
  endpoints that serve rendered and raw section text for every crawler, so this actor does not read them. You get the
  citation, the full hierarchy, the headings, the matched excerpt, the version window, the amendment dates and the link
  to the official page — open the link (or quote the citation) when you need the wording itself.
- **The code is one to three days behind today.** The source publishes the code up to a processed day, which every row
  reports as `asOfDate`; `mode: "titles"` shows it per title as `upToDateAsOf`. A date the source has not reached is
  clamped down to the newest available day, logged as a warning and flagged in `SUMMARY.asOfDateClamped` — it is not an
  error.
- **10 000 hits per query.** The search index refuses to page past that offset. A run that reaches the ceiling says so
  in the status message and in `SUMMARY.resultWindowHit`; narrow it with `cfrTitle`, `cfrPart`, `agencySlugs` or a
  change window.
- **Only the JSON API is read.** The site's HTML pages answer a redirect to a different domain when they are requested
  from a data-centre address, so this actor never fetches a page — it talks to `www.ecfr.gov/api` only. Links in the
  rows are for you to open in a browser.
- **A missing node gives a row, not silence.** A part or section that does not exist on the chosen day, a reserved
  title, or a filter combination with no data yields exactly one row with `found: false` and a heading that says what
  was missing. A section that a rewrite removed (21 CFR 820.30 after the February 2026 rewrite of part 820) has no
  current page, but `mode: "versions"` still has its amendment history.
- **Superseded versions need `includeHistory`.** By default a section appears once, as it stood on `asOfDate`. With
  `includeHistory: true` you get one row per version, each with its own `startsOn`, `endsOn` and `inForce` — which is
  how you diff a section over time.
- **`inForce` is computed, not copied.** A dated hit can still carry a non-null `endsOn` when a later version already
  exists, so "in force" is worked out from `startsOn`, `endsOn` and `asOfDate` rather than read off the source.
- **No published rate limit, so the actor stays polite.** Requests are sequential with a short pause, 429 and 5xx
  answers (the amendment log occasionally returns 502) are retried with backoff, and a run stops after 40 requests.
- **Empty fields are the source's.** `subpart`, `subchapter` and `subjectGroup` exist only where the code uses them;
  `excerpt` and `score` only in `search`; `sizeBytes` and `descendantRange` only in `structure`; one agency in the
  dictionary owns no chapter of the code at all.
- **`onlyNew` keeps one list per filter combination.** Changing the query, the title or the part starts a new list, so
  the first run of a changed watch writes everything it finds. The keys live in this actor's own key-value store.
- **Licensing.** The Code of Federal Regulations is a work of the US government and in the public domain; the actor adds
  the structure, the citations and the links.

***

Made by **Yadroo**. Sibling actors: [federal-register-documents](https://apify.com/yadroo/federal-register-documents) (the daily rules and notices behind these amendments), [sec-edgar-filings](https://apify.com/yadroo/sec-edgar-filings), [openfda-records](https://apify.com/yadroo/openfda-records), [cisa-kev-vulnerabilities](https://apify.com/yadroo/cisa-kev-vulnerabilities), [bls-release-calendar](https://apify.com/yadroo/bls-release-calendar).

# Actor input Schema

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

`search` queries the codified text of the whole Code of Federal Regulations and writes one row per matching section or appendix, by default only the version in force. `structure` walks one title's tree and writes a row per node, so you can see which parts and sections exist. `versions` reads the amendment log of a title or part: one row per section version with its amendment and issue date, which is the cheap way to answer 'what changed in 40 CFR 141 this year'. `counts` turns a phrase into a map of the code — how many hits sit in each title, chapter and part — without paying per section. `titles` and `agencies` write the two dictionaries the filters below accept; run them once and keep the values.

## `query` (type: `string`):

Words to look for in the regulation text, used by `search` and `counts`. Several words are matched as separate terms and ranked; put double quotes around a phrase (`"design controls"`) to keep the words together and in order. Empty in mode `search` returns the sections the other filters allow, which is useful with *Changed after* to list recent amendments without a topic.

## `order` (type: `string`):

Applies to mode `search`. `hierarchy` reads like the printed code and is the one to pick when you export a part. `newest` pairs with *Changed after* for a change feed. Modes `structure` and `versions` always follow the code's own order, `titles` and `agencies` their own numbering and name.

## `asOfDate` (type: `string`):

YYYY-MM-DD. Empty = the newest day the source has processed, which is usually one to three days behind today; the actor reads that day from the title dictionary and reports it in the run log and in `asOfDate`. A date the source has not reached yet is refused by the API, so the actor clamps a future date down to the newest available day instead of failing. An older date gives you the code as it read that day — the way to quote a rule as of a contract or audit date.

## `includeHistory` (type: `boolean`):

Off (the default) every row is the version in force on *Text as it stood on*, so one section appears once. On, the search also returns the older and the not-yet-effective versions of a section: several rows per section, each with its own `startsOn`, `endsOn` and `inForce`. Turn it on to diff a section over time, keep it off for compliance lists.

## `changedAfter` (type: `string`):

YYYY-MM-DD. Keep only sections whose text was touched after this day. This is the field that turns the actor into a regulation watch: the source records every amendment, so a weekly task with a seven-day window writes exactly the sections that moved.

## `changedBefore` (type: `string`):

YYYY-MM-DD, the other end of the change window. Use both ends to pull one quarter of amendments.

## `lookbackDays` (type: `integer`):

Relative change window for scheduled runs, counted in UTC from the day the run starts: 7 = the past week, 90 = the past quarter. Ignored when *Changed after* is set. In mode `versions` it filters by issue date instead, which is the same calendar the source publishes amendments on.

## `cfrTitle` (type: `integer`):

One of the 50 titles of the code: 21 = food and drugs, 40 = environment, 12 = banks, 17 = commodity and securities exchanges, 26 = internal revenue, 29 = labor, 49 = transportation. Optional in `search` and `counts` (empty = all titles), required by `structure` and `versions`, ignored by `titles` and `agencies`. Run mode `titles` for the full list with names.

## `cfrChapter` (type: `string`):

Roman numeral as the code prints it (`I`, `III`, `XII`). A chapter belongs to one agency, so this is the way to keep only that agency's part of a title. Needs *CFR title*.

## `cfrPart` (type: `string`):

Part number inside the title, e.g. `820` in title 21 or `141` in title 40. In `search` it narrows the hits, in `structure` it exports that part's subparts and sections, in `versions` it limits the amendment log to that part. Needs *CFR title*.

## `cfrSubpart` (type: `string`):

Subpart letter inside the part (`A`, `C`, `UUUUU`). Needs *CFR part*.

## `cfrSection` (type: `string`):

A single section, written the way the citation does: `820.30`, `141.23`. Combined with *CFR title* this answers 'what happened to this section' — in `versions` you get its amendment history, in `search` with *Include superseded versions* every version of its text. A section that does not exist on the chosen date yields one row with `found: false` rather than an empty run.

## `agencySlugs` (type: `array`):

Agency slugs for mode `search`, combined with OR, e.g. `environmental-protection-agency`, `food-and-drug-administration`, `securities-and-exchange-commission`. The source maps each agency to the chapters it owns, so this filter is broader than a title filter and narrower than a full-code search. Mode `agencies` writes the whole dictionary with the chapters per slug; an unknown slug is reported with the closest match instead of a silent empty run.

## `nodeTypes` (type: `array`):

Mode `structure` only: which levels of the title tree become dataset rows. `["part"]` gives a compact table of contents, `["part", "section"]` the parts with their sections, which is also what an empty list means. Every row carries `nodeType`, its parent path and `sizeBytes`, the size of that node's text in the source's own XML, which tells you how long a part is before you read it.

## `substantiveOnly` (type: `boolean`):

Mode `versions` only. The source marks each amendment as substantive or not; the non-substantive ones are cross-reference and nomenclature fixes. On, only the amendments that changed the obligation are written.

## `includeRemoved` (type: `boolean`):

Mode `versions` only. On, sections that an amendment deleted are written too, with `removed: true` — worth having when you track a renumbering such as a part being rewritten.

## `onlyNew` (type: `boolean`):

Remember every row key (citation plus version start date, or amendment date in mode `versions`) in this actor's key-value store and write only keys that were not there on the previous run. The first run writes what it finds, later runs write what appeared since — a watch list on a schedule that does not pay twice for the same section.

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

Stop after this many rows. The search index serves at most 10 000 hits per query, so narrow the title, part or change window to go deeper; `structure` and `versions` of a large title can produce tens of thousands of nodes, which is what this cap is for.

## `fields` (type: `array`):

Keep only these fields, in this order, e.g. \["citation", "sectionName", "startsOn", "url"]. Empty = every field the mode fills.

## Actor input object example

```json
{
  "mode": "search",
  "query": "medical device",
  "order": "relevance",
  "includeHistory": false,
  "cfrTitle": 21,
  "nodeTypes": [
    "part",
    "section"
  ],
  "substantiveOnly": false,
  "includeRemoved": false,
  "onlyNew": false,
  "maxItems": 50
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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 = {
    "query": "medical device",
    "cfrTitle": 21,
    "nodeTypes": [
        "part",
        "section"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("yadroo/ecfr-regulations").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 = {
    "query": "medical device",
    "cfrTitle": 21,
    "nodeTypes": [
        "part",
        "section",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("yadroo/ecfr-regulations").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 '{
  "query": "medical device",
  "cfrTitle": 21,
  "nodeTypes": [
    "part",
    "section"
  ]
}' |
apify call yadroo/ecfr-regulations --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yadroo/ecfr-regulations"
        }
    }
}
```

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/taPpQC2YQGrYDqsoA/builds/sy2Bz872YDnwETGB3/openapi.json
