This guide gives Claude a search_listings tool backed by Trawley's
hybrid search endpoint, using the Anthropic
TypeScript SDK and its tool-use loop.
bash
npm install @anthropic-ai/sdk
Define the tool
A tool is a name, a description, and a JSON Schema for its input.
ts
const searchListings = {
name: 'search_listings',
description:
'Search live property listings from acmehomes.co.uk. Accepts a natural ' +
'language query; constraints like price, bedrooms, or date are understood. ' +
'Returns structured records.',
input_schema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'What to find, e.g. "3 bed houses under £500k with a garden"',
},
},
required: ['query'],
},
} as const
const TRAWLEY_SCRAPER_ID = 'scr_8f2a1c9e'
async function runSearch(query: string) {
const params = new URLSearchParams({ search: query, take: '10' })
const res = await fetch(
`https://api.trawley.ai/v1/scrapers/${TRAWLEY_SCRAPER_ID}/hybrid?${params}`,
)
const { data } = await res.json()
return data
}
Run the tool-use loop
Claude decides when to call the tool. When it does, run the search and send the result back so it can finish its answer.
ts
import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic()
const messages: Anthropic.MessageParam[] = [
{ role: 'user', content: 'Find a 3 bed house near Kendal with a garden under £500k.' },
]
while (true) {
const response = await client.messages.create({
model: 'claude-sonnet-4-5',
max_tokens: 1024,
tools: [searchListings],
messages,
})
messages.push({ role: 'assistant', content: response.content })
const toolUse = response.content.find((block) => block.type === 'tool_use')
if (response.stop_reason !== 'tool_use' || !toolUse) {
// No tool call — Claude has answered.
const text = response.content.find((b) => b.type === 'text')
console.log(text?.text)
break
}
const results = await runSearch((toolUse.input as { query: string }).query)
messages.push({
role: 'user',
content: [
{
type: 'tool_result',
tool_use_id: toolUse.id,
content: JSON.stringify(results),
},
],
})
}
The shape is always the same: send the tools, watch for a tool_use block, run
your function, and return a tool_result with the matching tool_use_id. Claude
loops until it has what it needs to answer.
Tips
- Trim the records you return to the fields Claude needs. Smaller tool results are cheaper and keep the answer focused.
- One query input is enough. Hybrid search interprets price, bedrooms, and dates from the query string, so you do not need a parameter per filter.
- Return an empty array gracefully when a scraper has no completed run, so Claude can say nothing was found.