Practical recipes in Python (requests + pandas). Every call needs your API key as a bearer
token; returns come back as decimal fractions (0.0523 = +5.23%) — see Methodology.
The /batch endpoints take up to 100 codes and count as a single request against your quota.
Code
import pandas as pdcodes = ["SPY", "QQQ", "VTI", "IWM", "DIA"]r = session.post(f"{BASE}/v1/trailing-returns/batch", json={"codes": codes})r.raise_for_status()df = pd.DataFrame(r.json()).set_index("code")# show a few periods as percentagescols = ["return_1y", "return_3y", "return_5y", "return_earliest_available"]print((df[cols] * 100).round(2))
Unknown codes are omitted from the response (no error), so compare df.index against codes
to spot any misses.
Calendar-year returns
Code
# one fund, one yearsession.get(f"{BASE}/v1/funds/QQQ/calendar-returns", params={"year": 2022}).json()# many funds, one yearsession.post(f"{BASE}/v1/calendar-returns/batch", json={"codes": codes, "year": 2022}).json()
Search / list the universe (with pagination)
GET /v1/funds uses cursor pagination — loop until next_cursor is null (limit max 1000):
Code
def all_funds(**filters): cursor = None while True: params = {"limit": 1000, **filters} if cursor: params["cursor"] = cursor page = session.get(f"{BASE}/v1/funds", params=params).json() yield from page["data"] cursor = page.get("next_cursor") if not cursor: breaketfs = list(all_funds(type="ETF", q="s&p 500"))print(len(etfs), etfs[0]["code"], etfs[0]["name"])
Compare two funds
Code
a, b = "SPY", "QQQ"rows = session.post(f"{BASE}/v1/trailing-returns/batch", json={"codes": [a, b]}).json()by_code = {row["code"]: row for row in rows}for period in ["return_1y", "return_5y", "return_10y"]: print(period, by_code[a][period], "vs", by_code[b][period])