Complete Guide to SEC EDGAR API Integration

Learn how to integrate the SEC EDGAR API into your applications with step-by-step examples, authentication methods, search techniques, and industry best practices for accessing 18M+ financial filings.

Table of Contents

  1. Introduction to SEC EDGAR API
  2. API Authentication & Setup
  3. Making Your First API Request
  4. Advanced Search Techniques
  5. Parsing Response Data
  6. Best Practices & Optimization
  7. Error Handling & Rate Limits
  8. Conclusion & Next Steps

Introduction to SEC EDGAR API

The SEC EDGAR API provides programmatic access to over 18 million SEC filings dating back to the 1990s. Whether you're building financial analysis tools, compliance monitoring systems, or investment research platforms, the EDGAR API offers comprehensive access to Forms 10-K, 10-Q, 8-K, proxy statements, and more.

🎯 What You'll Learn

By the end of this guide, you'll be able to:

  • Authenticate with the SEC EDGAR API
  • Perform basic and advanced search queries
  • Parse JSON responses and extract key data
  • Implement proper error handling and rate limiting
  • Follow industry best practices for production use

Why Use the SEC EDGAR API?

Unlike manual SEC.gov searches, the EDGAR API enables:

API Authentication & Setup

Before making requests to the SEC EDGAR API, you'll need to set up authentication and understand the endpoint structure.

Getting Your API Key

First, sign up for a free API key at Kaleidoscope API. Free accounts include 300 API calls per month, perfect for testing and small projects.

# Test your API key curl -X GET "https://api.kscope.io/v2/sec/search?key=YOUR_API_KEY&limit=1"

Setting Up Your Development Environment

Here's how to set up authentication in popular programming languages:

import requests import os # Store your API key securely API_KEY = os.getenv('KALEIDOSCOPE_API_KEY') BASE_URL = 'https://api.kscope.io/v2' # Common headers for all requests headers = { 'User-Agent': 'YourApp/1.0 (contact@yourcompany.com)', 'Accept': 'application/json' }

💡 Security Best Practice

Never hardcode API keys in your source code. Use environment variables, configuration files, or secure key management services in production environments.

Making Your First API Request

Let's start with a simple request to search for Apple's recent SEC filings:

def search_sec_filings(ticker, limit=10): """Search for SEC filings by ticker symbol""" url = f"{BASE_URL}/sec/search" params = { 'key': API_KEY, 'ticker': ticker, 'limit': limit } response = requests.get(url, params=params, headers=headers) response.raise_for_status() return response.json() # Example usage apple_filings = search_sec_filings('AAPL', limit=5) print(f"Found {len(apple_filings['data'])} Apple filings")

Understanding the Response Structure

The API returns a structured JSON response with the following format:

{ "data": [ { "form_type": "10-K", "company_name": "Apple Inc.", "ticker": "AAPL", "filing_date": "2024-11-01", "accession_number": "0000320193-24-000123", "period_ending": "2024-09-30", "filing_url": "https://www.sec.gov/ix?doc=/Archives/edgar/data/..." } ], "count": 5, "next_page": null }

Advanced Search Techniques

The SEC EDGAR API supports sophisticated search capabilities including Boolean expressions, date ranges, and form type filtering.

Filtering by Form Types

Search for specific SEC form types to narrow your results:

def get_annual_reports(ticker, years=3): """Get annual 10-K reports for the past N years""" url = f"{BASE_URL}/sec/search" params = { 'key': API_KEY, 'ticker': ticker, 'form_type': '10-K', 'date_from': f'{2025 - years}-01-01', 'date_to': '2025-01-01' } response = requests.get(url, params=params, headers=headers) return response.json()

Boolean Search Expressions

Use Boolean logic for complex search queries:

def search_acquisition_filings(company_name): """Search for merger & acquisition related filings""" params = { 'key': API_KEY, 'query': f'{company_name} AND (merger OR acquisition OR "tender offer")', 'form_type': '8-K,SC 13D,SC TO-I', 'limit': 20 } response = requests.get(f"{BASE_URL}/sec/search", params=params) return response.json()
Parameter Description Example
ticker Stock ticker symbol AAPL, MSFT, GOOGL
form_type SEC form types (comma-separated) 10-K,10-Q,8-K
date_from Start date (YYYY-MM-DD) 2024-01-01
date_to End date (YYYY-MM-DD) 2024-12-31
query Boolean search expression "revenue recognition" AND GAAP
cik Central Index Key 0000320193

Parsing Response Data

Once you've retrieved filing data, you'll need to extract and process the information for your application.

Extracting Key Information

def extract_filing_summary(filing_data): """Extract key information from filing response""" summary = [] for filing in filing_data['data']: filing_info = { 'company': filing.get('company_name'), 'form': filing.get('form_type'), 'date': filing.get('filing_date'), 'period': filing.get('period_ending'), 'url': filing.get('filing_url'), 'accession': filing.get('accession_number') } summary.append(filing_info) return summary # Usage example filings = search_sec_filings('TSLA', limit=10) summary = extract_filing_summary(filings) for filing in summary: print(f"{filing['company']} filed {filing['form']} on {filing['date']}")

Handling Pagination

For large result sets, implement pagination to retrieve all available data:

def get_all_filings(ticker, form_type=None): """Retrieve all filings for a company with pagination""" all_filings = [] page = 1 while True: params = { 'key': API_KEY, 'ticker': ticker, 'page': page, 'limit': 100 } if form_type: params['form_type'] = form_type response = requests.get(f"{BASE_URL}/sec/search", params=params) data = response.json() all_filings.extend(data['data']) # Check if there are more pages if not data.get('next_page') or len(data['data']) < 100: break page += 1 return all_filings

Best Practices & Optimization

Follow these best practices to build robust, efficient applications with the SEC EDGAR API.

Rate Limiting and Performance

⚠️ Rate Limit Guidelines

Free accounts are limited to 300 requests per month. Paid plans support up to 3,600 requests per hour. Always implement proper rate limiting to avoid hitting these limits.

import time from functools import wraps def rate_limit(calls_per_second=1): """Decorator to implement rate limiting""" def decorator(func): last_called = [0.0] @wraps(func) def wrapper(*args, **kwargs): elapsed = time.time() - last_called[0] left_to_wait = 1 / calls_per_second - elapsed if left_to_wait > 0: time.sleep(left_to_wait) result = func(*args, **kwargs) last_called[0] = time.time() return result return wrapper return decorator # Apply rate limiting to API calls @rate_limit(calls_per_second=0.5) def api_request(url, params): return requests.get(url, params=params, headers=headers)

Caching Responses

Implement caching to reduce API calls and improve performance:

import json import hashlib from pathlib import Path class SECAPIClient: def __init__(self, api_key, cache_dir='./cache'): self.api_key = api_key self.cache_dir = Path(cache_dir) self.cache_dir.mkdir(exist_ok=True) def _get_cache_key(self, params): """Generate cache key from request parameters""" param_str = json.dumps(params, sort_keys=True) return hashlib.md5(param_str.encode()).hexdigest() def search_with_cache(self, params, cache_hours=24): """Search with local caching""" cache_key = self._get_cache_key(params) cache_file = self.cache_dir / f"{cache_key}.json" # Check if cached version exists and is recent if cache_file.exists(): cache_age = time.time() - cache_file.stat().st_mtime if cache_age < cache_hours * 3600: with open(cache_file) as f: return json.load(f) # Make API request and cache result params['key'] = self.api_key response = requests.get(f"{BASE_URL}/sec/search", params=params) data = response.json() # Save to cache with open(cache_file, 'w') as f: json.dump(data, f) return data

Error Handling & Rate Limits

Robust error handling is essential for production applications using the SEC EDGAR API.

import requests from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry class SECAPIError(Exception): """Custom exception for SEC API errors""" pass def create_session_with_retries(): """Create requests session with automatic retries""" session = requests.Session() retry_strategy = Retry( total=3, backoff_factor=1, status_forcelist=[429, 500, 502, 503, 504] ) adapter = HTTPAdapter(max_retries=retry_strategy) session.mount("http://", adapter) session.mount("https://", adapter) return session def robust_api_request(url, params): """Make API request with comprehensive error handling""" session = create_session_with_retries() try: response = session.get(url, params=params, headers=headers, timeout=30) if response.status_code == 401: raise SECAPIError("Invalid API key or authentication failed") elif response.status_code == 429: raise SECAPIError("Rate limit exceeded. Please try again later.") elif response.status_code >= 400: raise SECAPIError(f"API request failed: {response.status_code} - {response.text}") return response.json() except requests.exceptions.RequestException as e: raise SECAPIError(f"Network error: {str(e)}") except json.JSONDecodeError: raise SECAPIError("Invalid JSON response from API")

Ready to Start Building?

Get your free API key and start accessing 18M+ SEC filings today. No credit card required for the free tier.

Conclusion & Next Steps

You now have the foundation to integrate the SEC EDGAR API into your applications. This guide covered authentication, search techniques, data parsing, and production best practices.

Next Steps

Additional Resources

Happy coding! If you build something cool with the SEC EDGAR API, we'd love to hear about it.