> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fieldfunded.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a Telegram Sports Odds Bot with Inline Search

> Build a Telegram bot with /odds, /scores, and inline search. Type @yourbot Lakers in any chat to see live odds. Node.js + FieldFunded SDK.

# Build a Telegram Odds Bot with Inline Search

A Telegram bot that serves live odds via commands and inline search. Users can type `@yourbot Lakers` in any chat to see odds without opening the bot.

## What You'll Use

| SDK Method       | Endpoint                   | Purpose                      |
| ---------------- | -------------------------- | ---------------------------- |
| `search()`       | `GET /v1/search`           | Find events by team name     |
| `getEventOdds()` | `GET /v1/events/{id}/odds` | Get odds for a matched event |
| `getScores()`    | `GET /v1/scores`           | List all live scores         |
| `getSports()`    | `GET /v1/sports`           | List available sports        |

## Prerequisites

* Node.js 18+
* A Telegram bot token (from [@BotFather](https://t.me/BotFather))
* A free FieldFunded API key

```bash theme={null}
npm install telegraf @fieldfunded/sdk
```

## Step 1: Initialize

```typescript theme={null}
import { Telegraf } from 'telegraf';
import { FieldFundedSDK } from '@fieldfunded/sdk';

const bot = new Telegraf(process.env.TELEGRAM_TOKEN!);
const ff = new FieldFundedSDK({
  apiKey: process.env.FIELDFUNDED_API_KEY!,
  baseUrl: 'https://api.fieldfunded.com/v1',
});
```

## Step 2: `/odds` Command

```typescript theme={null}
bot.command('odds', async (ctx) => {
  const team = ctx.message.text.split('/odds ')[1];
  if (!team) {
    return ctx.reply('Usage: /odds <team name>\nExample: /odds Barcelona');
  }

  const results = await ff.search(team, { limit: 3 });

  if (!results.events || results.events.length === 0) {
    return ctx.reply(`No events found for "${team}"`);
  }

  for (const event of results.events) {
    const odds = await ff.getEventOdds(event.id);
    const mainMarket = odds.markets?.[0];

    let message = `⚽ *${event.home_team} vs ${event.away_team}*\n`;
    message += `📍 ${event.league} • ${event.status}\n`;

    if (mainMarket) {
      message += `\n📊 *${mainMarket.name}*\n`;
      for (const sel of mainMarket.selections) {
        message += `  ${sel.name}: \`${sel.odds}\`\n`;
      }
    }

    await ctx.reply(message, { parse_mode: 'Markdown' });
  }
});
```

## Step 3: `/scores` Command

```typescript theme={null}
bot.command('scores', async (ctx) => {
  const sport = ctx.message.text.split('/scores ')[1] || undefined;
  const data = await ff.getScores({ sport });

  if (!data.scores || data.scores.length === 0) {
    return ctx.reply('No live games right now 📺');
  }

  let message = '🔴 *Live Scores*\n\n';

  for (const game of data.scores.slice(0, 15)) {
    message += `${game.home_team} *${game.score.home}-${game.score.away}* ${game.away_team}\n`;
    message += `  _${game.league}_\n\n`;
  }

  await ctx.reply(message, { parse_mode: 'Markdown' });
});
```

## Step 4: Inline Search (the killer feature)

Users type `@yourbot Lakers` in **any chat** and get odds results as inline cards:

```typescript theme={null}
bot.on('inline_query', async (ctx) => {
  const query = ctx.inlineQuery.query;
  if (!query || query.length < 2) return;

  const results = await ff.search(query, { limit: 5 });

  if (!results.events) return ctx.answerInlineQuery([]);

  const inlineResults = await Promise.all(
    results.events.map(async (event: any, i: number) => {
      const odds = await ff.getEventOdds(event.id);
      const mainMarket = odds.markets?.[0];

      let description = `${event.league} • ${event.status}`;
      let message = `⚽ *${event.home_team} vs ${event.away_team}*\n`;
      message += `📍 ${event.league}\n`;

      if (mainMarket) {
        const oddsLine = mainMarket.selections
          .map((s: any) => `${s.name}: ${s.odds}`)
          .join(' | ');
        message += `📊 ${mainMarket.name}: ${oddsLine}`;
        description += ` | ${oddsLine}`;
      }

      return {
        type: 'article' as const,
        id: String(i),
        title: `${event.home_team} vs ${event.away_team}`,
        description,
        input_message_content: {
          message_text: message,
          parse_mode: 'Markdown' as const,
        },
      };
    })
  );

  await ctx.answerInlineQuery(inlineResults);
});
```

## Step 5: Launch

```typescript theme={null}
bot.launch();
console.log('Bot is running!');

// Graceful shutdown
process.once('SIGINT', () => bot.stop('SIGINT'));
process.once('SIGTERM', () => bot.stop('SIGTERM'));
```

## Deploy

Deploy to [Render](https://render.com) or [Railway](https://railway.app) (both have free tiers):

```bash theme={null}
# Set environment variables
TELEGRAM_TOKEN=your_telegram_token
FIELDFUNDED_API_KEY=your_api_key
```

## Rate Limit Math

* `/odds` = 2 requests (search + odds)
* `/scores` = 1 request
* Inline query = 1-6 requests (search + odds per result)
* Free tier handles **\~2,000 bot interactions/month**

<Card title="Search API Reference" icon="magnifying-glass" href="/api-reference/events/search-events">
  See search endpoint docs →
</Card>

***

## Related Guides

* [Build a Discord Bot](/guides/build-discord-bot) — same concept for Discord servers
* [Build WhatsApp Alerts](/guides/build-whatsapp-alerts) — automated alerts to your phone
* [Build a Betting Platform](/guides/build-betting-platform) — full-stack architecture

<CardGroup cols={2}>
  <Card title="Get Your Free API Key" icon="key" href="/quickstart">
    Start building in 5 minutes — 10,000 free requests/month
  </Card>

  <Card title="See Pricing" icon="dollar-sign" href="/compare/pricing">
    All plans compared side by side
  </Card>
</CardGroup>
