Home / Tutorials / Governance & compensation
Governance & compensation

Compare audit fees and auditors across companies

Pull audit, audit related, tax and other fees from proxy statements, plus the auditor of record, and compare non-audit fee share across companies.

Proxy statements disclose what a company paid its independent auditor, split into audit fees, audit related fees, tax fees and all other fees. /v1/audit-fees extracts that table; /v1/auditor returns the auditor of record from the annual report.

Four companies side by side

import os
import requests

BASE = "https://secfeedapi.com"
HEADERS = {"Authorization": f"Bearer {os.environ['SECFEED_KEY']}"}

def latest(values):
    """Values come as printed in the proxy: {year: amount}, or a list with the latest year first."""
    if isinstance(values, dict):
        y = max(values)
        return y, values[y]
    return "latest", values[0]

def normalize(fees):
    out = {}
    for label, values in fees.items():
        key = label.lower()
        if "total" in key:
            out["total"] = latest(values)
        elif "related" in key:
            out["audit_related"] = latest(values)
        elif key.startswith("audit"):
            out["audit"] = latest(values)
    return out

for ticker in ["NVDA", "MSFT", "AMZN", "JPM"]:
    fees = requests.get(f"{BASE}/v1/audit-fees", params={"ticker": ticker}, headers=HEADERS, timeout=120).json()
    auditor = requests.get(f"{BASE}/v1/auditor", params={"ticker": ticker}, headers=HEADERS, timeout=120).json()
    n = normalize(fees["fees"])
    year, audit = n["audit"]
    _, total = n["total"]
    print(f"{ticker:5} {auditor.get('auditorName', '?')[:26]:26} {year}  audit {audit:>14,.1f}  total {total:>14,.1f}  "
          f"unit {fees['unit'] or 'as printed'}  non-audit share {1 - audit / total:.1%}")

Output:

NVDA  PricewaterhouseCoopers LLP 2026  audit   10,166,400.0  total   13,361,322.0  unit as printed  non-audit share 23.9%
MSFT  DELOITTE & TOUCHE LLP      2025  audit   53,880,000.0  total   78,465,000.0  unit as printed  non-audit share 31.3%
AMZN  Ernst & Young LLP          2025  audit   47,260,000.0  total   59,256,965.0  unit as printed  non-audit share 20.2%
JPM   PricewaterhouseCoopers LLP 2025  audit           91.0  total          135.0  unit millions  non-audit share 32.6%

Normalizing the table

The fee table is printed differently by each company, and the API returns it as printed:

  • Labels vary. "Audit Fees", "Audit fees" and "Audit" all mean the same thing, as do "Total", "Total Fees" and "TOTAL FEES". The normalize function above maps them by keyword.
  • Units vary. Some companies print whole dollars, others thousands or millions. When the proxy states the unit, it is in unit (millions for JPM above). When unit is null, the values are exactly as printed, so ratios like the non-audit share are safe while absolute amounts need a check against the document.
  • Years. Most tables come back as {year: amount}. A few proxies print columns without year headers, and then values come as a list with the latest year first.

Why non-audit share matters

A high share of non-audit fees (tax and consulting work) is a long studied auditor independence signal. Audit committees disclose pre-approval policies for those services in the same section of the proxy.

Auditor of record

/v1/auditor reads AuditorName, AuditorLocation and AuditorFirmId (the PCAOB ID) from the annual report's XBRL, plus whether the auditor attested to internal control over financial reporting.

Next steps

More governance & compensation tutorials

Run this tutorial with your own key

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

Get free API key →