Skip to content

THE ENTITY Data · Developer API

The data behind THE ENTITY, as an API

Read-only access to the four datasets that power the platform — the causal supply-chain graph, the computed engine, the tracked-elite smart-money panel, and market feeds& cited filings — over REST, MCP, Sheets and bulk export. Authenticate with an entk_live_ Bearer key; every call is metered and tiered. 29 endpoints, one stable envelope, no PII, no writes.

Building an AI agent? This is the MCP server for AI supply-chain data — the graph, engine and smart-money as native tools for Claude, Cursor or your own agent.

RESTMCPSheets / ExcelBulk exportOpenAPI 3.1

Overview

THE ENTITY Data sells programmatic, read-only access to four datasets. Every dataset is exposed over the same conventions, and the same key can reach any dataset its scopes allow.

  • graphThe causal value-chain graph — nodes, typed relations, company edges, facilities, chokepoints and the layer taxonomy.
  • engineComputed exhibits and the daily record — nowcast, min-cut, seeded Monte-Carlo forecast, conviction and node-score history.
  • smart-moneyThe tracked-elite 13F panel — roster, consensus, the archive tape, clone book, fund books and company holder rosters. Counts are of the TRACKED panel, not institutions at large.
  • feedsMarket microstructure and grounded citations — insider trades/positions, ownership events, short interest/volume, and verbatim cited chain mentions + corroborated extracted metrics.

Everything is read-only (only GET), carries no personal data, and returns the stable envelope described below. The base URL is https://joinentity.com/api/v1.

Recipes

Four things people build with THE ENTITY Data. Each is a few calls with the same entk_live_ key — copy, swap in your key, and go.

Supply-chain risk dashboard

Where is the AI chain tightest, and who’s exposed? Join the structural pinch points, the live tightness index, and where the tracked elite are crowding.

Endpoints: /graph/chokepoints · /engine/nowcast · /smart-money/consensus

# the structural pinch points, ranked
curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/graph/chokepoints?sort=exposureScore&order=desc&limit=10"
# system tightness right now (0-100, per layer)
curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/engine/nowcast"
# names ≥3 tracked-elite funds hold, joined to the verdict
curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/smart-money/consensus?min_holders=3"

Feed an AI agent (MCP)

Point Claude, Cursor or your own agent at the MCP server — it gets the graph, engine and smart-money reads as native tools, authenticated by your key.

Endpoint: /mcp (Streamable HTTP, JSON-RPC 2.0)

// e.g. an MCP client config
{
  "mcpServers": {
    "the-entity": {
      "url": "https://joinentity.com/api/v1/mcp",
      "headers": { "Authorization": "Bearer $ENTITY_KEY" }
    }
  }
}

13F consensus in your model

Pull the tracked-elite consensus and any fund’s archived book straight into a notebook or sheet. Counts are of the tracked panel — labeled, never inflated.

Endpoints: /smart-money/consensus · /smart-money/funds/{cik}/holdings

import requests
H = {"Authorization": f"Bearer {KEY}"}
consensus = requests.get("https://joinentity.com/api/v1/smart-money/consensus",
                         headers=H, params={"min_holders": 3}).json()["data"]
# one fund's archived 13F book across quarters
book = requests.get("https://joinentity.com/api/v1/smart-money/funds/1067983/holdings",
                    headers=H).json()["data"]

Screen for accumulation the engine backs

One call: companies the tracked elite are net-accumulating andthe engine has conviction on — ranked by net adds. The platform’s core thesis as a screener.

Endpoint: /screener/accumulation (or compose /smart-money/tape + /engine/node-scores yourself)

# net-accumulated + engine conviction ≥ 60, most-added first
curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/screener/accumulation?min_holders=3&min_conviction=60"

Prefer no code? The same data lands in Sheets & Excel and as bulk CSV / NDJSON.

For AI agents

THE ENTITY Data is built to be an agent’s tool for the AI supply chain. The MCP serverexposes the graph, engine and smart-money reads as native tools — your assistant calls them directly, authenticated by the user’s entk_live_ key, read-only (no writes, no trades). Point Claude, Cursor or your own agent at it:

{
  "mcpServers": {
    "the-entity": {
      "url": "https://joinentity.com/api/v1/mcp",
      "headers": { "Authorization": "Bearer $ENTITY_KEY" }
    }
  }
}
  • Transport: Streamable HTTP, JSON-RPC 2.0 at https://joinentity.com/api/v1/mcp.
  • Tools = the read + personal surface of THE ENTITY (search the graph, pull smart-money, engine exhibits, feeds). Never propose/admin, never a trade.
  • Every result carries the same honesty labels as the REST envelope (as_of vs detected_at, tracked-panel counts).

Answer engines (ChatGPT, Perplexity, Claude, Google AI Overviews) can also discover the platform via /llms.txt.

Authentication

Every data request needs a key, sent as a bearer token (the x-api-key header is also accepted). Keys are minted in your account settings after subscribing — see pricing. Keys are prefixed entk_live_.

Test a key end-to-end against /whoami — it runs the full authorizer and returns your tier, scopes and live usage (and consumes exactly one metered call):

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/whoami"

Tiers

Every tier reaches the same datasets; they differ only by monthly call quota, per-minute rate, key cap and the surfaces they may use. bulk is a Pro/Scale surface.

TierPriceMonthly callsRate / minMax keysSurfaces
ENTITY Data · Starter$79/mo10,000602rest, mcp
ENTITY Data · Pro$149/mo100,00030010rest, mcp, bulk
ENTITY Data · Scale$499/mo1,000,00060025rest, mcp, bulk

Quota is an account pool across all your keys. The values above are the live tier table — never hardcoded here.

Rate & quota headers

Every authorized reply — success, degraded read, or a 429 — carries the rate and quota state, so you never have to guess how much headroom is left.

HeaderMeaning
x-ratelimit-limitPer-minute request ceiling for the account.
x-ratelimit-remainingRequests left in the current minute window.
x-ratelimit-resetUnix time (seconds) when the rate window resets.
x-quota-limitMonthly call allowance for the tier.
x-quota-remainingCalls left in the monthly allowance.
x-quota-resetISO timestamp when the monthly quota resets.
retry-afterSeconds to wait before retrying (present on 429).

Both throttles surface as 429 but mean different things: rate_limited is the per-minute ceiling (retry after retry-after seconds); quota_exceeded is the monthly allowance (resets at x-quota-reset).

Response envelope

Every data endpoint returns the same shape. Clients branch on the top-level ok.

{
  "ok": true,
  "data": ... ,                 // object | array | null (null only when degraded)
  "page": {                     // present on paginated collections
    "limit": 50, "offset": 0,
    "count": 50, "total": 812,
    "hasMore": true, "nextOffset": 50
  },
  "as_of": "2026-06-30",        // the data's own effective/observation date (ISO) or null
  "detected_at": "2026-07-02",  // when WE ingested it (ISO) or null — NEVER conflated with as_of
  "meta": { ... }               // honesty labels: tracked_funds, display_only/source, degraded, …
}

On a gate failure the body is instead { "ok": false, "error": "…" } with the matching status. Pagination is capped server-side: ask for limit=99999 and you get 200 rows, not an error.

Endpoints

29 endpoints across the four datasets. The full machine spec, with every parameter and schema, is at /api/v1/openapi.json.

Query power — on every endpoint
  • ?fields=a,b,c — keep only those top-level fields in each object (smaller payloads).
  • ?sort=field + ?order=asc|desc — order a listed collection (nulls last), applied before paging.
  • GET /search?q=… — ranked full-text search across nodes, domains, themes and companies; follow each hit’s id into its typed endpoint.
curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/graph/nodes?layerId=chips&sort=title&fields=id,title,layerId&limit=5"

Graph graph

The causal value-chain graph — nodes, typed relations, company edges, facilities, chokepoints and the layer taxonomy.

GET/search

Full-text search across the graph — nodes, domains, themes and companies (ranked).

Params: q (required), limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/search?q=hbm"
GET/graph/layers

The five value-chain layers and their domains (full taxonomy).

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/graph/layers"
GET/graph/nodes

List causal-graph nodes (summary projection), filterable + paginated.

Params: layerId, domainId, status, complexity, timeHorizon, q, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/graph/nodes?layerId=chips&limit=20"
GET/graph/nodes/{id}

One node, full detail (all summaries, watch items, metrics, sources).

Params: id

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/graph/nodes/{id}"
GET/graph/edges

Company↔company relationships (supplier/customer/partner/competitor).

Params: fromCompanyId, toCompanyId, company, type, nodeId, confidenceTier, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/graph/edges?company=nvidia"
GET/graph/relations

Typed node→node causal relations (strength, elasticity, latency).

Params: sourceNodeId, targetNodeId, nodeId, type, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/graph/relations?nodeId=foundry-capacity"
GET/graph/facilities

Physical sites (fabs, plants, ports) geolocated + hooked to nodes.

Params: companyId, geographyId, nodeId, kind, status, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/graph/facilities?geographyId=taiwan"
GET/graph/chokepoints

Structural pinch points (straits, ports, jurisdictions) + affected nodes.

Params: kind, nodeId, geographyId, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/graph/chokepoints?kind=strait"
GET/graph/companies/{id}/exposures

One company's exposures to chain nodes (intensity 0–100).

Params: id, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/graph/companies/{id}/exposures"
GET/cited/chain-mentions

Grounded, verbatim value-chain mentions from earnings calls (cited, source-linked).

Params: company (required), node, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/cited/chain-mentions?company=NVDA&node=hbm"
GET/cited/extracted-metrics

Corroboration-gated metric series extracted from cited sources (uncorroborated suppressed).

Params: node, metric, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/cited/extracted-metrics?node=cowos"

Engine engine

Computed exhibits and the daily record — nowcast, min-cut, seeded Monte-Carlo forecast, conviction and node-score history.

GET/engine/nowcast

Constraint Nowcast — system tightness index (0–100), per-layer + movers.

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/engine/nowcast"
GET/engine/min-cut

Min cut of the Energy→Applications flow network (structural exhibit).

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/engine/min-cut"
GET/engine/forecast

Probabilistic bottleneck-migration forecast (seeded Monte-Carlo).

Params: horizonQuarters, runs

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/engine/forecast?horizonQuarters=6"
GET/engine/conviction

Conviction record from D1 (per-company score/verdict/drivers).

Params: company, date, limit

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/engine/conviction?company=nvidia"
GET/engine/node-scores

Daily node-score record from D1 (global/tension/news/timing).

Params: node, days, from, to

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/engine/node-scores?node=hbm&days=30"

Smart Money smart-money

The tracked-elite 13F panel — roster, consensus, the archive tape, clone book, fund books and company holder rosters. Counts are of the TRACKED panel, not institutions at large.

GET/screener/accumulation

DERIVED screen: companies the tracked elite are net-accumulating AND the engine agrees (ranked).

Params: min_holders, min_adding, min_conviction, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/screener/accumulation?min_holders=3&min_conviction=60"
GET/smart-money/funds

The tracked-elite fund roster (optionally scored), + a labeled platform sidecar.

Params: scores, include_platform, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/smart-money/funds?scores=true"
GET/smart-money/funds/{cik}/holdings

One fund's archived 13F book across quarters (filing-dated archive).

Params: cik, from_quarter, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/smart-money/funds/{cik}/holdings"
GET/smart-money/consensus

Universe consensus: companies ≥1 tracked-elite fund holds, joined to the verdict.

Params: min_holders, convergence, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/smart-money/consensus?min_holders=3"
GET/smart-money/tape

THE TAPE — archive-basis fund entry/exit/accel/cool events (display-only).

Params: before, material_pct, type, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/smart-money/tape?type=entry"
GET/smart-money/clone-book

The Long/Short Clone Book snapshot + backtest curve (display-only, filing-archive).

Params: curve

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/smart-money/clone-book"
GET/smart-money/companies/{company}/census

One company's FULL-census 13F holder history (all filers, the honest denominator).

Params: company, quarters

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/smart-money/companies/{company}/census"
GET/smart-money/companies/{company}/holders

The tracked-elite fund book for one company (value ladder + optional trajectory).

Params: company, history, include_platform

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/smart-money/companies/{company}/holders?history=90"

Feeds & Cited feeds

Market microstructure and grounded citations — insider trades/positions, ownership events, short interest/volume, and verbatim cited chain mentions + corroborated extracted metrics.

GET/feeds/insider-trades

SEC Form 4 open-market insider buys/sells (transaction-dated, not our detection).

Params: company, person_cik, since_days, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/feeds/insider-trades?company=NVDA"
GET/feeds/insider-positions

SEC Form 3/5 insider holdings (positions owned) — periodOfReport / filedAt / detectedAt.

Params: company (required), limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/feeds/insider-positions?company=NVDA"
GET/feeds/ownership-events

Filing-sourced ownership events (13D/G, Form 144, congress PTRs, new-insiders, reveals).

Params: company, window_days, kinds, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/feeds/ownership-events?window_days=60"
GET/feeds/short-interest

FINRA bi-monthly short interest — a symbol's cycles, or the latest crowded names.

Params: ticker, company, cycles, mode, min_dtc, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/feeds/short-interest?ticker=NVDA"
GET/feeds/short-volume

FINRA daily short-volume (tradeDate vs detectedAt; regSho tri-state, never coerced).

Params: ticker, company, days, limit, offset

curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/feeds/short-volume?ticker=NVDA&days=30"

Sheets & Excel

Sheets and Excel are just REST clients of the API above — the same entk_live_ key drives them, on any tier with a REST scope. Nothing extra to buy. Paste the code below; no Marketplace or Office Store install needed. Your key is stored privately and sent only in the request header — never put it in a cell.

Google Sheets — the =ENTITY() function

  1. In your sheet: Extensions ▸ Apps Script. Replace the sample with the Code.gs below.
  2. Click + ▸ HTML, name the file exactly Sidebar, and paste the Sidebar snippet.
  3. Save, then reload the sheet — a THE ENTITY Data menu appears.
  4. THE ENTITY Data ▸ Set API key once (this also grants the one-time network permission).

Then pull data live into any cell:

=ENTITY("/smart-money/funds","scores=true")
=ENTITY("/graph/nodes","q=hbm&limit=20")
=ENTITY("/engine/forecast","horizonQuarters=6")
=ENTITY("/graph/nodes/hbm")            ' one node, full detail

The result spills into empty cells to the right and down. For large or fully-paginated pulls, use THE ENTITY Data ▸ Pull to table— it writes a static table (no 30-second cell limit) and is the right path on sheets shared with other editors, since it runs under each editor’s own key. In a cell, =ENTITY() reads the sheet owner’s key (a Google restriction), so on a shared sheet prefer the sidebar. =ENTITY() refreshes when its arguments change, not on a timer — pass a cell you bump as the third argument to force a refresh.

Code.gs
/**
 * THE ENTITY Data — Google Sheets client.
 *
 * INSTALL (no Marketplace needed):
 *   1. Extensions > Apps Script. Delete any sample, paste THIS file as "Code.gs".
 *   2. Add an HTML file named exactly "Sidebar" and paste the Sidebar snippet.
 *   3. Save, then reload the Sheet. A "THE ENTITY Data" menu appears.
 *   4. THE ENTITY Data > Set API key — paste your entk_live_ key ONCE
 *      (this also grants the one-time network permission; custom functions
 *      can't prompt for it themselves).
 * Then: =ENTITY("/graph/nodes","q=hbm")  — or  THE ENTITY Data > Pull to table.
 *
 * Your key is stored in your user properties (never in a cell) and sent only in
 * the Authorization header. Read-only: every call is a GET.
 *
 * SHARED SHEETS: by a Google rule, =ENTITY() in a cell reads the SHEET OWNER'S
 * key (not the invoking editor's). So on a solo/owned sheet =ENTITY is fully
 * per-you; on a sheet shared with other editors, those editors should use
 * "THE ENTITY Data > Pull to table" (the sidebar runs as the active user and
 * reads THAT user's key), not =ENTITY.
 */
var ENTITY_BASE = 'https://joinentity.com/api/v1';
var ENTITY_KEY_PROP = 'ENTITY_API_KEY';
var ENTITY_PAGE_LIMIT = 200;        // server max page size
var ENTITY_CELL_ROW_CAP = 1000;     // =ENTITY() in-cell row cap (the 30s wall)
var ENTITY_CELL_TIME_MS = 25000;    // =ENTITY() time budget, under the 30s wall
var ENTITY_IMPORT_ROW_CAP = 50000;  // sidebar "Pull to table" cap
var ENTITY_IMPORT_TIME_MS = 300000; // sidebar time budget (server ~6-min limit)

/* ---- menu + key management (public: reachable from the menu / dialog) ---- */

function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('THE ENTITY Data')
    .addItem('Set API key…', 'entitySetKey')
    .addItem('Test connection', 'entityTestConnection')
    .addSeparator()
    .addItem('Pull to table…', 'entityShowSidebar')
    .addSeparator()
    .addItem('Clear API key', 'entityClearKey')
    .addToUi();
}

function entitySetKey() {
  var html = HtmlService.createHtmlOutput(
    '<div style="font:13px -apple-system,Segoe UI,Roboto,sans-serif;padding:4px">' +
    '<p>Paste your <b>entk_live_</b> key. Stored privately (never in a cell).</p>' +
    '<form onsubmit="return save()">' +
    '<input id="k" type="password" placeholder="entk_live_…" style="width:100%;box-sizing:border-box;padding:6px">' +
    '<p style="text-align:right;margin:10px 0 0"><button type="submit">Save key</button></p></form>' +
    '<script>function save(){var v=document.getElementById("k").value;' +
    'google.script.run.withSuccessHandler(function(){google.script.host.close()})' +
    '.withFailureHandler(function(e){alert(e.message)}).entitySaveKey(v);return false;}</script></div>'
  ).setWidth(380).setHeight(150);
  SpreadsheetApp.getUi().showModalDialog(html, 'Set API key');
}

// PUBLIC (no trailing underscore) so the dialog's google.script.run can reach it.
function entitySaveKey(key) {
  key = String(key || '').trim();
  if (!key) throw new Error('No key provided.');
  PropertiesService.getUserProperties().setProperty(ENTITY_KEY_PROP, key);
  // Touch the network so the script.external_request scope is granted now.
  try { UrlFetchApp.fetch(ENTITY_BASE + '/whoami', { method: 'get', muteHttpExceptions: true, headers: { Authorization: 'Bearer ' + key } }); } catch (e) {}
  return true;
}

function entityClearKey() {
  PropertiesService.getUserProperties().deleteProperty(ENTITY_KEY_PROP);
  SpreadsheetApp.getUi().alert('THE ENTITY Data', 'API key cleared.', SpreadsheetApp.getUi().ButtonSet.OK);
}

function entityTestConnection() {
  var ui = SpreadsheetApp.getUi();
  try {
    // /whoami is NOT enveloped — tier/scopes/usage sit at the top level.
    var body = entityFetch_('/whoami', '');
    var info = { tier: body.tier, scopes: body.scopes, usage: body.usage };
    ui.alert('THE ENTITY Data', 'Connected. ' + JSON.stringify(info).slice(0, 400), ui.ButtonSet.OK);
  } catch (e) {
    ui.alert('THE ENTITY Data', 'Failed: ' + e.message, ui.ButtonSet.OK);
  }
}

function entityShowSidebar() {
  var html = HtmlService.createHtmlOutputFromFile('Sidebar').setTitle('THE ENTITY Data — Pull to table');
  SpreadsheetApp.getUi().showSidebar(html);
}

/* ---- the =ENTITY() custom function ---- */

/**
 * Pull a THE ENTITY Data endpoint into cells (spills to the right + down).
 *   =ENTITY("/smart-money/funds","scores=true")
 *   =ENTITY("/graph/nodes/hbm")
 * @param {string} path      Endpoint path, e.g. "/graph/nodes".
 * @param {string} query     Optional query string, e.g. "q=hbm&limit=20".
 * @param {any} refreshToken Optional: reference a cell you bump to force a refresh
 *                           (custom functions do not auto-refresh on a timer).
 * @return A table (header row + rows), or a readable message.
 * @customfunction
 */
function ENTITY(path, query, refreshToken) {
  if (path === undefined || path === null || path === '') return 'Usage: =ENTITY("/graph/nodes","q=hbm")';
  try {
    var res = entityCollect_(String(path), query == null ? '' : String(query), ENTITY_CELL_ROW_CAP, ENTITY_CELL_TIME_MS);
    if (res.degraded) return 'THE ENTITY: ' + res.degraded;
    if (!res.rows.length) return [['(no rows)' + (res.note ? ' — ' + res.note : '')]];
    return res.rows;
  } catch (e) {
    return 'THE ENTITY: ' + e.message;
  }
}

/* ---- sidebar server function (PUBLIC: reached via google.script.run) ---- */

function entityImportToActiveCell(path, query) {
  var res = entityCollect_(String(path || ''), String(query || ''), ENTITY_IMPORT_ROW_CAP, ENTITY_IMPORT_TIME_MS);
  if (res.degraded) throw new Error(res.degraded);
  var rows = res.rows.length ? res.rows : [['(no rows)' + (res.note ? ' — ' + res.note : '')]];
  var width = 1;
  for (var i = 0; i < rows.length; i++) width = Math.max(width, rows[i].length);
  for (var j = 0; j < rows.length; j++) { while (rows[j].length < width) rows[j].push(''); }
  var sheet = SpreadsheetApp.getActiveSheet();
  var cell = sheet.getActiveCell();
  var range = sheet.getRange(cell.getRow(), cell.getColumn(), rows.length, width);
  range.setValues(rows);
  // Honesty note on the top-left cell: the data's own date vs when we ingested it.
  var note = [];
  if (res.asOf) note.push('as_of: ' + res.asOf);
  if (res.detectedAt) note.push('detected_at: ' + res.detectedAt);
  if (res.capped) note.push('(capped at ' + ENTITY_IMPORT_ROW_CAP + ' rows)');
  else if (res.note) note.push('(' + res.note + ')');
  if (note.length) sheet.getRange(cell.getRow(), cell.getColumn()).setNote(note.join('\n'));
  return { rows: rows.length, cols: width, capped: !!res.capped };
}

// The endpoint catalog for the sidebar dropdown (PUBLIC: read via google.script.run).
function entityEndpointCatalog() {
  return [{"path":"/search","dataset":"graph","summary":"Full-text search across the graph — nodes, domains, themes and companies (ranked).","example":"/search?q=hbm"},{"path":"/graph/layers","dataset":"graph","summary":"The five value-chain layers and their domains (full taxonomy).","example":"/graph/layers"},{"path":"/graph/nodes","dataset":"graph","summary":"List causal-graph nodes (summary projection), filterable + paginated.","example":"/graph/nodes?layerId=chips&limit=20"},{"path":"/graph/nodes/{id}","dataset":"graph","summary":"One node, full detail (all summaries, watch items, metrics, sources).","example":"/graph/nodes/{id}"},{"path":"/graph/edges","dataset":"graph","summary":"Company↔company relationships (supplier/customer/partner/competitor).","example":"/graph/edges?company=nvidia"},{"path":"/graph/relations","dataset":"graph","summary":"Typed node→node causal relations (strength, elasticity, latency).","example":"/graph/relations?nodeId=foundry-capacity"},{"path":"/graph/facilities","dataset":"graph","summary":"Physical sites (fabs, plants, ports) geolocated + hooked to nodes.","example":"/graph/facilities?geographyId=taiwan"},{"path":"/graph/chokepoints","dataset":"graph","summary":"Structural pinch points (straits, ports, jurisdictions) + affected nodes.","example":"/graph/chokepoints?kind=strait"},{"path":"/graph/companies/{id}/exposures","dataset":"graph","summary":"One company's exposures to chain nodes (intensity 0–100).","example":"/graph/companies/{id}/exposures"},{"path":"/engine/nowcast","dataset":"engine","summary":"Constraint Nowcast — system tightness index (0–100), per-layer + movers.","example":"/engine/nowcast"},{"path":"/engine/min-cut","dataset":"engine","summary":"Min cut of the Energy→Applications flow network (structural exhibit).","example":"/engine/min-cut"},{"path":"/engine/forecast","dataset":"engine","summary":"Probabilistic bottleneck-migration forecast (seeded Monte-Carlo).","example":"/engine/forecast?horizonQuarters=6"},{"path":"/engine/conviction","dataset":"engine","summary":"Conviction record from D1 (per-company score/verdict/drivers).","example":"/engine/conviction?company=nvidia"},{"path":"/engine/node-scores","dataset":"engine","summary":"Daily node-score record from D1 (global/tension/news/timing).","example":"/engine/node-scores?node=hbm&days=30"},{"path":"/screener/accumulation","dataset":"smart-money","summary":"DERIVED screen: companies the tracked elite are net-accumulating AND the engine agrees (ranked).","example":"/screener/accumulation?min_holders=3&min_conviction=60"},{"path":"/smart-money/funds","dataset":"smart-money","summary":"The tracked-elite fund roster (optionally scored), + a labeled platform sidecar.","example":"/smart-money/funds?scores=true"},{"path":"/smart-money/funds/{cik}/holdings","dataset":"smart-money","summary":"One fund's archived 13F book across quarters (filing-dated archive).","example":"/smart-money/funds/{cik}/holdings"},{"path":"/smart-money/consensus","dataset":"smart-money","summary":"Universe consensus: companies ≥1 tracked-elite fund holds, joined to the verdict.","example":"/smart-money/consensus?min_holders=3"},{"path":"/smart-money/tape","dataset":"smart-money","summary":"THE TAPE — archive-basis fund entry/exit/accel/cool events (display-only).","example":"/smart-money/tape?type=entry"},{"path":"/smart-money/clone-book","dataset":"smart-money","summary":"The Long/Short Clone Book snapshot + backtest curve (display-only, filing-archive).","example":"/smart-money/clone-book"},{"path":"/smart-money/companies/{company}/census","dataset":"smart-money","summary":"One company's FULL-census 13F holder history (all filers, the honest denominator).","example":"/smart-money/companies/{company}/census"},{"path":"/smart-money/companies/{company}/holders","dataset":"smart-money","summary":"The tracked-elite fund book for one company (value ladder + optional trajectory).","example":"/smart-money/companies/{company}/holders?history=90"},{"path":"/feeds/insider-trades","dataset":"feeds","summary":"SEC Form 4 open-market insider buys/sells (transaction-dated, not our detection).","example":"/feeds/insider-trades?company=NVDA"},{"path":"/feeds/insider-positions","dataset":"feeds","summary":"SEC Form 3/5 insider holdings (positions owned) — periodOfReport / filedAt / detectedAt.","example":"/feeds/insider-positions?company=NVDA"},{"path":"/feeds/ownership-events","dataset":"feeds","summary":"Filing-sourced ownership events (13D/G, Form 144, congress PTRs, new-insiders, reveals).","example":"/feeds/ownership-events?window_days=60"},{"path":"/feeds/short-interest","dataset":"feeds","summary":"FINRA bi-monthly short interest — a symbol's cycles, or the latest crowded names.","example":"/feeds/short-interest?ticker=NVDA"},{"path":"/feeds/short-volume","dataset":"feeds","summary":"FINRA daily short-volume (tradeDate vs detectedAt; regSho tri-state, never coerced).","example":"/feeds/short-volume?ticker=NVDA&days=30"},{"path":"/cited/chain-mentions","dataset":"graph","summary":"Grounded, verbatim value-chain mentions from earnings calls (cited, source-linked).","example":"/cited/chain-mentions?company=NVDA&node=hbm"},{"path":"/cited/extracted-metrics","dataset":"graph","summary":"Corroboration-gated metric series extracted from cited sources (uncorroborated suppressed).","example":"/cited/extracted-metrics?node=cowos"}];
}

/* ---- internals (trailing underscore = private to the script) ---- */

// Fetch ONE page. Throws a readable Error on any {ok:false}/HTTP>=400.
function entityFetch_(path, query) {
  var url = ENTITY_BASE + entityNormPath_(path);
  if (query) url += (url.indexOf('?') >= 0 ? '&' : '?') + String(query).replace(/^[?&]/, '');
  var resp = UrlFetchApp.fetch(url, {
    method: 'get',
    muteHttpExceptions: true,
    headers: { Authorization: 'Bearer ' + entityGetKey_() },
  });
  var code = resp.getResponseCode();
  var body = {};
  try { body = JSON.parse(resp.getContentText() || '{}'); } catch (e) { body = {}; }
  if (code >= 400 || body.ok === false) {
    throw new Error(entityErrMsg_((body && body.error) || ('http_' + code), resp));
  }
  return body;
}

// Collect (paginating under caps) + flatten to a 2-D array. Buffers all rows so
// the header set is the UNION across every page (a key that first appears on
// page 2 still becomes a column). Returns { rows, asOf, detectedAt, meta, capped,
// degraded?, note? }.
function entityCollect_(path, query, rowCap, timeMs) {
  var start = Date.now();
  var objects = [];   // accumulated row-objects (array-of-objects endpoints)
  var primitives = []; // accumulated primitive rows (array-of-primitives)
  var kv = null;      // a single detail object → field/value
  var offset = 0;
  var userPaged = /(?:^|[?&])(?:limit|offset)=/.test(query || '');
  var last = null;
  var capped = false;
  var sawCollection = false;
  var stoppedEarly = false;
  while (true) {
    var q = userPaged ? query : entityMergeQuery_(query, 'limit=' + ENTITY_PAGE_LIMIT + '&offset=' + offset);
    var body = entityFetch_(path, q);
    last = body;
    var data = body.data;
    if (data === null || data === undefined) {
      if (sawCollection) { stoppedEarly = true; break; } // a later page degraded — flag it, don't present partial as full
      return { rows: [], degraded: 'unavailable' + (entityMetaNote_(body) ? ' — ' + entityMetaNote_(body) : ''), meta: body.meta || null };
    }
    // The collection: an array, or (defensive) the sole array field of an object
    // that carries a top-level page — a nested paginated collection.
    var coll = null;
    if (Array.isArray(data)) coll = data;
    else if (data && typeof data === 'object' && body.page) coll = entitySoleArrayField_(data);

    if (coll !== null) {
      sawCollection = true;
      for (var i = 0; i < coll.length; i++) {
        var v = coll[i];
        if (v !== null && typeof v === 'object') objects.push(v); else primitives.push(v);
        if (objects.length + primitives.length >= rowCap) { capped = true; break; }
      }
      var page = body.page;
      if (userPaged || capped || !page || page.hasMore !== true) break;
      offset = (page.nextOffset !== null && page.nextOffset !== undefined) ? page.nextOffset : (offset + ENTITY_PAGE_LIMIT);
      if (Date.now() - start > timeMs) { capped = true; break; }
      continue;
    }
    if (data && typeof data === 'object') { kv = data; break; } // detail object (not paginated)
    primitives.push(data); break; // a bare primitive
  }

  var out = [];
  if (kv) {
    out.push(['field', 'value']);
    var keys = Object.keys(kv);
    for (var k = 0; k < keys.length; k++) out.push([keys[k], entityCell_(kv[keys[k]])]);
  } else if (objects.length) {
    var headers = entityUnionKeys_(objects); // union across ALL pages
    out.push(headers);
    for (var r = 0; r < objects.length; r++) {
      var o = objects[r];
      out.push(headers.map(function (h) { return entityCell_(o ? o[h] : null); }));
    }
  } else if (primitives.length) {
    out.push(['value']);
    for (var p = 0; p < primitives.length; p++) out.push([entityCell_(primitives[p])]);
  } else {
    // Empty collection — valid, just no rows. Surface any meta status honestly.
    return { rows: [], note: entityMetaNote_(last) || 'no rows', asOf: last ? (last.as_of || null) : null, meta: last ? (last.meta || null) : null };
  }
  return {
    rows: out,
    asOf: last ? (last.as_of || null) : null,
    detectedAt: last ? (last.detected_at || null) : null,
    meta: last ? (last.meta || null) : null,
    capped: capped,
    note: stoppedEarly ? 'incomplete — a later page was unavailable' : (capped ? 'capped' : ''),
  };
}

function entityGetKey_() {
  var k = PropertiesService.getUserProperties().getProperty(ENTITY_KEY_PROP);
  if (!k) throw new Error('set your API key first (THE ENTITY Data > Set API key).');
  return k;
}

function entityNormPath_(path) {
  path = String(path || '').trim();
  if (path.indexOf('http') === 0) path = path.replace(ENTITY_BASE, ''); // tolerate a full URL
  if (path.charAt(0) !== '/') path = '/' + path;
  return path;
}

function entityMergeQuery_(base, extra) {
  base = String(base || '').replace(/^[?&]/, '');
  return base ? base + '&' + extra : extra;
}

function entityUnionKeys_(arr) {
  var keys = [];
  var seen = {};
  for (var i = 0; i < arr.length; i++) {
    var o = arr[i];
    if (o && typeof o === 'object') {
      for (var k in o) { if (Object.prototype.hasOwnProperty.call(o, k) && !seen[k]) { seen[k] = 1; keys.push(k); } }
    }
  }
  return keys.length ? keys : ['value'];
}

function entityCell_(v) {
  if (v === null || v === undefined) return '';
  if (typeof v === 'object') { try { return JSON.stringify(v); } catch (e) { return String(v); } }
  return v;
}

// A PURE single-list wrapper's array (e.g. { rows: [...] }), else null. A record
// that carries a list ALONGSIDE other fields (a detail object like census
// { latest, trend, … }) is NOT a wrapper and must keep all its fields.
function entitySoleArrayField_(obj) {
  var keys = Object.keys(obj);
  if (keys.length === 1 && Array.isArray(obj[keys[0]])) return obj[keys[0]];
  return null;
}

// A short human status from the envelope meta (empty / rebuilding / not found …), or ''.
function entityMetaNote_(body) {
  var m = body && body.meta;
  if (!m) return '';
  if (m.reason) return String(m.reason);
  if (m.rebuilding) return 'rebuilding';
  if (m.empty) return 'no data yet';
  if (m.found === false) return 'not found';
  if (m.available === false) return 'not available';
  if (m.degraded) return 'degraded';
  if (m.note) return String(m.note);
  return '';
}

function entityErrMsg_(code, resp) {
  var map = {
    missing_key: 'no API key sent',
    invalid_key: 'invalid API key',
    inactive_addon: 'your THE ENTITY Data plan is not active',
    surface_not_in_tier: 'this surface is not in your tier',
    scope_denied: 'your key lacks the scope for this dataset',
    rate_limited: 'rate limited',
    quota_exceeded: 'monthly quota exhausted',
    unavailable: 'temporarily unavailable — try again shortly',
  };
  var msg = map[code] || code;
  if (code === 'rate_limited' && resp) {
    try {
      var h = resp.getHeaders() || {};
      var ra = h['Retry-After'] || h['retry-after'];
      if (ra) msg += ' — retry in ' + ra + 's';
    } catch (e) {}
  }
  return msg;
}
Sidebar (HTML file)
<!-- THE ENTITY Data — add this as an HTML file named exactly "Sidebar". -->
<!DOCTYPE html>
<html>
  <head><base target="_top">
    <style>
      body { font: 13px -apple-system, Segoe UI, Roboto, sans-serif; margin: 0; padding: 12px; color: #1a1c1f; }
      label { display: block; margin: 10px 0 4px; font-weight: 600; }
      select, input { width: 100%; box-sizing: border-box; padding: 6px; }
      button { margin-top: 12px; padding: 8px 12px; cursor: pointer; }
      .hint { color: #6b7076; font-size: 12px; margin: 6px 0 0; }
      #status { margin-top: 12px; white-space: pre-wrap; }
    </style>
  </head>
  <body>
    <p class="hint">Pulls the FULL result (all pages) into a static table starting at your active cell.</p>
    <label for="ep">Endpoint</label>
    <select id="ep"><option value="">Loading…</option></select>
    <label for="path">Path</label>
    <input id="path" placeholder="/graph/nodes">
    <label for="q">Query (optional)</label>
    <input id="q" placeholder="q=hbm">
    <button id="go" onclick="pull()">Pull to table</button>
    <p id="status" class="hint"></p>
    <script>
      var CAT = [];
      function fill() {
        google.script.run.withSuccessHandler(function (rows) {
          CAT = rows || [];
          var sel = document.getElementById('ep');
          sel.innerHTML = '<option value="">— choose an endpoint —</option>';
          CAT.forEach(function (e, i) {
            var o = document.createElement('option');
            o.value = String(i); o.textContent = '[' + e.dataset + '] ' + e.path;
            sel.appendChild(o);
          });
        }).entityEndpointCatalog();
      }
      document.getElementById('ep').addEventListener('change', function () {
        var e = CAT[this.value];
        if (!e) return;
        var ex = String(e.example || e.path);
        var qi = ex.indexOf('?');
        document.getElementById('path').value = qi >= 0 ? ex.slice(0, qi) : ex;
        document.getElementById('q').value = qi >= 0 ? ex.slice(qi + 1) : '';
        var ph = /\{[^}]*\}/.test(document.getElementById('path').value);
        document.getElementById('status').textContent = (e.summary || '') + (ph ? '  ⚠ replace the {…} in the path with a real value' : '');
      });
      function pull() {
        var path = document.getElementById('path').value.trim();
        var q = document.getElementById('q').value.trim();
        if (!path) { document.getElementById('status').textContent = 'Enter a path first.'; return; }
        if (/\{[^}]*\}/.test(path)) { document.getElementById('status').textContent = 'Replace the {…} placeholder in the path with a real value first.'; return; }
        var btn = document.getElementById('go'); btn.disabled = true;
        document.getElementById('status').textContent = 'Pulling…';
        google.script.run
          .withSuccessHandler(function (r) { btn.disabled = false; document.getElementById('status').textContent = 'Wrote ' + r.rows + ' rows × ' + r.cols + ' cols' + (r.capped ? ' (capped)' : '') + '.'; })
          .withFailureHandler(function (e) { btn.disabled = false; document.getElementById('status').textContent = 'THE ENTITY: ' + e.message; })
          .entityImportToActiveCell(path, q);
      }
      fill();
    </script>
  </body>
</html>

Microsoft Excel — Power Query

  1. Home ▸ Manage Parameters ▸ New: name EntityApiKey, type Text, value = your key.
  2. Data ▸ Get Data ▸ From Other Sources ▸ Blank Query, then Advanced Editor.
  3. Paste the query below, edit Path / Params, and Close & Load.
// THE ENTITY Data — Excel via Power Query (Data > Get Data > Blank Query > Advanced Editor).
// FIRST: Home > Manage Parameters > New:  Name = EntityApiKey, Type = Text, Current Value = your entk_live_ key.
// Then paste this, edit "Path" and "Params", and Close & Load. Refreshes on Excel desktop (Windows/Mac).
let
    BaseUrl = "https://joinentity.com/api/v1",          // keep this a LITERAL (dynamic bits go in RelativePath/Query)
    Path    = "/smart-money/funds",                     // <- change me
    Params  = [ scores = "true" ],                    // <- endpoint query params, e.g. [ q = "hbm" ]
    PageSize = 200,

    GetPage = (offset as number) as record =>
        let
            Response = Web.Contents(BaseUrl, [
                RelativePath = Path,
                Query = Record.Combine({ Params, [ limit = Text.From(PageSize), offset = Text.From(offset) ] }),
                Headers = [ Authorization = "Bearer " & EntityApiKey ],
                ManualStatusHandling = {400, 401, 403, 404, 429, 500, 503}
            ]),
            Body = try Json.Document(Response) otherwise [ ok = false, error = "unavailable" ]
        in
            Body,

    Page0 = GetPage(0),
    Ok0 = Record.FieldOrDefault(Page0, "ok", false),
    HasData0 = Record.HasFields(Page0, "data"),   // distinguish data:null (degraded) from a non-enveloped body (e.g. /whoami)
    Data0 = Record.FieldOrDefault(Page0, "data", null),
    Meta0 = Record.FieldOrDefault(Page0, "meta", null),
    Note0 = if Meta0 = null then "" else Record.FieldOrDefault(Meta0, "reason", Record.FieldOrDefault(Meta0, "note", "")),

    // Page through offset until hasMore = false. A "stop" flag ends the loop on a
    // clean finish (that sentinel is NOT emitted), while a page fetched mid-loop
    // that comes back ok=false IS emitted, so a later-page error can be surfaced
    // instead of silently truncating.
    Pages = List.Generate(
        () => [ off = 0, body = Page0, stop = false ],
        each [stop] = false,
        each
            let
                pg = Record.FieldOrDefault([body], "page", null),
                more = Record.FieldOrDefault([body], "ok", false) = true and pg <> null and Record.FieldOrDefault(pg, "hasMore", false) = true,
                next = if pg = null then [off] + PageSize else Record.FieldOrDefault(pg, "nextOffset", [off] + PageSize)
            in
                if more then [ off = next, body = GetPage(next), stop = false ]
                else [ off = -1, body = [ ok = true, data = null ], stop = true ],
        each [body]
    ),
    ErrPage = List.First(List.Select(Pages, each Record.FieldOrDefault(_, "ok", false) <> true), null),
    // A LATER page that came back degraded (ok:true, data:null) also truncates —
    // surface it rather than silently returning only the earlier pages.
    TruncPage = List.First(List.Select(List.Skip(Pages, 1), each Record.FieldOrDefault(_, "ok", false) = true and Record.FieldOrDefault(_, "data", null) = null), null),

    // data is a list (collection endpoints) or a single record (detail/exhibit).
    // Defensive unwrap ONLY for a genuine nested paginated collection: a record
    // that carries a top-level 'page' AND is a PURE single-list wrapper. A record
    // with a list ALONGSIDE other fields (e.g. census {latest,trend,…}) is a
    // detail object → one row, never collapsed to just the list.
    Rows = List.Combine(List.Transform(Pages, each
        let
            d = Record.FieldOrDefault(_, "data", null),
            pg = Record.FieldOrDefault(_, "page", null),
            names = if d <> null and d is record then Record.FieldNames(d) else {},
            lists = if d <> null and d is record then List.Select(Record.FieldValues(d), each _ is list) else {}
        in
            if d = null then {}
            else if d is list then d
            else if pg <> null and List.Count(names) = 1 and List.Count(lists) = 1 then lists{0}
            else { d })),

    // Columns = UNION of field names across ALL rows (a field that first appears
    // on a later row still becomes a column).
    Cols = List.Distinct(List.Combine(List.Transform(Rows, each if _ is record then Record.FieldNames(_) else { "value" }))),

    Result =
        if Ok0 <> true then error "THE ENTITY: " & Record.FieldOrDefault(Page0, "error", "request failed")
        // A non-enveloped OK body (e.g. /whoami) has no 'data' field — show its top-level fields, don't cry "unavailable".
        else if not HasData0 then Table.FromRecords({ Record.RemoveFields(Page0, { "ok" }) })
        else if Data0 = null then error "THE ENTITY: data unavailable" & (if Note0 <> "" then " (" & Note0 & ")" else "")
        else if ErrPage <> null then error "THE ENTITY: pagination stopped — " & Record.FieldOrDefault(ErrPage, "error", "a later page failed")
        else if TruncPage <> null then error "THE ENTITY: pagination stopped — a later page returned no data (degraded)"
        else if List.Count(Rows) = 0 then #table({ "result" }, {})
        else if List.First(Rows) is record then Table.FromRecords(Rows, Cols, MissingField.UseNull)
        else Table.FromList(Rows, Splitter.SplitByNothing(), { "value" })
in
    Result

Power Query refreshes on Excel for Windows and Mac (desktop). Excel for the web won’t refresh an authenticated source, so use the desktop app there — or the in-cell add-in below.

Microsoft Excel — in-cell =ENTITY.QUERY() beta

A self-hosted Office add-in gives Excel a live cell function, like Sheets. It’s sideloaded from our manifest (no Office Store install), works on Excel for Windows, Mac and the web, and stores your key outside the workbook.

  1. Save the manifest: joinentity.com/excel/manifest.xml.
  2. Sideload it (Microsoft’s steps): Excel for web → Add-ins ▸ Upload My Add-in; Windows → a shared-folder catalog; Mac → the wef folder.
  3. Open the THE ENTITY Data button on the Home tab, save your entk_live_ key.
=ENTITY.QUERY("/smart-money/funds","scores=true")
=ENTITY.QUERY("/graph/nodes","q=hbm")

Beta — self-hosted and sideloaded, so please try it and tell us what breaks. Power Query is the supported path today; Excel custom functions require a Microsoft 365 subscription (not volume-licensed Office 2021 or iPad).

Bulk export

On Pro and Scale, one call returns a whole collection as CSV or NDJSON — the shape you point a warehouse’s external table, a COPY, or Power Query at, instead of paging the REST API thousands of times. Same key, gated on the bulksurface and the resource’s dataset scope; exactly one call is metered per pull.

# the whole graph as CSV
curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/bulk?resource=graph/nodes&format=csv"

# smart-money consensus as NDJSON
curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/bulk?resource=smart-money/consensus&format=ndjson"

# list what's exportable
curl -H "Authorization: Bearer $ENTITY_KEY" "https://joinentity.com/api/v1/bulk"

Exportable resources:

resourcedataset
graph/nodesgraph
graph/edgesgraph
graph/relationsgraph
graph/facilitiesgraph
graph/chokepointsgraph
graph/layersgraph
smart-money/fundssmart-money
smart-money/consensussmart-money

A pull is capped at 100,000 rows (the response carries x-entity-capped: true and x-entity-rowswhen so). Per-entity time-series (a company’s insider trades, short interest, one fund’s holdings) stay on the paginated REST endpoints above, where you filter first.

Errors

Failures return { ok: false, error } with a stable error string. The rate/quota headers are echoed on failures too.

StatuserrorCondition
401missing_keyNo Authorization: Bearer (or x-api-key) header on the request.
401invalid_keyThe key is unknown or has been revoked.
403inactive_addonThe key's account has no active THE ENTITY Data subscription.
403surface_not_in_tierThe tier does not include the requested surface (e.g. bulk on Starter).
403scope_deniedThe key's scopes exclude this surface + dataset.
429rate_limitedThe per-minute rate ceiling was exceeded (see retry-after).
429quota_exceededThe monthly call allowance is exhausted (resets at x-quota-reset).
503unavailableMetering infrastructure was unreachable — the request fails closed, never an unmetered grant.

A read that throws server-side does NOT 500: it degrades to 200 with { ok: true, data: null, meta: { degraded: true } } and an x-degraded: 1 header, so a transient outage never leaks internals or looks like a confident empty.

Honesty notes

  • as_of vs detected_at. as_ofis the data's own date (a filing/trade/settlement/quarter date); detected_at is when we ingested it. They are separate fields and never collapsed into one.
  • “Of N tracked funds.” Smart-money counts are of the tracked elite roster (meta.tracked_funds), not institutions at large. The company-census endpoint is the exception — it reports the full 13F filer denominator, labeled as such.
  • Archive vs live. The tape and clone book are quarter-end reconstructions, flagged display_only / source: archive — never presented as real-time.
  • Cited, not asserted. Chain mentions and extracted metrics carry source: cited; every mention is a verbatim, source-linked quote, and single-point series are suppressed by the reader.