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 URLAuthBilling
FIRM data APIhttps://firm.tengu.coAuthorization: Bearer tengu_... (or X-API-Key)flat credits per call
Brain agent APIhttps://brain.tengu.co/v1Authorization: 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#

  1. In the dashboard, open Tengu API → Overview and reveal your key (it looks like tengu_...). Copy it once and store it like a password.
  2. Export it so the snippets below work:
Shell
export TENGU_API_KEY="tengu_..."

2. Authentication#

Both APIs accept the same key three ways, in this precedence (first one present wins):

  1. Authorization: Bearer tengu_...recommended; works unchanged with the OpenAI/Anthropic SDKs.
  2. X-API-Key: tengu_...
  3. ?api_key=tengu_... query parameter.

These three are equivalent:

Shell
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):

Shell
1curl -H "Authorization: Bearer $TENGU_API_KEY" \2  "https://firm.tengu.co/api/v3/intel/congress"
Python
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.

Shell
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.

Python
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])
TypeScript
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:

Shell
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), deprecated

7. 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:

Python
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.

Statuserror_codeMeaningWhat to do
401unauthorizedKey missing/invalid/disabled.Check the key; rotate under Tengu API → Overview.
402plan_requiredProduct not in your plan. Body adds tool_group, current_plan, unlocked_by[], upgrade_url.Upgrade to a plan in unlocked_by. No credits charged.
402usage_exceededWallet empty / overage cap reached.Top up or upgrade. No credits charged.
403account_suspendedBilling lapsed.Resolve billing in the dashboard.
404not_foundUnknown route or symbol.Check the path against GET /api/capabilities.
429rate_limitedPlan rate limit exceeded.Wait Retry-After seconds (60), then retry with backoff.
502unkey_unreachableKey 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_KEY from env (never hardcode). FIRM base https://firm.tengu.co; Brain base https://brain.tengu.co/v1. Auth: Authorization: Bearer $TENGU_API_KEY on 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. items for feeds, results for search, quote for a quote). Don't assume a fixed key — inspect the envelope.
  • Retry policy: retry ONLY 429 (wait Retry-After≈60s) and 502 (wait Retry-After≈5s), with exponential backoff capped at 120s. Do not retry 401/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). search and /api/capabilities are free. Brain costs ~1 credit/token. A 402 plan_required body's unlocked_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.