Integrate

Three calls. One key. Copy them and go.

Everything below was run against the live API and pasted in. If a response here does not match what you get back, the page is wrong and we want to know.

1 · Get a key

Open the app, go to API, press Generate new key. It is shown once — only a SHA-256 digest is stored, so there is no way to recover it later. Calls you make inside the app are not billed; calls carrying a key are.

basehttps://edfaiegblbtddqjslgyl.supabase.co/rest/v1/rpc
headersapikey: sb_publishable_RYUazcegtfytJNLrxYPueg_L-TSOaF0
Content-Type: application/json
body"p_key": "tl_live_…"  — required on every call

The apikey above is the project's publishable key and is printed in full on purpose — it identifies the project and grants nothing on its own. Every row you can reach is decided by a policy in the database, which is why it can sit in this page and in the site's own JavaScript. The one in p_key is yours, is not publishable, and is the one that is billed.

Every endpoint is a POST, including the reads. That is deliberate: it keeps your key out of URLs, where it would end up in server logs, browser history and anything that ships a referrer header.

POST /search

2 · Resolve a company

Takes a ticker, a ten-digit CIK, or part of a name. Returns the identifier the other two calls take. A company with three listings is one entity here, because resolution happens on the CIK.

curl -X POST $BASE/search \
  -H "apikey: $PUB" -H "Content-Type: application/json" \
  -d '{"p_q": "honeywell", "p_limit": 2, "p_key": "tl_live_…"}'
[{ "node_id": "cmp_004048", "name": "Honeywell International Inc.",
  "ticker": "HON", "cik": "0000773840", "country": "US", "events": 115 },
 { "node_id": "cmp_011418", "name": "Honeywell Aerospace Inc", "ticker": "HONA" … }]

Two entities, correctly kept apart. events is how many events read by the model have reached it, which is a reasonable way to pick when a name is ambiguous.

POST /connections

3 · Walk its connections

Companies one or two relationships away, ranked by bridges — how many independent paths reach them. One shared director is a coincidence; seventeen is a relationship.

curl -X POST $BASE/connections \
  -H "apikey: $PUB" -H "Content-Type: application/json" \
  -d '{"p_id": "HON", "p_hops": 2, "p_limit": 100, "p_key": "tl_live_…"}'
[{ "node_id": "cmp_000014", "name": "3M Company", "ticker": "MMM",
  "hops": 1, "bridges": 37, "via_name": null,
  "relation": "selects as compensation peer", "source": "def14a",
  "evidence": "MMM_2026-03-25_def14a.htm|Honeywell International Inc.",
  "as_of": "2026-03-25" },
 … 80 more at one hop …
 { "node_id": "cmp_008910", "name": "TransDigm Group Incorporated", "ticker": "TDG",
  "hops": 2, "bridges": 17, "via_name": "BWX Technologies, Inc.",
  "relation": "selects as compensation peer", "source": "def14a",
  "evidence": "BWXT_2026-03-18_def14a.htm|TransDigm Group Incorporated",
  "as_of": "2026-03-18" }, … ]

via_name is null at one hop — there is no intermediate, the two are connected directly. At two hops it names the company or the person the path runs through: TransDigm is reached from Honeywell by way of BWX Technologies, which selects both as compensation peers.

Ranking puts every one-hop neighbour ahead of every two-hop one, because a direct link always has more independent paths behind it. Honeywell has 81 companies at one hop, so a p_limit below that returns nothing but hops: 1 however you set p_hops — which is why the call above asks for 100. Set it by how deep you want to read, not by how many rows you want back.

p_id accepts a ticker, a CIK or a node_id, so you can skip /search when you already hold an identifier.

POST /events

4 · Read what reached it

Events read by the model indexed by the company they reached, not the company they were about. Watching Honeywell surfaces a press release about Moog.

curl -X POST $BASE/events \
  -H "apikey: $PUB" -H "Content-Type: application/json" \
  -d '{"p_id": "HON", "p_since": "2026-08-01", "p_limit": 40, "p_key": "tl_live_…"}'
{ "event_title": "Moog Celebrates 75 Years of Innovation…",
  "event_at": "2026-08-14T23:59:59+00:00", "hops": 2,
  "direction": "bearish", "magnitude": "moderate",
  "why": "Honeywell is a direct competitor in motion control and aerospace…",
  "relation": "names as competitor", "source": "10k_item1",
  "evidence": "0000076334-25-000035|Honeywell International, Inc.|Aerospace…" }

Turning evidence into a link

The evidence field is pipe-separated and its first segment is the document. Four shapes, and they are distinguishable without asking:

FIRST SEGMENTWHAT TO DO
0000076334-25-000035
An SEC accession number. Its first ten digits are the filer's CIK, and EDGAR wants that without leading zeros:
sec.gov/Archives/edgar/data/76334/000007633425000035/0000076334-25-000035-index.htm
https://…
Already a URL — a DART filing, or an SEC document we hold the direct path for. Use it as-is.
MMM_2026-03-25_def14a.htm
A named filing. Not resolvable to a URL on its own; it identifies the document for anyone reading the row.
vendor peer list
Not a document, and it says so. This edge came from the one third-party peer list in the graph, or from a curated set — topics_semi.csv and topics_commodity.csv read the same way. The segment after the pipe is the pair or the topic rather than a filing: vendor peer list|RLJ->CLDT, topics_semi.csv|fpga. There is no URL to build, which is the point of labelling it — you can tell it from a filing without looking it up.

What you get charged, and what you get back when it fails

CALLPER CALL
$0.0040
/connections — it walks the graph, so it costs the most
$0.0020
/events
Free
/search — resolving a name into an identifier is what makes the other two usable; charging for it taxes the wrong step. Still recorded, at zero, so the usage page can answer "am I near the limit".

Every call needs an account. Pass p_key, or call from a signed-in session — the interface does the second, which is why using the app is not billed. A call with neither is refused with 28000. An account and a key are free; the free plan simply has the lowest ceiling. Usage appears in the app under API as it accrues.

Rate limits

Per hour, per account, enforced inside the database rather than in this page. A limit a static site imposes is a suggestion — the same function is one curl away. Going over returns 54000, with the count and the cap in the message.

PLANCALLS / HOUR
500
Explore, and any signed-in account without a plan — clicking through the interface never comes close
5,000
Individual
20,000
Team — ask if a job needs more. It is a number in a table, not a rebuild.

There is no anonymous traffic to leave unbounded. Requiring an account is what makes the ceiling above real: every call belongs to someone, so every call can be counted. p_limit is capped inside each function as well, so no single call can be large regardless of plan.

ERRORMEANS
28000
invalid or revoked API key — the key does not match a live one. Revoked keys fail the same way as wrong ones.
22023
no company matches ZZZZ — the identifier resolved to nothing. Try /search.
57014
statement timeout — the query ran past the limit. Lower p_hops to 1, or retry: the first call for a company is the cold one.