Compare executive compensation across companies
Build one table of CEO and named executive officer pay for several companies from their proxy statements, with salary, stock awards, totals and pay ratios.
Every proxy statement (DEF 14A) contains a Summary Compensation Table: salary, bonus, stock and option awards, non-equity incentive pay, other compensation and a total, for each named executive officer (NEO), usually for three fiscal years. /v1/executive-compensation returns that table as JSON for any ticker. This tutorial puts five companies side by side.
The response
Each call returns the proxy's accession number and one row per executive per year:
{"name": "Satya Nadella Chairman and Chief Executive Officer", "year": 2025, "salary": 2500000.0,
"bonus": 0.0, "stockAwards": 84245496.0, "nonEquityIncentive": 9555000.0, "allOther": 196294.0,
"total": 96496790.0}
The name field holds the name and title as printed in the table, so the script splits them.
The script
import os
import re
import statistics
import requests
BASE = "https://secfeedapi.com"
HEADERS = {"Authorization": f"Bearer {os.environ['SECFEED_KEY']}"}
TICKERS = ["MSFT", "AAPL", "NVDA", "GOOGL", "AMZN"]
TITLE = re.compile(r"\b(Chair|Chairman|Chief|President|Executive|Senior|Vice|CEO|CFO|Former|General|Co-Founder|Founder)\b")
def split_name(s):
"""'Satya Nadella Chairman and Chief Executive Officer' -> ('Satya Nadella', 'Chairman and Chief ...')"""
m = TITLE.search(s)
return (s[:m.start()].strip(" ,"), s[m.start():]) if m else (s, "")
rows = []
for t in TICKERS:
d = requests.get(f"{BASE}/v1/executive-compensation", params={"ticker": t}, headers=HEADERS, timeout=120).json()
execs = d["executives"]
year = max(e["year"] for e in execs)
latest = [e for e in execs if e["year"] == year] # the most recent fiscal year in the table
ceo = latest[0] # the principal executive officer is listed first
rows.append({
"ticker": t, "year": year, "ceo": split_name(ceo["name"])[0], "salary": ceo["salary"],
"stock": ceo["stockAwards"], "ceo_total": ceo["total"], "neos": len(latest),
"median_neo": statistics.median(e["total"] for e in latest[1:]) if len(latest) > 1 else None,
})
m = lambda v: f"{v / 1e6:,.1f}" if v is not None else "-"
print(f"{'ticker':<7}{'FY':<6}{'CEO':<20}{'salary':>8}{'stock':>8}{'total':>8}{'NEOs':>6}{'median other':>14}")
for r in sorted(rows, key=lambda r: -r["ceo_total"]):
print(f"{r['ticker']:<7}{r['year']:<6}{r['ceo'][:19]:<20}{m(r['salary']):>8}{m(r['stock']):>8}{m(r['ceo_total']):>8}"
f"{r['neos']:>6}{m(r['median_neo']):>14}")
print("\n$ millions. 'median other' = median total pay of the other named executive officers.")
for r in rows:
if r["median_neo"]:
print(f"{r['ticker']}: CEO pay is {r['ceo_total'] / r['median_neo']:.1f}x the median of the other named executives")
Output
ticker FY CEO salary stock total NEOs median other
MSFT 2025 Satya Nadella 2.5 84.2 96.5 5 28.2
AAPL 2025 Tim Cook 3.0 57.5 74.3 6 27.0
NVDA 2026 Jen-Hsun Huang 1.5 24.8 36.3 5 14.3
GOOGL 2025 Sundar Pichai 2.0 0.0 10.9 5 30.2
AMZN 2025 Andrew R. Jassy 0.4 0.0 2.1 6 0.4
$ millions. 'median other' = median total pay of the other named executive officers.
MSFT: CEO pay is 3.4x the median of the other named executives
AAPL: CEO pay is 2.7x the median of the other named executives
NVDA: CEO pay is 2.5x the median of the other named executives
GOOGL: CEO pay is 0.4x the median of the other named executives
AMZN: CEO pay is 4.9x the median of the other named executives
Reading it
- Stock awards drive the totals. Salaries sit between $0.4 million and $3 million. The spread comes from equity grants, valued at grant date.
- Grant cycles distort single years. Alphabet and Amazon grant large CEO awards every few years, not annually. In a year without a grant the CEO's total is small and the CEO to NEO ratio can drop below 1, as it does for Alphabet here. Compare three year sums, or look at pay versus performance data, before drawing conclusions.
- Fiscal years differ. NVIDIA's fiscal year 2026 ended in January 2026, so its latest table is a year "ahead" of the others.
- The NEO count varies. Companies list the CEO, the CFO and the three other highest paid executive officers, plus anyone who left during the year, so you will see 5 or 6 rows.
Variations
- Three year totals. Group by name and sum
totalover all years in the response. - A whole index. Loop over a list of tickers. Each call takes about a second; cache the results, since proxies change once a year.
- Pay mix. Compute
stockAwards / totalper executive to compare how much of the pay is at risk. - Directors. Board pay is a separate table, see Directors and board members.