Trawley
Integration guides

OpenAI function calling

Expose your Trawley scraper as a function the OpenAI models can call.

This guide defines a search_listings function that the OpenAI models can call, backed by Trawley's hybrid search endpoint.

bash
npm install openai

Define the function

ts
const tools = [
  {
    type: 'function',
    function: {
      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.',
      parameters: {
        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 call loop

ts
import OpenAI from 'openai'

const client = new OpenAI()

const messages: OpenAI.ChatCompletionMessageParam[] = [
  { role: 'user', content: 'Find a 3 bed house near Kendal with a garden under £500k.' },
]

while (true) {
  const completion = await client.chat.completions.create({
    model: 'gpt-4.1',
    messages,
    tools,
  })

  const message = completion.choices[0].message
  messages.push(message)

  if (!message.tool_calls?.length) {
    console.log(message.content)
    break
  }

  for (const call of message.tool_calls) {
    const { query } = JSON.parse(call.function.arguments)
    const results = await runSearch(query)
    messages.push({
      role: 'tool',
      tool_call_id: call.id,
      content: JSON.stringify(results),
    })
  }
}

After running a function, push a role: 'tool' message with the matching tool_call_id. The model reads the result and continues until it can answer in plain text.

What's next