Tools
X Search
The X Search tool enables Grok to perform keyword search, semantic search, user search, and thread fetch on X (formerly Twitter). This powerful tool allows the model to access real-time social media content, analyze posts, and gather insights from X's vast data.
X Search is billed at $5 per 1k posts fetched and $10 per 1k user profiles fetched, in addition to token costs; see tool invocation costs for what counts as a fetched post or profile.
SDK Support
| SDK/API | Tool Name |
|---|---|
| Responses API | x_search |
| Vercel AI SDK | xai.tools.xSearch() |
This tool is also supported in all Responses API compatible SDKs.
Basic Usage
import { xai } from '@ai-sdk/xai';
import { generateText } from 'ai';
const { text, sources } = await generateText({
model: xai.responses('grok-4.7'),
prompt: 'What are people saying about SpaceXAI on X?',
tools: {
x_search: xai.tools.xSearch(),
},
});
console.log(text);
console.log('Citations:', sources);
import os
from openai import OpenAI
api_key = os.getenv("XAI_API_KEY")
client = OpenAI(
api_key=api_key,
base_url="https://api.x.ai/v1",
)
response = client.responses.create(
model="grok-4.7",
input=[
{
"role": "user",
"content": "What are people saying about SpaceXAI on X?",
},
],
tools=[
{
"type": "x_search",
},
],
)
print(response)
curl https://api.x.ai/v1/responses \\
-H "Content-Type: application/json" \\
-H "Authorization: Bearer $XAI_API_KEY" \\
-d '{
"model": "grok-4.7",
"input": [
{
"role": "user",
"content": "What are people saying about SpaceXAI on X?"
}
],
"tools": [
{
"type": "x_search"
}
]
}'
X Search Parameters
| Parameter | Type | Description |
|---|---|---|
allowed_x_handles |
array of strings | Only consider posts from these handles, written without the @ (max 20; 10 in the Vercel AI SDK) |
excluded_x_handles |
array of strings | Ignore posts from these handles, written without the @ (max 20; 10 in the Vercel AI SDK) |
from_date |
string | Start of the date range (YYYY-MM-DD) |
to_date |
string | End of the date range (YYYY-MM-DD) |
enable_image_understanding |
boolean | Let the model look at images in the posts it finds. Defaults to false. |
enable_video_understanding |
boolean | Let the model watch videos in the posts it finds. Defaults to false. |
Only Consider Posts from Specific Handles
Use allowed_x_handles to consider X posts only from a given list of X handles.
[!NOTE]
allowed_x_handlescannot be set together withexcluded_x_handlesin the same request.
const { text } = await generateText({
model: xai.responses('grok-4.7'),
prompt: 'What has SpaceX posted about Starship recently?',
tools: {
x_search: xai.tools.xSearch({
allowedXHandles: ['SpaceX'],
}),
},
});
response = client.responses.create(
model="grok-4.7",
input=[{"role": "user", "content": "What has SpaceX posted about Starship recently?"}],
tools=[
{
"type": "x_search",
"allowed_x_handles": ["SpaceX"],
},
],
)
Exclude Posts from Specific Handles
Use excluded_x_handles to prevent the model from including X posts from the specified handles in any X search tool invocations.
const { text } = await generateText({
model: xai.responses('grok-4.7'),
prompt: 'How are people reacting to the latest Tesla delivery numbers?',
tools: {
x_search: xai.tools.xSearch({
excludedXHandles: ['Tesla'],
}),
},
});
response = client.responses.create(
model="grok-4.7",
input=[{"role": "user", "content": "How are people reacting to the latest Tesla delivery numbers?"}],
tools=[
{
"type": "x_search",
"excluded_x_handles": ["Tesla"],
},
],
)
Date Range
You can restrict the date range of search data used by specifying from_date and to_date. This limits the data to posts from 00:00 UTC on from_date up to, but not including, to_date. To search a single day, set to_date to the day after it.
Use YYYY-MM-DD for both fields, e.g., "2026-09-28". Dates in any other format, including full date-times, aren't applied.
const { text } = await generateText({
model: xai.responses('grok-4.7'),
prompt: 'How did people react to the Starship launch?',
tools: {
x_search: xai.tools.xSearch({
fromDate: '2026-09-28',
toDate: '2026-10-01',
}),
},
});
response = client.responses.create(
model="grok-4.7",
input=[{"role": "user", "content": "How did people react to the Starship launch?"}],
tools=[
{
"type": "x_search",
"from_date": "2026-09-28",
"to_date": "2026-10-01",
},
],
)
Enable Image Understanding
Setting enable_image_understanding to true allows the agent to analyze images in X posts encountered during the search process.
const { text } = await generateText({
model: xai.responses('grok-4.7'),
prompt: 'Find X posts with images about AI',
tools: {
x_search: xai.tools.xSearch({
enableImageUnderstanding: true,
}),
},
});
response = client.responses.create(
model="grok-4.7",
input=[{"role": "user", "content": "Find X posts with images about AI"}],
tools=[
{
"type": "x_search",
"enable_image_understanding": True,
},
],
)
Enable Video Understanding
Setting enable_video_understanding to true allows the agent to analyze videos in X posts. This is only available for X Search (not Web Search).
const { text } = await generateText({
model: xai.responses('grok-4.7'),
prompt: 'Find X posts with videos about AI',
tools: {
x_search: xai.tools.xSearch({
enableVideoUnderstanding: true,
}),
},
});
response = client.responses.create(
model="grok-4.7",
input=[{"role": "user", "content": "Find X posts with videos about AI"}],
tools=[
{
"type": "x_search",
"enable_video_understanding": True,
},
],
)
How X Search Works
When you add x_search to tools, the model decides which of the functions below to call, and how often. It reads the posts it fetches and answers with citations. Each call appears in the response output as a custom_tool_call item named after the function, and the response reports the usage counts.
| Function | What the model uses it for | Billed as |
|---|---|---|
x_keyword_search |
Keyword search with X's advanced search operators, sorted by Top or Latest |
Posts fetched |
x_semantic_search |
Search by meaning rather than exact words | Posts fetched |
x_user_search |
Find users by name or description | Profiles fetched |
x_thread_fetch |
Fetch one post with its parent posts and replies | Posts fetched |
With image or video understanding turned on, the model can also view images and videos in the posts it finds, billed as image tokens.
Your handle and date parameters apply to every keyword and semantic search, whatever query the model writes. Thread fetches don't use them.
Examples
Outputs are abridged; results vary because the model picks its own queries.
Keep Costs Down
To spend less, give the model less to fetch: narrow the handles and dates, ask for fewer rounds of searching with max_turns, and check the fetch counts in the response.
import { xai } from '@ai-sdk/xai';
import { generateText } from 'ai';
const { response } = await generateText({
model: xai.responses('grok-4.7'),
prompt: 'What did SpaceX post about Starship in September 2026?',
tools: { x_search: xai.tools.xSearch({ allowedXHandles: ['SpaceX'], fromDate: '2026-09-01', toDate: '2026-10-01' }) },
providerOptions: { xai: { maxTurns: 2 } },
include: { responseBody: true },
});
// response.body is the raw Responses API JSON.
const usage = Object(response.body).usage;
console.log(`x_posts_fetched: ${usage.server_side_tool_usage_details.x_posts_fetched}`);
console.log(`x_users_fetched: ${usage.server_side_tool_usage_details.x_users_fetched}`);
import os
from openai import OpenAI
client = OpenAI(api_key=os.getenv("XAI_API_KEY"), base_url="https://api.x.ai/v1")
response = client.responses.create(
model="grok-4.7",
input="What did SpaceX post about Starship in September 2026?",
tools=[{"type": "x_search", "allowed_x_handles": ["SpaceX"], "from_date": "2026-09-01", "to_date": "2026-10-01"}],
extra_body={"max_turns": 2},
)
usage = response.usage.model_dump()
details = usage["server_side_tool_usage_details"]
print(f"x_posts_fetched: {details['x_posts_fetched']}")
print(f"x_users_fetched: {details['x_users_fetched']}")
curl -s https://api.x.ai/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $XAI_API_KEY" \
-d '{
"model": "grok-4.7",
"input": "What did SpaceX post about Starship in September 2026?",
"tools": [{"type": "x_search", "allowed_x_handles": ["SpaceX"], "from_date": "2026-09-01", "to_date": "2026-10-01"}],
"max_turns": 2
}' | jq -r '.usage | "x_posts_fetched: \(.server_side_tool_usage_details.x_posts_fetched)", "x_users_fetched: \(.server_side_tool_usage_details.x_users_fetched)"'
x_posts_fetched: 19
x_users_fetched: 0
Usage Counts
Each Responses API response that ran X Search reports how many items it fetched, so you can reconcile a request against the per-item pricing in effect as of September 21, 2026. The counts live under usage.server_side_tool_usage_details, next to the per-call x_search_calls:
| Field | Counts |
|---|---|
x_posts_fetched |
Posts returned by x_keyword_search, x_semantic_search, and x_thread_fetch, including parent and quoted posts and every post of a fetched thread |
x_users_fetched |
User profiles returned by x_user_search |
Both counts accumulate over every X Search call in the request and are not de-duplicated; a post returned by two searches counts twice. When streaming, the usage on the terminal event (response.completed, or response.incomplete when the response was truncated) is the total for the request.
The server_side_tool_usage_details block of a /v1/responses usage object after two X searches looks like this:
"server_side_tool_usage_details": {
"web_search_calls": 0,
"x_search_calls": 2,
"x_posts_fetched": 44,
"x_users_fetched": 3,
"code_interpreter_calls": 0,
"file_search_calls": 0,
"mcp_calls": 0,
"document_search_calls": 0,
"image_generation_calls": 0
}
Citations
For details on how to retrieve and use citations from search results, see the Citations page.