DEV Community

agenthustler
agenthustler

Posted on

How to Build a Public Company Director Network Tracker

How to Build a Public Company Director Network Tracker

Board interlocks — where directors sit on multiple company boards — reveal hidden power networks in corporate governance. Let's build a Python tool that maps these connections using public SEC data.

Data Sources

  • SEC EDGAR — Official filings with director information
  • Proxy statements (DEF 14A) — Most detailed director info
  • OpenCorporates — Open corporate data

Setting Up

pip install requests beautifulsoup4 pandas networkx matplotlib
Enter fullscreen mode Exit fullscreen mode

Fetching Director Data from SEC EDGAR

import requests
import time

SEC_HEADERS = {"User-Agent": "DirectorTracker research@example.com"}
EDGAR_BASE = "https://efts.sec.gov/LATEST"

def search_company(company_name):
    params = {
        "q": f'"{company_name}"',
        "forms": "DEF 14A",
        "dateRange": "custom",
        "startdt": "2025-01-01",
        "enddt": "2026-12-31"
    }
    resp = requests.get(f"{EDGAR_BASE}/search-index",
                       params=params, headers=SEC_HEADERS)
    time.sleep(0.2)
    if resp.status_code == 200:
        return resp.json().get("hits", {}).get("hits", [])
    return []

results = search_company("Apple Inc")
print(f"Found {len(results)} proxy filings")
Enter fullscreen mode Exit fullscreen mode

Parsing Proxy Statements for Directors

from bs4 import BeautifulSoup
import re

def extract_directors_from_proxy(filing_url):
    params = {
        "api_key": "YOUR_SCRAPERAPI_KEY",
        "url": filing_url,
        "render": "false"
    }
    resp = requests.get("https://api.scraperapi.com", params=params)
    soup = BeautifulSoup(resp.text, "html.parser")
    directors = []
    for section in soup.find_all(["h2", "h3", "b", "strong"]):
        text = section.get_text()
        if any(kw in text.lower() for kw in ["director", "nominee", "board member"]):
            parent = section.find_parent()
            if parent:
                names = re.findall(r"([A-Z][a-z]+ (?:[A-Z]\. )?[A-Z][a-z]+)",
                                  parent.get_text())
                directors.extend(names)
    return list(set(directors))
Enter fullscreen mode Exit fullscreen mode

ScraperAPI handles SEC filing pages reliably.

Building the Board Network

import networkx as nx

def build_board_network(company_directors):
    G = nx.Graph()
    for company, directors in company_directors.items():
        G.add_node(company, type="company")
        for director in directors:
            G.add_node(director, type="director")
            G.add_edge(director, company)
    return G

board_data = {
    "Apple": ["Tim Cook", "Al Gore", "James Bell", "Andrea Jung"],
    "Alphabet": ["Sundar Pichai", "John Hennessy", "Frances Arnold"],
    "Microsoft": ["Satya Nadella", "John Thompson", "Emma Walmsley"],
}

G = build_board_network(board_data)
Enter fullscreen mode Exit fullscreen mode

Finding Board Interlocks

def find_interlocks(G):
    interlocks = []
    directors = [n for n, d in G.nodes(data=True) if d.get("type") == "director"]
    for director in directors:
        companies = list(G.neighbors(director))
        if len(companies) > 1:
            interlocks.append({
                "director": director,
                "companies": companies,
                "board_count": len(companies)
            })
    return sorted(interlocks, key=lambda x: x["board_count"], reverse=True)

for il in find_interlocks(G):
    print(f"  {il['director']}: {', '.join(il['companies'])}")
Enter fullscreen mode Exit fullscreen mode

Visualization

import matplotlib.pyplot as plt

def visualize_network(G):
    plt.figure(figsize=(16, 12))
    pos = nx.spring_layout(G, k=1.5, seed=42)
    companies = [n for n, d in G.nodes(data=True) if d.get("type") == "company"]
    directors = [n for n, d in G.nodes(data=True) if d.get("type") == "director"]
    nx.draw_networkx_nodes(G, pos, nodelist=companies,
                           node_color="#e74c3c", node_size=800)
    nx.draw_networkx_nodes(G, pos, nodelist=directors,
                           node_color="#3498db", node_size=400)
    nx.draw_networkx_edges(G, pos, alpha=0.3)
    nx.draw_networkx_labels(G, pos, font_size=8)
    plt.title("Corporate Board Network")
    plt.savefig("board_network.png", dpi=150)

visualize_network(G)
Enter fullscreen mode Exit fullscreen mode

Scale with ThorData proxies and monitor with ScrapeOps.

Key Takeaways

  • SEC proxy statements (DEF 14A) are the authoritative source for director data
  • NetworkX models bipartite company-director relationships
  • Board interlocks reveal hidden corporate influence networks
  • Regular monitoring tracks governance changes over time

SEC filings are public records. Respect EDGAR rate limits (10 requests/second max).

Top comments (0)