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.
You’ll need an API key to follow along. See the Quickstart guide to create one.
Endpoints Used
Endpoint Purpose Reference /v1/radar/byCategoryPartnership & acquisition events Docs /v1/signal/topHighest-impact events across all orgs Docs /v1/brief/batchCombined view for a watchlist of orgs Docs /v1/organizations/byOrganizationNameLook up org by name Docs
Workflow
Step 1: Build a Watchlist
Search for organizations by name and collect their IDs into a watchlist.
curl "https://api.shoal.xyz/v1/organizations/byOrganizationName?name=Chainlink" \
-H "Authorization: Bearer YOUR_API_KEY"
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) } " )
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.
# 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"
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
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.
# 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"
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' ] } " )
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.