Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.shoal.xyz/llms.txt

Use this file to discover all available pages before exploring further.

Overview

BD teams need early signals on partnerships, funding rounds, and product launches. Shoal’s radar and signal endpoints surface these events automatically, ranked by significance — so you can act on opportunities before they’re widely known.

Deliver Alerts Where Your Team Works

Slack

Real-time radar and signal cards in your BD channel, plus /shoal brief slash commands.

Telegram

Daily digest of top signal events ranked by score — perfect for mobile-first teams.
Shoal Intelligence Slack integration showing radar and signal event cards in a BD channel
You’ll need an API key to follow along. See the Quickstart guide to create one.

Endpoints Used

EndpointPurposeReference
/v1/radar/byCategoryPartnership & acquisition eventsDocs
/v1/signal/topHighest-impact events across all orgsDocs
/v1/brief/batchCombined view for a watchlist of orgsDocs
/v1/organizations/byOrganizationNameLook up org by nameDocs

Workflow

Step 1: Build a Watchlist

Search for organizations by name and collect their IDs into a watchlist.
cURL
curl "https://api.shoal.xyz/v1/organizations/byOrganizationName?name=Chainlink" \
  -H "Authorization: Bearer YOUR_API_KEY"
Python
import os, requests

API_KEY = os.environ.get("SHOAL_API_KEY", "YOUR_API_KEY")
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

# Resolve org names to IDs
watchlist_names = ["Chainlink", "Arbitrum", "Aave", "Lido"]
watchlist_ids = []

for name in watchlist_names:
    r = requests.get(
        "https://api.shoal.xyz/v1/organizations/byOrganizationName",
        headers=HEADERS,
        params={"name": name},
    )
    data = r.json()["data"]
    if data:
        watchlist_ids.append(str(data[0]["id"]))
        print(f"{name} -> ID {data[0]['id']}")

print(f"Watchlist IDs: {','.join(watchlist_ids)}")
JavaScript
const API_KEY = process.env.SHOAL_API_KEY || 'YOUR_API_KEY';
const HEADERS = { Authorization: `Bearer ${API_KEY}` };

const watchlistNames = ['Chainlink', 'Arbitrum', 'Aave', 'Lido'];
const watchlistIds = [];

for (const name of watchlistNames) {
  const res = await fetch(
    `https://api.shoal.xyz/v1/organizations/byOrganizationName?name=${encodeURIComponent(name)}`,
    { headers: HEADERS }
  );
  const { data } = await res.json();
  if (data.length > 0) {
    watchlistIds.push(data[0].id);
    console.log(`${name} -> ID ${data[0].id}`);
  }
}

console.log(`Watchlist IDs: ${watchlistIds.join(',')}`);

Step 2: Poll for BD-Relevant Events

Call the radar category endpoints on a schedule with the since parameter to get only new events.
cURL
# Partnership events in the last 24 hours
SINCE=$(date -u -v-24H +"%Y-%m-%dT%H:%M:%SZ")  # macOS
# SINCE=$(date -u -d '24 hours ago' +"%Y-%m-%dT%H:%M:%SZ")  # Linux

curl "https://api.shoal.xyz/v1/radar/byCategory?category=partnership&since=$SINCE" \
  -H "Authorization: Bearer YOUR_API_KEY"

# Funding rounds
curl "https://api.shoal.xyz/v1/radar/byCategory?category=funding&since=$SINCE" \
  -H "Authorization: Bearer YOUR_API_KEY"
Python
import time
from datetime import datetime, timedelta, timezone

cursor = (datetime.now(timezone.utc) - timedelta(hours=24)).isoformat()

while True:
    # Check partnership events
    partnerships = requests.get(
        "https://api.shoal.xyz/v1/radar/byCategory",
        headers=HEADERS,
        params={"category": "partnership", "since": cursor},
    ).json()["data"]

    # Check funding events
    funding = requests.get(
        "https://api.shoal.xyz/v1/radar/byCategory",
        headers=HEADERS,
        params={"category": "funding", "since": cursor},
    ).json()["data"]

    for event in partnerships + funding:
        print(f"[{event['eventCategory']}] {event['title']}")

    if partnerships or funding:
        all_events = partnerships + funding
        cursor = max(e["latestPostTimestamp"] for e in all_events)

    time.sleep(900)  # Poll every 15 minutes
JavaScript
let cursor = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();

async function pollBDEvents() {
  const categories = ['partnership', 'funding'];

  for (const category of categories) {
    const res = await fetch(
      `https://api.shoal.xyz/v1/radar/byCategory?category=${category}&since=${cursor}`,
      { headers: HEADERS }
    );
    const { data } = await res.json();
    for (const event of data) {
      console.log(`[${event.eventCategory}] ${event.title}`);
    }
    if (data.length > 0) {
      const latest = data[0].createdAt;
      if (latest > cursor) cursor = latest;
    }
  }
}

setInterval(pollBDEvents, 15 * 60 * 1000); // Poll every 15 minutes

Step 3: Get a Daily Briefing

Use /v1/brief/batch with your watchlist IDs for a single-call portfolio update when batch brief is available on your plan.
cURL
# Use the IDs returned from Step 1 (e.g. from byOrganizationName lookups)
SINCE=$(date -u -v-24H +"%Y-%m-%dT%H:%M:%SZ")  # macOS
# SINCE=$(date -u -d '24 hours ago' +"%Y-%m-%dT%H:%M:%SZ")  # Linux

curl "https://api.shoal.xyz/v1/brief/batch?ids=YOUR_ORG_IDS&since=$SINCE&compact=true" \
  -H "Authorization: Bearer YOUR_API_KEY"
Python
ids = ",".join(watchlist_ids)
since = (datetime.now(timezone.utc) - timedelta(hours=24)).isoformat()

r = requests.get(
    "https://api.shoal.xyz/v1/brief/batch",
    headers=HEADERS,
    params={"ids": ids, "since": since, "compact": "true"},
)

data = r.json()
for org in data["organizations"]:
    radar_count = org["counts"]["radar"]
    signal_count = org["counts"]["signal"]
    if radar_count > 0 or signal_count > 0:
        print(f"{org['label']}: {radar_count} radar, {signal_count} signals")
        for event in org.get("radar", []):
            print(f"  [{event['eventCategory']}] {event['title']}")
JavaScript
const ids = watchlistIds.join(',');
const since = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();

const briefRes = await fetch(
  `https://api.shoal.xyz/v1/brief/batch?ids=${ids}&since=${since}&compact=true`,
  { headers: HEADERS }
);

const briefData = await briefRes.json();
for (const org of briefData.organizations) {
  const { radar, signal } = org.counts;
  if (radar > 0 || signal > 0) {
    console.log(`${org.label}: ${radar} radar, ${signal} signals`);
    for (const event of org.radar || []) {
      console.log(`  [${event.eventCategory}] ${event.title}`);
    }
  }
}

Example Output

The /v1/brief/batch response groups events by organization. See the full schema for all available fields.
{
  "organizations": [
    {
      "id": 42,
      "label": "Chainlink",
      "counts": { "radar": 2, "signal": 5 },
      "radar": [
        {
          "eventCategory": "partnership",
          "title": "Chainlink CCIP integration with major TradFi institution",
          "signal": 9,
          "latestPostTimestamp": "2026-03-09T14:22:00Z"
        },
        {
          "eventCategory": "funding",
          "title": "Chainlink ecosystem fund announces new cohort",
          "signal": 7,
          "latestPostTimestamp": "2026-03-09T09:15:00Z"
        }
      ]
    }
  ],
  "next_cursor": null
}

Lead Enrichment

Enrich your CRM contacts with Shoal intelligence before outreach — match company names to tracked organizations and surface recent events.

Lead & Company Enrichment

Full walkthrough with examples: input format, CLI usage, MCP tool, and API-only approach.

Tips

The since parameter is required on batch and polling endpoints. Always pass it to avoid fetching stale data.
  • Use compact=true on batch calls to reduce payload size.
  • Poll every 15-60 minutes depending on how time-sensitive your BD pipeline is.
  • Use the signal score from /v1/signal/top to prioritize outreach — higher scores indicate more significant events.
  • Combine partnership radar with signal spikes to identify the highest-conviction opportunities.