API Documentation

Learn how to use the eCFR Browser API to access CFR data programmatically.

Introduction

The eCFR Browser API provides programmatic access to the Code of Federal Regulations (CFR) data. You can use this API to retrieve titles, parts, sections, and content from the CFR.

The API is RESTful and returns data in JSON format. All endpoints are relative to the base URL of the application.

Base URL

All API endpoints are relative to the base URL of the application.

Authentication

Most endpoints do not require authentication. However, some endpoints that modify data (like bookmarks) use a static user ID for demonstration purposes.

Rate Limiting

There is currently no rate limiting implemented for the API endpoints. However, please be considerate and avoid making too many requests in a short period of time.

Error Handling

All API endpoints return appropriate HTTP status codes:

  • 200 OK: The request was successful
  • 400 Bad Request: The request was invalid
  • 404 Not Found: The requested resource was not found
  • 500 Internal Server Error: An error occurred on the server

Error responses include a JSON object with an error field:

{
  "error": "Error message"
}

Redis Cache Integration

The eCFR Browser uses Redis for caching to improve performance and reduce database load. This section explains how Redis is integrated into the application and how it affects API responses.

Cache Architecture

The Redis cache stores CFR references and search indexes to enable fast lookups and search operations. Several key structures are used to organize the data:

Key Structures

The Redis cache uses the following key patterns:

  • cfr:ref:[normalized_reference]: Stores CFR section data by normalized reference (e.g., "21cfr101.9")
  • cfr:search:index: A sorted set containing all searchable CFR references
  • cfr:cache:last_updated: Timestamp of the last cache update
  • cfr:cache:population_status: JSON object with information about the cache population process
  • cfr:search:index:status: JSON object with information about the search index status

Example of a cache population status object:

{
  "totalRecords": 39178,
  "processedRecords": 2000,
  "cachedRecords": 2000,
  "inProgress": false,
  "lastProcessedId": 2034,
  "startTime": "2025-04-28T00:24:18.233Z",
  "endTime": "2025-04-28T12:00:50.846Z"
}

Cache-Enabled Endpoints

The following API endpoints utilize the Redis cache for improved performance:

Section Reference Endpoint

The /api/ecfr/section/reference endpoint checks the Redis cache before querying the database:

Example: Redis Cache Flow for Section Reference

// Pseudocode for the section reference endpoint
async function getSectionByReference(reference) {
  // Normalize the reference
  const normalizedRef = normalizeReference(reference);
  
  // Check Redis cache
  const key = createCfrReferenceKey(normalizedRef); // Returns "cfr:ref:" + normalizedRef
  const cachedData = await redis.get(key);
  
  if (cachedData) {
    // Return the cached section data
    return JSON.parse(cachedData);
  }
  
  // If not in cache, query database
  const section = await database.getSectionByReference(normalizedRef);
  
  if (section) {
    // Update cache for future requests
    await redis.set(key, JSON.stringify({
      id: section.id,
      reference: normalizedRef,
      titleNumber: section.parts.titles.number,
      titleName: section.parts.titles.name,
      partNumber: section.parts.number,
      partName: section.parts.name,
      sectionNumber: section.number,
      sectionName: section.name,
      sectionText: section.regulation_text
    }));
  }
  
  return section;
}

Search Endpoint

The /api/ecfr/search and related search endpoints use the sorted set in Redis (cfr:search:index) to quickly find matching CFR references:

Example: Redis Search Implementation

// Pseudocode for searching CFR references
async function searchCfrReferences(query, limit = 50) {
  // Normalize the query
  const normalizedQuery = query.trim().toLowerCase();
  
  // Prepare match pattern for ZSCAN
  const matchPattern = "*" + normalizedQuery + "*";
  
  // Create a map to store unique results
  const resultsMap = new Map();
  
  // Get entries matching the pattern using ZSCAN
  let cursor = 0;
  
  do {
    // Scan the sorted set for matches
    const scanResult = await redis.zscan("cfr:search:index", cursor, {
      match: matchPattern,
      count: 100
    });
    
    cursor = scanResult[0];
    const matches = scanResult[1];
    
    // Process matches
    for (let i = 0; i < matches.length; i += 2) {
      const reference = matches[i];
      const key = "cfr:ref:" + reference;
      
      // Get the full data for this reference
      const data = await redis.get(key);
      
      if (data && resultsMap.size < limit) {
        const parsed = JSON.parse(data);
        resultsMap.set(parsed.id, parsed);
      }
    }
  } while (cursor !== 0 && resultsMap.size < limit);
  
  // Fall back to database if needed
  if (resultsMap.size === 0) {
    await databaseFallbackSearch(normalizedQuery, limit, resultsMap);
  }
  
  return Array.from(resultsMap.values()).slice(0, limit);
}

API Endpoints

Titles

Get All Titles
GET
Returns a list of all titles in the eCFR.
/api/ecfr/titles
Content Type: application/json
{
  "titles": [
    {
      "id": 1,
      "number": "1",
      "name": "General Provisions",
      "description": "General provisions and definitions applicable across federal regulations.",
      "created_at": "2023-01-01T00:00:00.000Z",
      "updated_at": "2023-01-01T00:00:00.000Z"
    },
    {
      "id": 2,
      "number": "2",
      "name": "Grants and Agreements",
      "description": "Regulations governing federal grants and cooperative agreements.",
      "created_at": "2023-01-01T00:00:00.000Z",
      "updated_at": "2023-01-01T00:00:00.000Z"
    }
  ]
}

Example: Fetching all titles

// Using fetch API
fetch('/api/ecfr/titles')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

Parts

Get Parts by Title
GET
Returns a list of all parts in a specific title.
/api/ecfr/parts
Content Type: application/json
{
  "parts": [
    {
      "id": 1,
      "title_id": 1,
      "number": "1",
      "name": "Definitions",
      "description": "General definitions used throughout the Code of Federal Regulations.",
      "created_at": "2023-01-01T00:00:00.000Z",
      "updated_at": "2023-01-01T00:00:00.000Z"
    },
    {
      "id": 2,
      "title_id": 1,
      "number": "2",
      "name": "General Provisions",
      "description": "General provisions applicable throughout federal regulations.",
      "created_at": "2023-01-01T00:00:00.000Z",
      "updated_at": "2023-01-01T00:00:00.000Z"
    }
  ]
}

Example: Fetching parts by title

// Using fetch API
fetch('/api/ecfr/parts?titleId=1')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

Sections

Get Section by ID
GET
Returns a specific section by ID, including its content. This endpoint uses Redis caching for improved performance.
/api/ecfr/content/{id}
Content Type: application/json
{
  "section": {
    "id": 1,
    "part_id": 1,
    "number": "1.1",
    "name": "Definitions",
    "content_path": "sections/1/1/1.1.md",
    "publication_year": "2019",
    "last_updated": "2023-01-01T00:00:00.000Z",
    "created_at": "2023-01-01T00:00:00.000Z",
    "updated_at": "2023-01-01T00:00:00.000Z",
    "regulation_text": "# §1.1 Definitions\n\n(a) As used in this chapter...",
    "cfr_reference": "1 CFR 1.1"
  },
  "content": "# §1.1 Definitions\n\n(a) As used in this chapter..."
}

Example: Fetching a section by ID

// Using fetch API
fetch('/api/ecfr/content/1')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
Get Section by CFR Reference
GET
Returns a specific section by CFR reference. This endpoint uses Redis caching for improved performance.
/api/ecfr/section/reference
Content Type: application/json
{
  "section": {
    "id": 1,
    "part_id": 1,
    "number": "1.1",
    "name": "Definitions",
    "content_path": "sections/1/1/1.1.md",
    "publication_year": "2019",
    "last_updated": "2023-01-01T00:00:00.000Z",
    "created_at": "2023-01-01T00:00:00.000Z",
    "updated_at": "2023-01-01T00:00:00.000Z",
    "regulation_text": "# §1.1 Definitions\n\n(a) As used in this chapter...",
    "cfr_reference": "1 CFR 1.1",
    "parts": {
      "id": 1,
      "number": "1",
      "name": "Definitions",
      "titles": {
        "id": 1,
        "number": "1",
        "name": "General Provisions"
      }
    }
  }
}

Example: Fetching a section by CFR reference

// Using fetch API
fetch('/api/ecfr/section/reference?reference=1 CFR 1.1')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
Search Sections
GET
Searches for sections based on a query string. This endpoint uses Redis caching for common queries.
/api/ecfr/search
Content Type: application/json
{
  "results": [
    {
      "id": 1,
      "part_id": 1,
      "number": "1.1",
      "name": "Definitions",
      "content_path": "sections/1/1/1.1.md",
      "publication_year": "2019",
      "last_updated": "2023-01-01T00:00:00.000Z",
      "created_at": "2023-01-01T00:00:00.000Z",
      "updated_at": "2023-01-01T00:00:00.000Z",
      "regulation_text": "# §1.1 Definitions\n\n(a) As used in this chapter...",
      "cfr_reference": "1 CFR 1.1",
      "parts": {
        "id": 1,
        "number": "1",
        "name": "Definitions",
        "titles": {
          "id": 1,
          "number": "1",
          "name": "General Provisions"
        }
      }
    }
  ],
  "totalCount": 1,
  "page": 1,
  "pageSize": 10,
  "totalPages": 1
}

Example: Searching for sections

// Using fetch API
fetch('/api/ecfr/search?q=definitions&page=1&pageSize=10')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

Bookmarks

Get User Bookmarks
GET
Returns a list of bookmarks for the current user.
/api/bookmarks
Content Type: application/json
{
  "bookmarks": [
    {
      "id": 1,
      "user_id": "default-user",
      "section_id": 1,
      "created_at": "2023-01-01T00:00:00.000Z",
      "updated_at": "2023-01-01T00:00:00.000Z",
      "section": {
        "id": 1,
        "part_id": 1,
        "number": "1.1",
        "name": "Definitions",
        "cfr_reference": "1 CFR 1.1"
      }
    }
  ]
}
Add Bookmark
POST
Adds a bookmark for the current user.
/api/bookmarks
Content Type: application/json
{
  "success": true,
  "message": "Bookmark added successfully",
  "bookmark": {
    "id": 1,
    "user_id": "default-user",
    "section_id": 1,
    "created_at": "2023-01-01T00:00:00.000Z",
    "updated_at": "2023-01-01T00:00:00.000Z"
  }
}
Remove Bookmark
DELETE
Removes a bookmark for the current user.
/api/bookmarks
Content Type: application/json
{
  "success": true,
  "message": "Bookmark removed successfully"
}

Cache Management

Get Cache Status
GET
Returns the status of the CFR cache.
/api/admin/cfr-cache
Content Type: application/json
{
  "status": {
    "totalReferences": 39178,
    "cacheSize": "~39178 references",
    "lastUpdated": "2025-04-28T12:00:50.846Z",
    "populationStatus": {
      "totalRecords": 39178,
      "processedRecords": 39178,
      "cachedRecords": 39178,
      "inProgress": false,
      "lastProcessedId": 39178,
      "startTime": "2025-04-28T00:24:18.233Z",
      "endTime": "2025-04-28T12:00:50.846Z"
    }
  }
}
Populate Cache
POST
Populates the CFR cache with data.
/api/admin/cfr-cache
Content Type: application/json
{
  "success": true,
  "count": 100,
  "lastProcessedId": 100,
  "withFullText": true
}
Clear Cache
DELETE
Clears the CFR cache.
/api/admin/cfr-cache
Content Type: application/json
{
  "success": true,
  "message": "Cache cleared successfully"
}

Client SDK

We recommend using a client-side SDK to interact with the eCFR Browser API. You can create a simple client wrapper like this:

Example: JavaScript SDK

// ecfr-client.js
class EcfrClient {
  constructor(baseUrl = '') {
    this.baseUrl = baseUrl;
  }

  async getAllTitles() {
    return this.get('/api/ecfr/titles');
  }

  async getPartsByTitle(titleId) {
    return this.get(`/api/ecfr/parts?titleId=${titleId}`);
  }

  async getSectionById(id) {
    return this.get(`/api/ecfr/content/${id}`);
  }

  async getSectionByReference(reference) {
    return this.get(`/api/ecfr/section/reference?reference=${encodeURIComponent(reference)}`);
  }

  async searchSections(query, page = 1, pageSize = 10) {
    return this.get(`/api/ecfr/search?q=${encodeURIComponent(query)}&page=${page}&pageSize=${pageSize}`);
  }

  async getBookmarks() {
    return this.get('/api/bookmarks');
  }

  async addBookmark(sectionId) {
    return this.post('/api/bookmarks', { sectionId });
  }

  async removeBookmark(sectionId) {
    return this.delete('/api/bookmarks', { sectionId });
  }

  async get(path) {
    const response = await fetch(`${this.baseUrl}${path}`);
    if (!response.ok) {
      throw new Error(`HTTP error ${response.status}`);
    }
    return response.json();
  }

  async post(path, data) {
    const response = await fetch(`${this.baseUrl}${path}`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    });
    if (!response.ok) {
      throw new Error(`HTTP error ${response.status}`);
    }
    return response.json();
  }

  async delete(path, data) {
    const response = await fetch(`${this.baseUrl}${path}`, {
      method: 'DELETE',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    });
    if (!response.ok) {
      throw new Error(`HTTP error ${response.status}`);
    }
    return response.json();
  }
}

// Usage
const client = new EcfrClient();
client.getAllTitles()
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

This SDK provides a convenient way to interact with the eCFR Browser API. You can extend it with additional methods as needed.

Webhooks

The eCFR Browser API does not currently support webhooks. This feature may be added in the future.

Support

If you have questions or need help with the eCFR Browser API, please contact the development team. We're happy to assist you with any issues or questions you might have.