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
# 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.
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
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.
# 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=10curl -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.
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.