
Short answer: Yes, the eCFR has a free official REST API, and it needs no API key. GovInfo, the federal publishing site, also offers the same regulations as bulk XML files. Use the eCFR API when you need one section, a past date, a change history or search. Use the bulk files when you want a whole title or a whole yearly edition. None of it is the official legal text. The eCFR calls itself "authoritative but unofficial," and the official CFR is the yearly edition.
Written and checked on 12 October 2026. This is general information, not legal advice; check the cited statute or opinion before relying on it.
TL;DR
- Three official doors, three sets of rules. The eCFR API needs no key. GovInfo bulk files need no key. The GovInfo API needs one.
- Every request below was run on 12 October 2026 against the live service with no login, and the responses shown are trimmed copies of what came back.
- The eCFR is a compilation. The official text is the yearly CFR edition, and the eCFR's change history starts in January 2017.
- Know the limits. The pages we could read state no rate limit, search stops at 10,000 results, and the full-text endpoint refuses requests that do not accept compression.
- A simple nightly check works. Read the title list first and refresh only the titles that changed.
Official text, as of 12 October 2026
The example used throughout is 45 CFR 164.404, "Notification to individuals," the HIPAA rule on telling people about a data breach. Paragraph (b) reads: "a covered entity shall provide the notification required by paragraph (a) of this section without unreasonable delay and in no case later than 60 calendar days after discovery of a breach," subject to the law-enforcement delay in 164.412. That wording was read on 12 October 2026 in the eCFR's data, in GovInfo's bulk file and in the official 2025 edition of the section.
What changed in 2025 and 2026. For this section, nothing the eCFR records. Its version list for 164.404 has two entries: 30 December 2016, and a non-substantive reissue on 30 March 2023. The title as a whole was last amended on 6 October 2026, but that date covers all of Title 45, not this section.
For the plain-English version of what the eCFR is, see what the eCFR is and whether it is official.
Your nightly job rebuilds a regulations database, and a teammate proposes downloading every title every night. What is a better first step?
Three official doors
| Source | Address | Key | Returns | Best for |
|---|---|---|---|---|
| eCFR API | ecfr.gov/api/... (admin, search and versioner services) | None | JSON for lists, structure and search; XML for text | One section on a date, history, search |
| GovInfo bulk data, eCFR | govinfo.gov/bulkdata/ECFR/title-N/ | None | One XML file per title, current text only | Loading a full title |
| GovInfo bulk data, CFR annual | govinfo.gov/bulkdata/CFR/<year>/title-N/ | None | The yearly edition as XML volumes and a zip | Dated snapshots, one per year |
| GovInfo API | api.govinfo.gov | Required | Package lists and links to the bulk files | Finding which titles changed since a date |
The eCFR's own reader aid says: "No API keys are needed; all you need is an HTTP client or browser." It also says the data starts from the files used to make the print and PDF editions, which GPO then publishes as bulk XML. The eCFR API documentation lists the endpoints. That page loads as a Swagger interface that did not render as text when we read it, so every parameter below was checked by running it, not by reading the Swagger text.
Status comes first
The eCFR's own page says it is "an editorial compilation of CFR material and amendments published in the daily Federal Register." GPO's guide to the eCFR bulk files, a document dated August 2015 on GPO's GitHub, says the XML files are "not part of the official on-line format of the CFR." It adds that "only the PDF and Text versions" of CFR content on FDsys "have legal status," and that "XML files available for download are not digitally signed." Not verified in our October 2026 review: the legal status GPO gives its CFR yearly-edition XML, which has its own user guide. Store the date you pulled the data, and label it a compilation.
Counts, sizes and dates in this guide were read on 12 October 2026. Search totals, file sizes, element counts and last-modified times drift as the CFR changes, so treat them as "as of 12 October 2026" and re-run a call before you rely on a number.
Request 1: how fresh is the data?
curl -s --compressed "https://www.ecfr.gov/api/versioner/v1/titles.json"
{"titles":[
{"number":1,"name":"General Provisions","latest_amended_on":"2022-12-29","latest_issue_date":"2026-08-10","up_to_date_as_of":"2026-10-08","reserved":false},
... 48 more titles ...
{"number":45,"name":"Public Welfare","latest_amended_on":"2026-10-06","latest_issue_date":"2026-10-06","up_to_date_as_of":"2026-10-08","reserved":false}],
"meta":{"date":"2026-10-08","import_in_progress":false}}
That came back as HTTP 200, 1,261 bytes on the wire and 8,033 decoded. There are 50 entries, and Title 35 is "reserved": true with null dates, so 49 hold content. The eCFR's "Understanding eCFR Dates" page explains the three dates. latest_amended_on is the last substantive change. latest_issue_date is the last change of any kind, so it can be later and only fix typos or formatting. up_to_date_as_of is the date the title is current through. When import_in_progress is false, every title carries the same currency date as meta.date. Make this the first call of any nightly job. If up_to_date_as_of has not moved, stop.
Request 2: one section as XML, on a date
curl -s --compressed \
"https://www.ecfr.gov/api/versioner/v1/full/2026-10-01/title-45.xml?part=164§ion=164.404"
<?xml version="1.0" encoding="UTF-8"?>
<DIV8 N="164.404" TYPE="SECTION" hierarchy_metadata="{"path":"/on/_SUBSTITUTE_DATE_/title-45/section-164.404","citation":"45 CFR 164.404"}">
<HEAD>§ 164.404 Notification to individuals.</HEAD>
<P>(a) <I>Standard</I> ... (b) <I>Implementation specification: Timeliness of notification.</I> ...
The response was HTTP 200, text/xml, 1,963 bytes on the wire and 5,607 bytes decoded. Four things to know.
Compression is required. Without it the service answers HTTP 406. Plain curl with no flags sends no Accept-Encoding header, and this is what came back:
<error>
<message>This endpoint requires response compression. Send an Accept-Encoding header that permits compression.</message>
<supportCode>11</supportCode>
</error>
--compressed fixes it. The Python requests library asks for gzip by default, so it never hits this.
Encoding needs care. The response declares text/xml with no charset, and the XML declaration says UTF-8. Python requests falls back to ISO-8859-1 for a text type with no charset, and the section sign breaks:
r = requests.get(url, params={"part": "164", "section": "164.404"})
print(r.headers["content-type"], r.encoding) # text/xml ISO-8859-1
i = r.text.find("<HEAD>")
print(r.text[i:i+52]) # <HEAD>§ 164.404 Notification to individuals.</HEAD>
j = r.content.decode("utf-8").find("<HEAD>")
print(r.content.decode("utf-8")[j:j+52]) # <HEAD>§ 164.404 Notification to individuals.</HEAD>
Decode r.content as UTF-8 yourself.
Dates have edges. These were all run with --compressed:
date=2030-01-01 -> 404 {"error":"The requested date 2030-01-01 is past the title's most recent issue date of 2026-10-08, see https://www.ecfr.gov/api/versioner/v1/titles for details"}
date=2015-01-01 -> 404 {"error":"No matching content found."}
date=2026-13-45 -> 400 {"error":"Bad parameter: invalid date."}
A section request returns the section alone. It has no Authority or Source lines. Ask for the part (part=164, no section) to get them: that response was 57,969 bytes on the wire and 282,682 decoded. It carried the Authority line, "42 U.S.C. 1302(a); 42 U.S.C. 1320d-1320d-9; ...", and three Source lines. A whole title is much bigger.
Request 3: structure and ancestry
curl -s --compressed "https://www.ecfr.gov/api/versioner/v1/structure/2026-10-01/title-45.json"
curl -s --compressed "https://www.ecfr.gov/api/versioner/v1/ancestry/2026-10-01/title-45.json?section=164.404"
The first call returned the whole tree for Title 45 with no regulation text, 234,080 bytes on the wire and 1,931,145 decoded. Each node carries an identifier, labels, a type, a reserved flag and a size, and some carry a descendant_range, the span of part or section numbers underneath, such as parts 1 to 199 for Subtitle A. The size value for Title 45 was 14,112,759; treat it as a relative measure for sizing a job.
{"identifier":"45","label_level":"Title 45","label_description":"Public Welfare","type":"title","size":14112759,
"children":[{"identifier":"A","label_level":" Subtitle A","label_description":"Department of Health and Human Services","type":"subtitle","size":6504145,"descendant_range":"...", ...
The second returned the parents of one section. Trimmed, they are Title 45, Subtitle A, Subchapter C ("Administrative Data Standards and Related Requirements," covering 160 to 169), Part 164 ("Security and Privacy"), Subpart D, and the section itself. Use structure to draw a table of contents without downloading text, and to size a job before you run it.
Request 4: the history of one section
curl -sg --compressed \
"https://www.ecfr.gov/api/versioner/v1/versions/title-45.json?issue_date[gte]=2009-01-01&part=164§ion=164.404"
The -g flag matters. Without it curl reads the square brackets as a range and prints nothing.
{"content_versions":[
{"amendment_date":"2016-12-30","issue_date":"2016-12-30","identifier":"164.404","substantive":true,"removed":false,"type":"section", ...},
{"amendment_date":"2016-12-30","issue_date":"2023-03-30","identifier":"164.404","substantive":false,"removed":false,"type":"section", ...}],
"meta":{"title":"45","part":"164","section":"164.404","result_count":"2","latest_amendment_date":"2016-12-30","latest_issue_date":"2023-03-30"}}
The history begins at the eCFR's own first snapshot, not at the rule's age. This rule came from a Federal Register notice dated 24 August 2009 (74 FR 42740, document E9-20169, an interim final rule). The eCFR's Source line for Subpart D cites the 2009 page, 74 FR 42767. Yet the first version here is from 30 December 2016. The same call also answered 200 without the issue_date filter, though do not rely on that. For anything older, use the yearly editions, which our post on CFR annual editions covers.
Request 5: search, and the 10,000 ceiling
curl -s --compressed \
'https://www.ecfr.gov/api/search/v1/results?query=%22breach%20of%20unsecured%20protected%20health%20information%22&per_page=3&date=2026-10-01'
{"results":[{"starts_on":"2016-12-30","ends_on":null,"type":"Section",
"hierarchy":{"title":"45","part":"164","subpart":"D","section":"164.406", ...},
"headings":{"section":"Notification to the media.", ...},
"full_text_excerpt":"(a) Standard. For a <strong>breach of unsecured protected health information</strong> involving more than ...",
"change_types":["effective","initial"]}, ...],
"meta":{"current_page":1,"total_pages":3,"total_count":9,"max_score":39.475594,
"description":"Changes to sections matching '\"breach of unsecured protected health information\"'"}}
Two traps. First, results are version records, as the description says. On 12 October 2026 the same phrase returned 11 results without a date and 9 with date=2026-10-01, so pass a date to get the sections in force. The count endpoint (/api/search/v1/count) with no date also returned 11.
Second, a broad query hits a ceiling. A search for safety with a date reported "total_count":10000,"total_pages":10000 at one result per page. That is the ceiling, not the real count. Asking for a page past it fails:
.../results?query=safety&per_page=1000&page=11
400 {"errors":{"error":["can only paginate through 10,000 results. Try using filters to limit results."]}}
The fix is to slice. On 12 October 2026, adding hierarchy[title]=49 brought the same query to 6,527 results, and agency_slugs[]=transportation-department brought it to 7,610. Both ran with date=2026-10-01. The admin service at /api/admin/v1/agencies.json lists 153 agencies with their slugs. The admin service also has a corrections endpoint, and /api/admin/v1/corrections/title/45.json returned records such as one for 45 CFR 46.103 with its Federal Register citation, 70 FR 36328.
Bulk data: whole titles and the yearly editions
The GovInfo bulk data directory for the eCFR holds 49 title folders (Title 35 is absent) and a resources folder with the schema and user guide. As of 12 October 2026 the Title 45 file, ECFR-title45.xml, returned HTTP 200, text/xml, 13,769,405 bytes, last modified 7 October 2026 at 21:18 GMT. The folder listing shows different titles refreshed on different days: Title 45 on 7 October 2026, Title 44 on 24 June 2026 and Title 4 on 19 July 2024. A title with no change is not rewritten, so do not assume every file moves daily.
This XML is a different dialect from the versioner's. The root is <DLPSTEXTCLASS>. A section is <DIV8 N="§ 164.404" NODE="45:2.0.1.2.21.4.1.3" TYPE="SECTION">, with the section sign inside the N attribute, where the versioner writes N="164.404". Source credits appear as <CITA> elements (1,719 in the Title 45 file), and 164.404 has none, as in the versioner copy. Write one parser per dialect.
The yearly CFR edition has folders for 1996 to 2026. The 2025 edition of Title 45 is five volumes, CFR-2025-title45-vol1.xml to vol5.xml, plus CFR-2025-title-45.zip. The 2026 folder was only partly filled on 12 October 2026. It listed titles 5, 7, 9, 10, 11, 12, 14, 15 and 16 plus a zip, and no Title 45 (a request for a 2026 Title 45 PDF on GovInfo returned its error page). So the current Title 45 text comes from the eCFR, and the newest yearly edition of Title 45 reflects 1 October 2025.
The GovInfo API lists packages that changed since a date. It needs a key. Without one:
GET https://api.govinfo.gov/collections/ECFR/2026-10-01T00:00:00Z?offsetMark=*&pageSize=2
401 {"error":{"code":"API_KEY_MISSING","message":"No api_key was supplied. Get one at https://www.govinfo.gov/api-signup"}}
With the shared DEMO_KEY the first run on 12 October 2026 returned HTTP 200 and a count of 24 packages, such as ECFR-title40 (dateIssued 2026-10-08, lastModified 2026-10-11T17:39:16Z) and ECFR-title42. Calls repeated later the same day returned HTTP 429 with OVER_RATE_LIMIT, so the count of 24 could not be re-checked. The responses carried x-ratelimit-limit: 10. Sign up for a real key.
Which door when
The limits, honestly
| Source | Stated limit | What we saw on 12 October 2026 |
|---|---|---|
| eCFR API | None stated on the pages read | Ten titles requests in a row all returned 200, with no rate-limit or Retry-After headers. The title-list responses were marked no-store. This is not a promise. |
| eCFR full XML | Compression required | HTTP 406 without it |
| eCFR search | 10,000 results per query | HTTP 400 beyond that |
| GovInfo bulk files | None stated on the pages read | Plain HTTP download |
| api.govinfo.gov | api.data.gov defaults: 1,000 requests per hour per key. DEMO_KEY: 30 per hour and 50 per day per IP | DEMO_KEY responses carried x-ratelimit-limit: 10, and repeat calls returned HTTP 429 (OVER_RATE_LIMIT) |
The api.data.gov developer manual gives the key limits. Not verified in our October 2026 review: how the eCFR API treats very heavy traffic, and what it returns when overloaded. Set a User-Agent that names you and gives a contact, cache titles and structure, and slow down on errors.
One more note on access. The eCFR website pages, as opposed to the API, can answer scripted requests with a "Request Access" page. That page says: "programmatic access to these sites is limited to access to our extensive developer APIs." A developer on GPO's GitHub reported the same page in February 2024 when calling from AWS Lambda, because cloud hosts cannot be added to an allow list. That call used a path under /admin/api/, not the documented /api/ base, and GPO's reply was that no key is needed. Use the documented API paths, not page addresses, and note that calls from cloud addresses may be challenged.
A short Python client
All of it was run on 12 October 2026 with Python 3.12.1 and requests 2.32.5.
import re
import requests
BASE = "https://www.ecfr.gov/api"
S = requests.Session()
S.headers["User-Agent"] = "example-ecfr-client/1.0 (you@example.com)"
def get(path, **params):
r = S.get(f"{BASE}/{path}", params=params, timeout=60)
r.raise_for_status()
return r
def title_status(number):
data = get("versioner/v1/titles.json").json()
row = next(t for t in data["titles"] if t["number"] == number)
return data["meta"], row
def section_xml(title, part, section, date):
r = get(f"versioner/v1/full/{date}/title-{title}.xml", part=part, section=section)
return r.content.decode("utf-8") # the response declares no charset
def versions(title, part, section):
r = get(f"versioner/v1/versions/title-{title}.json", part=part, section=section)
return [(v["amendment_date"], v["issue_date"], v["substantive"])
for v in r.json()["content_versions"]]
def search(query, date, **filters):
meta = get("search/v1/results", query=query, date=date, per_page=3, **filters).json()["meta"]
if meta["total_count"] >= 10000:
raise ValueError("hit the 10,000 ceiling; add a filter and search again")
return meta["total_count"]
Calling it for 45 CFR 164.404 (print(title_status(45)), then the section, versions and three searches, with the 10,000 guard caught) printed:
({'date': '2026-10-08', 'import_in_progress': False}, {'number': 45, 'name': 'Public Welfare', 'latest_amended_on': '2026-10-06', 'latest_issue_date': '2026-10-06', 'up_to_date_as_of': '2026-10-08', 'reserved': False})
§ 164.404 Notification to individuals.
in no case later than 60 calendar days
[('2016-12-30', '2016-12-30', True), ('2016-12-30', '2023-03-30', False)]
9
guard: hit the 10,000 ceiling; add a filter and search again
6527
The last three lines are the search for the quoted phrase (9 sections), the same search for safety tripping the guard, and safety narrowed to Title 49 with {"hierarchy[title]": 49} (6,527).
Where the data comes from
A short thread on GPO's usgpo/bulk-data repository explains the setup. In February 2024 a developer, rchancey, asked about an API key. The reply from jonquandt, who answered for the GovInfo side, was: "there is no API key needed to access the ecfr.gov API." In a later comment he said: "eCFR.gov pulls the latest eCFR xml from the GovInfo API. They store each version of a particular title and then provide point in time/change tracking on their system essentially by diffing each version of a given title." That explains why history starts when it does. The developer's own verdict on the bulk files was that they had been "a lifesaver" with one gap: "The only thing missing is changes." Read the thread on GitHub.
On timing, GPO's history page lists "August 2015 - eCFR XML Bulk Data was released on FDsys," and the 2015 guide says the bulk repository holds "the e-CFR in XML from 2015 forward." Not verified in our October 2026 review: the launch date of the eCFR REST API itself.
What the free sources do not hand you
The free routes give you text, structure and a change history. They do not give you one identifier that works across sources, a lookup from a citation to a record, alerts when a section changes, or joins from a regulation to the statute behind it and to the Federal Register notice that made it. You build those yourself, or you buy a maintained feed. Vaquill AI's CFR corpus, for example, is the current compilation, refreshed daily, and the yearly editions sit in a separate dated corpus. Our posts on the CFR as an API and on annual editions and point-in-time lookups cover that route, and the build-versus-buy guide to GovInfo compares the two. For the Federal Register side, see the Federal Register API. The wider map of sources is in US law data: the complete guide.
For the US statutes, regulations and court rules these laws sit in, see the US primary law API.
FAQ
Is there an official eCFR API?
Yes. The eCFR runs a free REST API under https://www.ecfr.gov/api/, with services for admin data, search and versioned content. It needs no key, and its documentation is a Swagger page at ecfr.gov/developers/documentation/api/v1.
Do I need an API key for the eCFR?
No. The eCFR's reader aid says no API keys are needed. The GovInfo API, which lists changed eCFR packages, does need a key, and DEMO_KEY is only for trials.
How do I get the CFR as XML?
There are three ways. The eCFR versioner returns XML for a section, a part or a title on a date, and it requires compression. GovInfo's bulk ECFR folder has one XML file per title with current text. GovInfo's bulk CFR folder has the yearly edition as XML volumes.
Can I get the CFR as JSON?
The eCFR API returns JSON for the title list, structure, ancestry, version history, search and counts. The full text came back as XML in every request run for this guide, and we found no JSON text endpoint. Unofficial wrappers that offer JSON exist, and we did not review them.
What is the eCFR API rate limit?
None is stated on the pages we read. In ten requests in a row we saw 200s and no rate-limit headers, which is an observation, not a guarantee. Cache what you can and slow down if the service returns errors.
How far back does the eCFR API go?
January 2017, according to the eCFR's Getting Started page. A date before the history returned 404 with "No matching content found." For earlier years use the yearly editions on GovInfo.
Is the eCFR data official or safe to cite?
It is described as authoritative but unofficial. Cite the yearly edition and the Federal Register for anything formal. The plain-English version is in is the eCFR official.
How do I know when a CFR section changes?
Poll the title list and compare up_to_date_as_of and latest_amended_on, then call the versions endpoint for the parts you track. A maintained feed can do this for you, as described in the CFR API guide.
New legal AI guides, weekly.
Further Reading
What Is the eCFR? Is It Official, and How Do You Search It?
Read postRegulations.gov API: Dockets, Documents and Comments Explained
Read postCompliance API: Two Meanings, and Where Regulatory Data Comes From
Read postCan Claude Search Statutes and Regulations? How to Check the Answer
Read postGovInfo API Guide: Keys, Endpoints, Limits and Bulk Data
Read postFederal Register API in Python: Search Rules and Notices
Read post
Co-Founder & CTO
Priyansh leads engineering and AI at Vaquill AI: the pipelines that pull statutes, regulations and court rules from every US jurisdiction's official publisher, and the REST API, MCP server and open dataset that serve them.