Quickstart
Go from zero to a live data call in under a minute. Your tengu_ key works across the FIRM data API
(firm.tengu.co) and the Brain agent API (brain.tengu.co) — one key, one shared credit wallet.
Quick reference#
| Base URL | Auth | Billing | |
|---|---|---|---|
| FIRM data API | https://firm.tengu.co | Authorization: Bearer tengu_... (or X-API-Key) | flat credits per call |
| Brain agent API | https://brain.tengu.co/v1 | Authorization: Bearer tengu_... | credits per token |
Both surfaces accept the same tengu_ key and debit the same credit wallet. Every FIRM response is a
JSON envelope: { "ok": true, "timestamp": "<ISO-8601>", ...data }.
Using an AI agent or an SDK? Every tool is also reachable over the Model Context Protocol (Claude & other agents) and generated SDKs — same key, same wallet. See Connect via MCP & SDKs.
1. Get your key#
- In the dashboard, open Tengu API → Overview and reveal your key (it looks like
tengu_...). Copy it once and store it like a password. - Export it so the snippets below work:
export TENGU_API_KEY="tengu_..."2. Authentication#
Both APIs accept the same key three ways, in this precedence (first one present wins):
Authorization: Bearer tengu_...— recommended; works unchanged with the OpenAI/Anthropic SDKs.X-API-Key: tengu_...?api_key=tengu_...query parameter.
These three are equivalent:
1curl -H "Authorization: Bearer $TENGU_API_KEY" "https://firm.tengu.co/api/v3/intel/congress"2curl -H "X-API-Key: $TENGU_API_KEY" "https://firm.tengu.co/api/v3/intel/congress"3curl "https://firm.tengu.co/api/v3/intel/congress?api_key=$TENGU_API_KEY"3. Your first data call (works on every paid plan)#
Pull the latest congressional trades (in Starter and up, 2 credits/call):
1curl -H "Authorization: Bearer $TENGU_API_KEY" \2 "https://firm.tengu.co/api/v3/intel/congress"1import os, requests2 3r = requests.get(4 "https://firm.tengu.co/api/v3/intel/congress",5 headers={"Authorization": f"Bearer {os.environ['TENGU_API_KEY']}"},6 timeout=30,7)8r.raise_for_status()9data = r.json() # envelope: {"ok": true, "timestamp": ..., "items": [...], "count": N}10print(data["items"][:3])Each call debits a flat per-product credit cost from your wallet (congressional = 2 credits). See your
remaining balance any time on the Tengu API → Overview page. 401, 402, 403, 404, 429, and 502
responses never debit credits.
4. A few real endpoints to try#
All are GET unless noted. {ticker} is a symbol like AAPL. Every response is envelope-wrapped
({"ok": true, "timestamp": ..., ...}). Full list: API reference or GET /api/capabilities.
1# Live quote — market_data (1 credit) → {"ok":true, "quote": {...}}2curl -H "Authorization: Bearer $TENGU_API_KEY" "https://firm.tengu.co/api/market/quote/AAPL"3 4# SEC filings index — public_filings (1 credit) → {"ok":true, "ticker":"AAPL", "items":[...]}5curl -H "Authorization: Bearer $TENGU_API_KEY" \6 "https://firm.tengu.co/api/v3/fundamentals/sec_filings?ticker=AAPL&limit=10"7 8# 13F institutional holdings — funds (4 credits) → {"ok":true, "items":[...], "count":N}9curl -H "Authorization: Bearer $TENGU_API_KEY" "https://firm.tengu.co/api/v3/intel/sec13f?ticker=AAPL"10 11# Search private companies / investors / funds — FREE (0 credits, discovery) → {"ok":true, "results":[...]}12curl -H "Authorization: Bearer $TENGU_API_KEY" "https://firm.tengu.co/api/v3/private_markets/search?q=stripe"13 14# Full private-company profile — private_markets (5 credits, Pro+) — use an id from search above15curl -H "Authorization: Bearer $TENGU_API_KEY" "https://firm.tengu.co/api/v3/private_markets/company/{company_id}"Free discovery, paid detail. /api/v3/private_markets/search is always free (it's the name→id typeahead).
The paid detail (5 credits, Pro+) is /api/v3/private_markets/company/{company_id} and the other
/api/v3/private_markets/* routes. Search first to get an id, then fetch the profile.
Which products your plan unlocks is in Pricing & credits. Calling a product
your plan doesn't include returns 402 plan_required (no credits charged) — the body tells you which
plan unlocks it. Upgrade in Tengu API → Plans.
5. Production-ready client (drop-in, with retries + error handling)#
This is the reference implementation — copy it as-is. It handles auth, the credit-cost-aware envelope, rate
limits (honors Retry-After), plan/credit errors, and transient key-service outages.
1import os, time, requests2 3FIRM_BASE = "https://firm.tengu.co"4KEY = os.environ["TENGU_API_KEY"]5SESSION = requests.Session()6SESSION.headers["Authorization"] = f"Bearer {KEY}"7 8class TenguError(RuntimeError):9 def __init__(self, status, code, message):10 super().__init__(f"[{status} {code}] {message}")11 self.status, self.code, self.message = status, code, message12 13def firm_call(path, params=None, method="GET", json_body=None, max_retries=4):14 """Call the FIRM data API. Returns the parsed JSON envelope, or raises TenguError.15 Retries only on 429 (rate limit) and 502 (key-service blip); never on 401/402/403/404."""16 url = f"{FIRM_BASE}{path}"17 for attempt in range(max_retries):18 resp = SESSION.request(method, url, params=params, json=json_body, timeout=30)19 if resp.status_code == 200:20 return resp.json() # {"ok": true, "timestamp": ..., ...data}21 # Error bodies are always {"detail": {"error_code": ..., "error_message": ...}}22 detail = (resp.json() or {}).get("detail", {}) if resp.content else {}23 code = detail.get("error_code", "unknown")24 msg = detail.get("error_message", resp.reason)25 if resp.status_code in (429, 502) and attempt < max_retries - 1:26 wait = int(resp.headers.get("Retry-After", 60 if resp.status_code == 429 else 5))27 time.sleep(min(wait * (2 ** attempt), 120)) # honor Retry-After, then back off28 continue29 # 401 (bad key), 402 (plan_required / usage_exceeded), 403, 404 → not retryable30 raise TenguError(resp.status_code, code, msg)31 raise TenguError(resp.status_code, code, msg)32 33# usage34trades = firm_call("/api/v3/intel/congress")["items"]35print(trades[:3])1// TypeScript / Node (fetch). Same contract: Bearer auth, envelope responses, Retry-After backoff.2const FIRM_BASE = "https://firm.tengu.co";3const KEY = process.env.TENGU_API_KEY!;4 5export async function firmCall<T = any>(path: string, init: RequestInit = {}, maxRetries = 4): Promise<T> {6 const url = `${FIRM_BASE}${path}`;7 for (let attempt = 0; attempt < maxRetries; attempt++) {8 const resp = await fetch(url, { ...init, headers: { Authorization: `Bearer ${KEY}`, ...init.headers } });9 if (resp.ok) return (await resp.json()) as T; // { ok: true, timestamp, ...data }10 const detail = (await resp.json().catch(() => ({}))).detail ?? {};11 if ((resp.status === 429 || resp.status === 502) && attempt < maxRetries - 1) {12 const wait = Number(resp.headers.get("Retry-After") ?? (resp.status === 429 ? 60 : 5));13 await new Promise((r) => setTimeout(r, Math.min(wait * 2 ** attempt, 120) * 1000));14 continue;15 }16 throw new Error(`[${resp.status} ${detail.error_code ?? "unknown"}] ${detail.error_message ?? resp.statusText}`);17 }18 throw new Error("exhausted retries");19}6. Discover every endpoint (no key required)#
GET /api/capabilities returns the live manifest — every route, its method, and params. Use it to plan calls
before you write them:
1curl "https://firm.tengu.co/api/capabilities" | jq '{manifest_version, tool_count, groups}'2# tools[] entries carry: name, method, path, display_name, group (v2/v3), deprecated7. The Brain agent API (Enterprise)#
Brain is an OpenAI/Anthropic-compatible agent that orchestrates FIRM tools for you — you write no
tool-calling code. Brain requires the Enterprise plan (it has autonomous access to the full FIRM product
suite). On any lower plan it returns 402 plan_required. Pro and below should call the FIRM data endpoints
directly (§3–5).
Point your existing OpenAI SDK at https://brain.tengu.co/v1 and authenticate with the standard
Authorization: Bearer header:
1from openai import OpenAI2client = OpenAI(api_key=os.environ["TENGU_API_KEY"], base_url="https://brain.tengu.co/v1")3 4resp = client.chat.completions.create(5 model="brain",6 messages=[{"role": "user",7 "content": "What did Nancy Pelosi trade recently, and how did NVDA move around those dates?"}],8)9print(resp.choices[0].message.content)That one request may internally pull congressional trades + market data — you orchestrate none of it. Brain is
stateless per request (send the full messages[] history to continue a conversation), supports streaming
(stream=true), and also exposes the Anthropic shape at POST /v1/messages. It debits the shared wallet by
token (one metered event per turn, regardless of how many FIRM tools it called internally).
8. Errors you'll see#
Every error body is {"detail": {"error_code": "...", "error_message": "...", ...}}. None of these deduct credits.
| Status | error_code | Meaning | What to do |
|---|---|---|---|
401 | unauthorized | Key missing/invalid/disabled. | Check the key; rotate under Tengu API → Overview. |
402 | plan_required | Product not in your plan. Body adds tool_group, current_plan, unlocked_by[], upgrade_url. | Upgrade to a plan in unlocked_by. No credits charged. |
402 | usage_exceeded | Wallet empty / overage cap reached. | Top up or upgrade. No credits charged. |
403 | account_suspended | Billing lapsed. | Resolve billing in the dashboard. |
404 | not_found | Unknown route or symbol. | Check the path against GET /api/capabilities. |
429 | rate_limited | Plan rate limit exceeded. | Wait Retry-After seconds (60), then retry with backoff. |
502 | unkey_unreachable | Key service blip (transient). | Retry after Retry-After seconds (5). |
For Claude Code & AI agents#
If you're an agent setting this up for a user, this is all you need:
- Config:
TENGU_API_KEYfrom env (never hardcode). FIRM basehttps://firm.tengu.co; Brain basehttps://brain.tengu.co/v1. Auth:Authorization: Bearer $TENGU_API_KEYon both. - Discovery:
GET https://firm.tengu.co/api/capabilities(no key needed) is the authoritative route/param list — read it before generating calls instead of guessing paths. - Responses: every FIRM response is
{"ok": true, "timestamp": ..., ...data}. The data keys differ per endpoint (e.g.itemsfor feeds,resultsfor search,quotefor a quote). Don't assume a fixed key — inspect the envelope. - Retry policy: retry ONLY
429(waitRetry-After≈60s) and502(waitRetry-After≈5s), with exponential backoff capped at 120s. Do not retry401/402/403/404— surface them to the user (they need a key, an upgrade, or a fixed path). - Budget: each FIRM call costs a fixed number of credits by product (1–10; see Pricing & credits).
searchand/api/capabilitiesare free. Brain costs ~1 credit/token. A402 plan_requiredbody'sunlocked_by[]tells the user exactly which plan to buy. - Use the §5 client verbatim — it already encodes the envelope, error shapes, and backoff correctly.
Next steps#
- API reference — every endpoint, per-plan access, credit costs, and error shapes.
- Pricing & credits — what each plan includes, add-ons, and what each call costs.
- Chat parity — make Brain feel like Tengu Chat with optional context blocks.