Skip to main content

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/allBrowse/search the org directoryDocs
/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.
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']}")

print(f"Credits used: {data['creditCost']}")
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}`);
    }
  }
}
console.log(`Credits used: ${briefData.creditCost}`);

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"
        }
      ]
    }
  ],
  "creditCost": 4
}

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 and to minimize credit usage.
  • Use compact=true on batch calls to reduce payload size and stay under your credit budget.
  • 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.