Developer API docs

Quickstarts

Copy-paste examples for the stock fundamentals API: curl, Python requests and pandas, Node fetch, a Claude Code MCP walkthrough, an OpenAI tool from OpenAPI.

Documentation updated 2026-09-02. Machine-readable: openapi.json · llms.txt

curl

Terminal
# Per-stock summary (JSON, keyless)
curl -s https://tgmcharts.com/api/v1/summary/AAPL

# 10 years of daily P/E as CSV
curl -s "https://tgmcharts.com/api/v1/series/KO/pe-ratio?years=10&format=csv" -o ko-pe-ratio.csv

# Five annual income statements as filed with the SEC
curl -s "https://tgmcharts.com/api/v1/statements/AAPL/income-statement?period=annual&years=5"

# A chart PNG
curl -s -o aapl-revenue.png https://tgmcharts.com/api/v1/charts/AAPL/revenue

# With a key: metered, X-RateLimit-* headers on the response
curl -s -D - -o /dev/null https://tgmcharts.com/api/v1/summary/AAPL \
  -H "Authorization: Bearer tgm_live_YOUR_KEY"

Python

requests for JSON, pandas straight from the CSV. Handle the honest states — points can be null, and a statement can carry a status instead of rows.

Python 3 · requests + pandas
import requests
import pandas as pd

BASE = "https://tgmcharts.com/api/v1"
HEADERS = {"User-Agent": "my-app/1.0 (data by TGMCharts)"}

# Summary
summary = requests.get(f"{BASE}/summary/AAPL", headers=HEADERS, timeout=30).json()
print(summary["name"], summary["valuation"]["peRatio"], summary["asOf"]["dataUpdatedAt"])

# Daily P/E history, 10 years
series = requests.get(f"{BASE}/series/KO/pe-ratio", params={"years": 10}, headers=HEADERS, timeout=30).json()
if series["points"] is None:
    print("withheld:", series["applicability"]["message"])   # not meaningful for the company type
else:
    for point in series["points"][-3:]:
        print(point["date"], point["value"])

# The same series as a DataFrame (the '#' lines are provenance comments)
df = pd.read_csv(f"{BASE}/series/KO/pe-ratio?years=10&format=csv", comment="#", parse_dates=["date"])
print(df.tail())

# Annual cash-flow statement as filed with the SEC, with per-value provenance
stmt = requests.get(f"{BASE}/statements/AAPL/cash-flow", params={"period": "annual", "years": 5}, headers=HEADERS, timeout=30).json()
if stmt.get("status"):                      # "preparing" or "unavailable" — rows is null
    print(stmt["status"], stmt.get("note"))
else:
    for row in stmt["rows"]:
        prov = row["provenance"].get("operatingCashFlow")
        print(row["fiscalYear"], row["metrics"]["operatingCashFlow"], prov["accn"] if prov else "not filed")

Node.js

Node 18+ · fetch
const BASE = "https://tgmcharts.com/api/v1";

const summary = await fetch(`${BASE}/summary/AAPL`).then((r) => r.json());
console.log(summary.name, summary.valuation.peRatio, summary.asOf.dataUpdatedAt);

// Balance sheet as filed with the SEC (JSON); rows is null while status is set
const statement = await fetch(`${BASE}/statements/AAPL/balance-sheet?period=annual&years=3`).then((r) => r.json());
if (statement.status) {
  console.log(statement.status, statement.note);           // "preparing" | "unavailable"
} else {
  for (const row of statement.rows) {
    console.log(row.fiscalYear, row.metrics.totalAssets, row.provenance.totalAssets?.tag ?? "not filed");
  }
}

// Keyed call: metered, X-RateLimit-* headers on the response
const keyed = await fetch(`${BASE}/series/KO/dividend-yield?years=5`, {
  headers: { Authorization: `Bearer ${process.env.TGMCHARTS_API_KEY}` },
});
console.log(keyed.status, keyed.headers.get("x-ratelimit-remaining"));
const series = await keyed.json();
console.log(series.methodology, series.points?.length ?? "withheld");

Claude Code and the MCP server

Three steps: mint a free key, register the server, ask. Claude picks the tool — compare_stocks for a side-by-side, get_metric_series for a history, get_chart for an embeddable PNG URL — and the results carry as-of dates and honest nulls it can cite. Details on the MCP page.

Terminal
# 1. Mint a free key (sign in, "Create key"): https://tgmcharts.com/developers/keys

# 2. Register the server — the key rides in the Authorization header
claude mcp add --transport http tgmcharts https://tgmcharts.com/api/v1/mcp \
  --header "Authorization: Bearer tgm_live_YOUR_KEY"

# 3. In Claude Code, ask, for example:
#    "Compare KO and PEP on operating margin and dividend yield,
#     then give me KO's 10-year P/E chart."
#    -> compare_stocks, then get_chart returns
#       https://tgmcharts.com/api/v1/charts/KO/pe-ratio?years=10
Same call over raw JSON-RPC
curl -s https://tgmcharts.com/api/v1/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer tgm_live_YOUR_KEY" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"compare_stocks","arguments":{"symbols":["KO","PEP"]}}}'

OpenAI tool definition from the OpenAPI document

The OpenAPI document enumerates every series slug, so a function tool can be built from it instead of hand-typing the enum. The example defines one tool for the series endpoint and executes the model's call against the API.

Python 3 · openai + requests
import json
import requests
from openai import OpenAI

BASE = "https://tgmcharts.com/api/v1"
spec = requests.get(f"{BASE}/openapi.json", timeout=30).json()
series_slugs = [m["slug"] for m in spec["x-tgmcharts-metrics"] if m["series"]]

tools = [{
    "type": "function",
    "function": {
        "name": "get_metric_series",
        "description": spec["paths"]["/api/v1/series/{symbol}/{metric}"]["get"]["summary"],
        "parameters": {
            "type": "object",
            "properties": {
                "symbol": {"type": "string", "description": "Ticker symbol, e.g. AAPL"},
                "metric": {"type": "string", "enum": series_slugs},
                "years": {"type": "integer", "minimum": 2, "maximum": 20},
            },
            "required": ["symbol", "metric"],
        },
    },
}]


def get_metric_series(symbol: str, metric: str, years: int | None = None) -> dict:
    params = {"years": years} if years else {}
    return requests.get(f"{BASE}/series/{symbol}/{metric}", params=params, timeout=30).json()


client = OpenAI()
messages = [{"role": "user", "content": "How has KO's P/E ratio moved over the last 10 years?"}]
first = client.chat.completions.create(model="gpt-4o", messages=messages, tools=tools)
call = first.choices[0].message.tool_calls[0]
result = get_metric_series(**json.loads(call.function.arguments))

messages += [
    first.choices[0].message,
    {"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)},  # carries methodology + asOf
]
answer = client.chat.completions.create(model="gpt-4o", messages=messages, tools=tools)
print(answer.choices[0].message.content)

Attribution

Wherever the data or charts appear, name TGMCharts and link tgmcharts.com — "data by TGMCharts". Chart PNGs carry it in the frame already. Cache responses for at least an hour; the data changes at most daily. See authentication and limits for keys, quotas and the API terms.

FAQ

Is there a Python example for the stock fundamentals API?
Yes — the quickstart uses requests for the summary, series and statements endpoints and pandas.read_csv with comment="#" for the CSV series. No SDK is needed; every endpoint is plain HTTPS returning JSON, CSV or PNG.
How do I give an AI agent access to the data?
Two ways: connect the MCP server (claude mcp add --transport http tgmcharts https://tgmcharts.com/api/v1/mcp, with a free key in the Authorization header), or build tool definitions from the OpenAPI document at /api/v1/openapi.json for OpenAI-style function calling.