Skip to main content
Version: 1.3.1

Searching Messages

The SDK provides two methods for searching CCIP messages via the API:

MethodUse when
searchAllMessagesYou want to iterate all matching results. Handles pagination automatically.
searchMessagesYou need page-level control — explicit cursor handling, partial fetching, or custom page sizes.

Both require the CCIP API (enabled by default).

Filters

All filters are optional. Omit a field to leave it unfiltered.

FilterTypeDescription
senderstringSender address
receiverstringReceiver address
sourceChainSelectorbigintSource chain selector
destChainSelectorbigintDestination chain selector
sourceTransactionHashstringSource transaction hash
readyForManualExecOnlybooleanWhen true, return only messages eligible for manual execution
note

api.getMessageIdsInTx(txHash) is a convenience wrapper that calls searchMessages with sourceTransactionHash internally.

Iterate All Results

searchAllMessages is an async generator that yields one MessageSearchResult at a time, fetching pages internally:

TypeScript
import { CCIPAPIClient } from '@chainlink/ccip-sdk'

const api = CCIPAPIClient.fromUrl()

for await (const msg of api.searchAllMessages({ sender: '0x9d087fC03ae39b088326b67fA3C788236645b717' })) {
console.log(msg.messageId, msg.status)
console.log(` ${msg.sourceNetworkInfo.name}${msg.destNetworkInfo.name}`)
}

Stop Early

Break out of the loop to stop fetching pages:

TypeScript
import { type MessageSearchResult, CCIPAPIClient } from '@chainlink/ccip-sdk'

const api = CCIPAPIClient.fromUrl()

const results: MessageSearchResult[] = []
for await (const msg of api.searchAllMessages({ sender: '0x...' })) {
results.push(msg)
if (results.length >= 10) break
}

Control Page Size

The limit option controls how many results the API returns per page (not the total):

TypeScript
for await (const msg of api.searchAllMessages({ sender: '0x...' }, { limit: 50 })) {
console.log(msg.messageId)
}

Manual Pagination

searchMessages returns a MessageSearchPage with data, hasNextPage, and cursor:

TypeScript
import { CCIPAPIClient } from '@chainlink/ccip-sdk'

const api = CCIPAPIClient.fromUrl()

// Fetch first page
let page = await api.searchMessages(
{ sender: '0x9d087fC03ae39b088326b67fA3C788236645b717' },
{ limit: 10 },
)

console.log(`Page 1: ${page.data.length} results`)

// Fetch subsequent pages using the cursor
while (page.hasNextPage) {
page = await api.searchMessages(undefined, { cursor: page.cursor!, limit: 10 })
console.log(`Next page: ${page.data.length} results`)
}
note

When paginating with a cursor, the filters parameter is ignored — the cursor encodes the original filters.

Combine Filters

TypeScript
import { CCIPAPIClient } from '@chainlink/ccip-sdk'

const api = CCIPAPIClient.fromUrl()

const page = await api.searchMessages({
sender: '0x9d087fC03ae39b088326b67fA3C788236645b717',
sourceChainSelector: 16015286601757825753n,
destChainSelector: 14767482510784806043n,
})

for (const msg of page.data) {
console.log(`${msg.messageId}: ${msg.status}`)
}

Find Stuck Messages

Filter for messages that are eligible for manual execution:

TypeScript
for await (const msg of api.searchAllMessages({ readyForManualExecOnly: true })) {
console.log(`Stuck: ${msg.messageId} (${msg.sourceNetworkInfo.name}${msg.destNetworkInfo.name})`)
}

Combine with sender to find stuck messages for a specific address:

TypeScript
for await (const msg of api.searchAllMessages({
sender: '0x...',
readyForManualExecOnly: true,
})) {
console.log(msg.messageId, msg.status)
}

Get Full Message Details

MessageSearchResult contains summary fields only. To get the full message (extraArgs, tokenAmounts, fees, execution details), call getMessageById:

TypeScript
import { CCIPAPIClient, MessageStatus } from '@chainlink/ccip-sdk'

const api = CCIPAPIClient.fromUrl()

for await (const msg of api.searchAllMessages({ sender: '0x...' })) {
if (msg.status === MessageStatus.Failed) {
const full = await api.getMessageById(msg.messageId)
console.log('Receipt tx:', full.metadata.receiptTransactionHash)
if (full.metadata.deliveryTime != null) {
console.log('Delivery time:', Number(full.metadata.deliveryTime), 'ms')
}
}
}

Pass an AbortSignal to cancel in-flight requests:

TypeScript
import { CCIPAPIClient, CCIPAbortError } from '@chainlink/ccip-sdk'

const api = CCIPAPIClient.fromUrl()
const controller = new AbortController()

// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5000)

try {
for await (const msg of api.searchAllMessages(
{ sender: '0x...' },
{ signal: controller.signal },
)) {
console.log(msg.messageId)
}
} catch (err) {
if (err instanceof CCIPAbortError) {
console.log('Search cancelled')
}
}

searchMessages also accepts signal in its options:

TypeScript
const page = await api.searchMessages(
{ sender: '0x...' },
{ limit: 10, signal: controller.signal },
)

MessageSearchResult Fields

Each result contains:

FieldTypeDescription
messageIdstringCCIP message ID (0x-prefixed, 32-byte hex)
originstringTransaction originator (EOA that submitted the send tx)
senderstringMessage sender address
receiverstringMessage receiver address
statusMessageStatusLifecycle status (use MessageStatus.Sent, MessageStatus.Success, etc.)
sourceNetworkInfoNetworkInfoSource chain name, selector, network type, family, and chain ID
destNetworkInfoNetworkInfoDestination chain name, selector, network type, family, and chain ID
sendTransactionHashstringSource chain transaction hash
sendTimestampstringISO 8601 send timestamp

Error Handling

ErrorWhen
CCIPTimeoutErrorRequest timed out
CCIPAbortErrorRequest cancelled via AbortSignal
CCIPHttpErrorAPI returned 4xx/5xx (except 404)
note

A 404 response is treated as "no results found" and returns an empty page (or yields nothing from searchAllMessages) — it does not throw.

Method Reference

MethodReturnsPagination
api.searchAllMessages(filters?, options?)AsyncGenerator<MessageSearchResult>Automatic
api.searchMessages(filters?, options?)MessageSearchPageManual (cursor)