Home / Guides / Boards & pay
Boards & pay

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 total over 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 / total per executive to compare how much of the pay is at risk.
  • Directors. Board pay is a separate table, see Directors and board members.

Next steps

More boards & pay guides

Run this guide with your own key

500 free API calls a month. Every example on this page works on the free plan.

Get free API key →