Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,8 @@
"edge/en/tools/search-research/serpapi-googlesearchtool",
"edge/en/tools/search-research/serpapi-googleshoppingtool",
"edge/en/tools/search-research/databricks-query-tool",
"edge/en/tools/search-research/youai-search"
"edge/en/tools/search-research/youai-search",
"edge/en/tools/search-research/livetennistool"
]
},
{
Expand Down
78 changes: 78 additions & 0 deletions docs/edge/en/tools/search-research/livetennistool.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
title: Live Tennis Tool
description: Live tennis scores, fixtures, player profiles and rankings for CrewAI agents via the Live Tennis API.
icon: trophy
mode: "wide"
---

# `LiveTennisTool`

## Description

Query professional tennis data from the [Live Tennis API](https://livetennisapi.com): matches currently in play with live scores, upcoming matches, scheduled fixtures, player search and profiles (including current ranking), published ranking tables, and your API quota usage.

The tool is scoped to REST endpoints, most of which are available on the API's free tier (keyed, 30 requests/minute, 100 requests/day), so it can be tried without payment. Completed-match history is a paid feature of the API and is not part of this tool.

## Environment Variables

```bash
LIVETENNIS_API_KEY=your_api_key # Free key at livetennisapi.com
```

## Actions

| `action` | What it returns | Plan |
|---|---|---|
| `live_matches` | Matches in play with current scores (optional `tour` filter) | Free |
| `upcoming_matches` | Matches starting soon (optional `tour` filter) | Free |
| `fixtures` | Scheduled matches (optional `tour` filter) | Free |
| `search_players` | Player search by name (requires `search`) | Free |
| `player_profile` | Single player detail, including current ranking (requires `player_id`, the numeric id from `search_players`) | Free |
| `rankings` | Published ranking table (requires `system`: `atp`, `wta`, `itf_jt`, `itf_mt`, `itf_wt`; UTR has no listing — it is a rating, not a ranking) | PRO |
| `usage` | Your API quota vs. consumption | Free |

### Parameters

- `tour` — optional filter for `live_matches`, `upcoming_matches` and `fixtures`: one of `atp`, `wta`, `challenger`, `itf`, `juniors`. Omit for all tours.
- `limit` / `offset` — pagination for the list actions (`live_matches`, `upcoming_matches`, `fixtures`, `search_players`, `rankings`). The API default is 50 results.

## Basic Usage

```python
from crewai import Agent
from crewai_tools import LiveTennisTool

tool = LiveTennisTool()

reporter = Agent(
role="Tennis Reporter",
goal="Summarise what is happening on tour right now",
backstory="You follow professional tennis and report live developments.",
tools=[tool],
)
```

## Direct Invocation

```python
import json

from crewai_tools import LiveTennisTool

tool = LiveTennisTool()

# Matches in play right now
print(tool.run(action="live_matches", tour="atp"))

# Find a player, then load their profile using the id from the search result
result = tool.run(action="search_players", search="alcaraz")
players = json.loads(result).get("data", []) if result.startswith("{") else []
if players: # result is a readable error string when the call fails
profile = tool.run(action="player_profile", player_id=players[0]["id"])
```

## Error Handling

The tool returns readable messages (rather than raising) when the API key is missing, invalid (401), the endpoint needs a higher plan (403), or the rate limit is hit (429), so an agent can recover or report the problem.

Full API documentation: [docs.livetennisapi.com](https://docs.livetennisapi.com).
2 changes: 2 additions & 0 deletions lib/crewai-tools/src/crewai_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
)
from crewai_tools.tools.json_search_tool.json_search_tool import JSONSearchTool
from crewai_tools.tools.linkup.linkup_search_tool import LinkupSearchTool
from crewai_tools.tools.live_tennis_tool.live_tennis_tool import LiveTennisTool
from crewai_tools.tools.llamaindex_tool.llamaindex_tool import LlamaIndexTool
from crewai_tools.tools.mdx_search_tool.mdx_search_tool import MDXSearchTool
from crewai_tools.tools.merge_agent_handler_tool.merge_agent_handler_tool import (
Expand Down Expand Up @@ -280,6 +281,7 @@
"JSONSearchTool",
"JinaScrapeWebsiteTool",
"LinkupSearchTool",
"LiveTennisTool",
"LlamaIndexTool",
"MCPServerAdapter",
"MDXSearchTool",
Expand Down
2 changes: 2 additions & 0 deletions lib/crewai-tools/src/crewai_tools/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
)
from crewai_tools.tools.json_search_tool.json_search_tool import JSONSearchTool
from crewai_tools.tools.linkup.linkup_search_tool import LinkupSearchTool
from crewai_tools.tools.live_tennis_tool.live_tennis_tool import LiveTennisTool
from crewai_tools.tools.llamaindex_tool.llamaindex_tool import LlamaIndexTool
from crewai_tools.tools.mdx_search_tool.mdx_search_tool import MDXSearchTool
from crewai_tools.tools.merge_agent_handler_tool.merge_agent_handler_tool import (
Expand Down Expand Up @@ -264,6 +265,7 @@
"JSONSearchTool",
"JinaScrapeWebsiteTool",
"LinkupSearchTool",
"LiveTennisTool",
"LlamaIndexTool",
"MDXSearchTool",
"MergeAgentHandlerTool",
Expand Down
69 changes: 69 additions & 0 deletions lib/crewai-tools/src/crewai_tools/tools/live_tennis_tool/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Live Tennis Tool

Query professional tennis data from the [Live Tennis API](https://livetennisapi.com) — live match scores, upcoming matches, scheduled fixtures, player search and profiles, ranking tables, and API usage.

The tool is scoped to REST endpoints, most of which are available on the free tier, so it can be tried without payment.

## Actions

| `action` | Endpoint | Plan | Notes |
|---|---|---|---|
| `live_matches` | `GET /matches?status=live` | Free | Matches in play with current scores; optional `tour` |
| `upcoming_matches` | `GET /matches?status=upcoming` | Free | Matches starting soon; optional `tour` |
| `fixtures` | `GET /fixtures` | Free | Scheduled matches; optional `tour` |
| `search_players` | `GET /players?search=` | Free | Requires `search` |
| `player_profile` | `GET /players/{id}` | Free | Requires `player_id` (numeric id from `search_players`); includes current ranking |
| `rankings` | `GET /rankings?system=` | PRO | Requires `system` (`atp`, `wta`, `itf_jt`, `itf_mt`, `itf_wt`); UTR has no listing (it is a rating, not a ranking) |
| `usage` | `GET /usage` | Free | Your quota vs. consumption; exempt from quota |
Comment thread
coderabbitai[bot] marked this conversation as resolved.

### Parameters

- `tour` — optional filter for `live_matches`, `upcoming_matches` and `fixtures`: one of `atp`, `wta`, `challenger`, `itf`, `juniors`. Omit for all tours.
- `limit` / `offset` — pagination for the list actions (`live_matches`, `upcoming_matches`, `fixtures`, `search_players`, `rankings`). The API default is 50 results.

The free tier is keyed and rate-limited to 30 requests/minute and 100 requests/day. Completed-match history is a paid feature and is not part of this tool. The API also offers a WebSocket push feed and model win probability on its top tier; this tool intentionally sticks to the polling REST surface.

## Environment Variables

```env
LIVETENNIS_API_KEY=your_api_key
```

Get a free key at [livetennisapi.com](https://livetennisapi.com). Full API documentation: [docs.livetennisapi.com](https://docs.livetennisapi.com).

## Example Usage

```python
import json

from crewai_tools import LiveTennisTool

tool = LiveTennisTool()

# Matches in play right now
print(tool.run(action="live_matches", tour="atp"))

# Find a player, then load their profile using the id from the search result
result = tool.run(action="search_players", search="alcaraz")
players = json.loads(result).get("data", []) if result.startswith("{") else []
if players: # result is a readable error string when the call fails
profile = tool.run(action="player_profile", player_id=players[0]["id"])
```

## With an Agent

```python
from crewai import Agent
from crewai_tools import LiveTennisTool

reporter = Agent(
role="Tennis Reporter",
goal="Summarise what is happening on tour right now",
backstory="You follow professional tennis and report live developments.",
tools=[LiveTennisTool()],
)
```

## Error Handling

The tool returns readable messages (rather than raising) when the API key is missing, invalid (401), the endpoint needs a higher plan (403), or the rate limit is hit (429), so an agent can recover or report the problem.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""Live Tennis API tool package: live scores, fixtures, players, rankings."""

from crewai_tools.tools.live_tennis_tool.live_tennis_tool import LiveTennisTool
from crewai_tools.tools.live_tennis_tool.schemas import LiveTennisToolSchema


__all__ = [
"LiveTennisTool",
"LiveTennisToolSchema",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"""Tool for querying professional tennis data from the Live Tennis API."""

from __future__ import annotations

import json
import os
from typing import Any

from crewai.tools import BaseTool, EnvVar
from pydantic import BaseModel, Field, ValidationError
import requests

from crewai_tools.tools.live_tennis_tool.schemas import LiveTennisToolSchema


API_KEY_ENV_VAR = "LIVETENNIS_API_KEY"


class LiveTennisTool(BaseTool):
"""Query the Live Tennis API (https://livetennisapi.com).

Covers the endpoints available on the free tier: live match scores,
upcoming matches, scheduled fixtures, player search and profiles
(including current ranking), and API usage. The 'rankings' action
(full published ranking tables) requires a paid plan.
"""

name: str = "Live Tennis API"
description: str = (
"Fetches professional tennis data from the Live Tennis API: matches "
"currently in play with live scores, upcoming matches, scheduled "
"fixtures, player search and profiles (including current ranking and "
"ranking points), published ranking tables, and your API quota usage. "
"Results are returned as JSON."
)
args_schema: type[BaseModel] = LiveTennisToolSchema
base_url: str = "https://api.livetennisapi.com/api/public/v1"
request_timeout: float = 30.0
env_vars: list[EnvVar] = Field(
default_factory=lambda: [
EnvVar(
name=API_KEY_ENV_VAR,
description="API key for the Live Tennis API (free tier available)",
required=True,
),
]
)

def _run(self, **kwargs: Any) -> str:
"""Validate the arguments, call the API, and return the response.

Returns the endpoint's JSON as a string on success, or a readable
error message (missing key, invalid arguments, 401/403/429/other
HTTP errors, network failure) so an agent can recover.
"""
api_key = os.environ.get(API_KEY_ENV_VAR, "").strip()
if not api_key:
return (
f"The {API_KEY_ENV_VAR} environment variable is not set. "
"Get a free API key at https://livetennisapi.com and set "
f"{API_KEY_ENV_VAR} to use this tool."
)

try:
params = LiveTennisToolSchema(**kwargs)
except ValidationError as e:
return f"Invalid arguments for the Live Tennis API tool: {e}"

path, query = self._build_request(params)
try:
response = requests.get(
f"{self.base_url}{path}",
headers={"Authorization": f"Bearer {api_key}"},
params=query,
timeout=self.request_timeout,
)
except requests.RequestException as e:
return f"Live Tennis API request failed: {e}"

if response.status_code == 401:
return (
"Live Tennis API rejected the request (401): the "
f"{API_KEY_ENV_VAR} key is missing, unknown, or disabled."
)
if response.status_code == 403:
return (
"Live Tennis API returned 403: this endpoint requires a higher "
f"subscription tier than the current key provides. {response.text}"
)
if response.status_code == 429:
return f"Live Tennis API rate limit exceeded (429): {response.text}"
if not response.ok:
return (
f"Live Tennis API returned HTTP {response.status_code}: {response.text}"
)

try:
return json.dumps(response.json())
except ValueError:
return response.text

@staticmethod
def _build_request(params: LiveTennisToolSchema) -> tuple[str, dict[str, Any]]:
"""Map a validated action to its endpoint path and query parameters."""
query: dict[str, Any] = {}
if params.limit is not None:
query["limit"] = params.limit
if params.offset is not None:
query["offset"] = params.offset

if params.action in ("live_matches", "upcoming_matches"):
query["status"] = "live" if params.action == "live_matches" else "upcoming"
if params.tour:
query["tour"] = params.tour
return "/matches", query
if params.action == "fixtures":
if params.tour:
query["tour"] = params.tour
return "/fixtures", query
if params.action == "search_players":
query["search"] = params.search
return "/players", query
if params.action == "player_profile":
return f"/players/{params.player_id}", {}
if params.action == "rankings":
query["system"] = params.system
return "/rankings", query
return "/usage", {}
Loading